WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ bin/
summary.md
/.speakeasy/
temp/
integration/temp
integrationTests/
.DS_Store
.vscode/launch.json
__debug_bin*
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ require (
github.com/speakeasy-api/huh v1.1.2
github.com/speakeasy-api/jq v0.1.1-0.20251107233444-84d7e49e84a4
github.com/speakeasy-api/openapi v1.12.1
github.com/speakeasy-api/openapi-generation/v2 v2.778.0
github.com/speakeasy-api/openapi-generation/v2 v2.778.5
github.com/speakeasy-api/sdk-gen-config v1.43.1
github.com/speakeasy-api/speakeasy-client-sdk-go/v3 v3.26.7
github.com/speakeasy-api/speakeasy-core v0.21.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -535,8 +535,8 @@ github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed h1:PL/kpBY5vkBm
github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed/go.mod h1:Gc8oQkjr2InxwumK0zOBtKN9gIlv9L2VmSVIUk2YxcU=
github.com/speakeasy-api/openapi v1.12.1 h1:q8KqVo6P9SkDD4hulXBT6KoPU3BY4eeBiSUHbVSHAes=
github.com/speakeasy-api/openapi v1.12.1/go.mod h1:ITV3em4IFe1Hd4gX5Peq9TE7+Rfd/WIHZE/aqxNgihg=
github.com/speakeasy-api/openapi-generation/v2 v2.778.0 h1:Mpu4F3zs3DsmC7BMYzVryYeLzdc/p+0N1vPaVbT2zO0=
github.com/speakeasy-api/openapi-generation/v2 v2.778.0/go.mod h1:ol7GV+VKS4rlkH1pkIdw55n0gXa3OOL+V32h65JRruA=
github.com/speakeasy-api/openapi-generation/v2 v2.778.5 h1:Dl6A8vNWryhJkOmtSg7uYFAN0pgJert11f/ClIH9PaY=
github.com/speakeasy-api/openapi-generation/v2 v2.778.5/go.mod h1:ol7GV+VKS4rlkH1pkIdw55n0gXa3OOL+V32h65JRruA=
github.com/speakeasy-api/sdk-gen-config v1.43.1 h1:rhhv6mAVV2yl1I6TqHkpunnOnSXyH5milgzEA9vJPEo=
github.com/speakeasy-api/sdk-gen-config v1.43.1/go.mod h1:kD0NPNX5yaG4j+dcCpLL0hHKQbFk6X93obp+v1XlK5E=
github.com/speakeasy-api/speakeasy-client-sdk-go/v3 v3.26.7 h1:SoWZkRlpFlv8qibCfXWrBZay1JeLS9uqJ+1cu+DFgXo=
Expand Down
86 changes: 46 additions & 40 deletions integration/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,74 +6,80 @@ import (
"os/exec"
"path/filepath"
"runtime"
"sync"
"testing"

"github.com/stretchr/testify/assert"
)

// prebuiltBinary holds the path to the pre-built speakeasy binary.
// This avoids recompiling on every `go run main.go` call, saving ~20s per invocation.
var prebuiltBinary string
var (
prebuiltBinary string
buildOnce sync.Once
buildErr error
)

// ensureBinary builds the speakeasy binary once on first call.
// Subsequent calls return immediately. This is called lazily by execute()
// so that executeI() invocations don't pay the build cost.
func ensureBinary() (string, error) {
buildOnce.Do(func() {
_, filename, _, _ := runtime.Caller(0)
baseFolder := filepath.Join(filepath.Dir(filename), "..")
// Use PID to avoid collision between parallel test runs on the same machine
binaryName := fmt.Sprintf("speakeasy-test-binary-%d", os.Getpid())
if runtime.GOOS == "windows" {
binaryName += ".exe"
}
binaryPath := filepath.Join(os.TempDir(), binaryName)

fmt.Println("Pre-building speakeasy binary for integration tests...")
buildCmd := exec.Command("go", "build", "-o", binaryPath, filepath.Join(baseFolder, "main.go"))
buildCmd.Dir = baseFolder
buildCmd.Stdout = os.Stdout
buildCmd.Stderr = os.Stderr
if err := buildCmd.Run(); err != nil {
buildErr = fmt.Errorf("failed to pre-build speakeasy binary: %w", err)
return
}
prebuiltBinary = binaryPath
fmt.Println("Pre-built speakeasy binary:", prebuiltBinary)
})
return prebuiltBinary, buildErr
}

// Entrypoint for CLI integration tests
func TestMain(m *testing.M) {
// Create a temporary directory
if _, err := os.Stat(tempDir); err == nil {
if err := os.RemoveAll(tempDir); err != nil {
panic(err)
}
}
testDir := integrationTestsDir()

if err := os.Mkdir(tempDir, 0o755); err != nil {
// Create the integrationTests directory (MkdirAll is safe for parallel test processes)
if err := os.MkdirAll(testDir, 0o755); err != nil {
panic(err)
}

// Pre-build the speakeasy binary once to avoid ~20s compilation overhead per test
_, filename, _, _ := runtime.Caller(0)
baseFolder := filepath.Join(filepath.Dir(filename), "..")
binaryName := "speakeasy-test-binary"
if runtime.GOOS == "windows" {
binaryName += ".exe"
}
binaryPath := filepath.Join(os.TempDir(), binaryName)

fmt.Println("Pre-building speakeasy binary for integration tests...")
buildCmd := exec.Command("go", "build", "-o", binaryPath, filepath.Join(baseFolder, "main.go"))
buildCmd.Dir = baseFolder
buildCmd.Stdout = os.Stdout
buildCmd.Stderr = os.Stderr
if err := buildCmd.Run(); err != nil {
panic(fmt.Sprintf("failed to pre-build speakeasy binary: %v", err))
}
prebuiltBinary = binaryPath
fmt.Println("Pre-built speakeasy binary:", prebuiltBinary)
code := m.Run()

// Defer the removal of the temp directory and binary
defer func() {
if err := os.RemoveAll(tempDir); err != nil {
panic(err)
}
// Cleanup must happen before os.Exit (defer is not executed with os.Exit)
if prebuiltBinary != "" {
os.Remove(prebuiltBinary)
}()
}

code := m.Run()
os.Exit(code)
}

func setupTestDir(t *testing.T) string {
t.Helper()
_, filename, _, _ := runtime.Caller(0)
workingDir := filepath.Dir(filename)
temp, err := createTempDir(workingDir)
temp, err := createTempDir("")
assert.NoError(t, err)
registerCleanup(t, workingDir, temp)
registerCleanup(t, temp)

return temp
}

func registerCleanup(t *testing.T, workingDir string, temp string) {
func registerCleanup(t *testing.T, temp string) {
t.Helper()
t.Cleanup(func() {
os.RemoveAll(filepath.Join(workingDir, temp))
os.RemoveAll(temp)
})
}
17 changes: 4 additions & 13 deletions integration/patches_git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -695,14 +694,9 @@ func TestGitArchitecture_DeltaCompressionEfficiency(t *testing.T) {
func TestGitArchitecture_ImplicitFetchFromRemote(t *testing.T) {
t.Parallel()

// Create temp directories inside the module tree (required for `go run` to work)
// Using the integration folder as base ensures go.mod is findable
_, filename, _, _ := runtime.Caller(0)
integrationDir := filepath.Dir(filename)

remoteDir := filepath.Join(integrationDir, "temp", "remote-"+randStringBytes(7)+".git")
envADir := filepath.Join(integrationDir, "temp", "envA-"+randStringBytes(7))
envBDir := filepath.Join(integrationDir, "temp", "envB-"+randStringBytes(7))
remoteDir := filepath.Join(integrationTestsDir(), "remote-"+randStringBytes(7)+".git")
envADir := filepath.Join(integrationTestsDir(), "envA-"+randStringBytes(7))
envBDir := filepath.Join(integrationTestsDir(), "envB-"+randStringBytes(7))

// Create directories
require.NoError(t, os.MkdirAll(remoteDir, 0755))
Expand Down Expand Up @@ -1216,10 +1210,7 @@ func TestGitArchitecture_MultipleTypeScriptTargetsSameRepo(t *testing.T) {
func setupDualTypeScriptTargetsTestDir(t *testing.T) string {
t.Helper()

// Create temp directory using runtime.Caller pattern (required for go run to work)
_, filename, _, _ := runtime.Caller(0)
integrationDir := filepath.Dir(filename)
temp := filepath.Join(integrationDir, "temp", "dual-ts-"+randStringBytes(7))
temp := filepath.Join(integrationTestsDir(), "dual-ts-"+randStringBytes(7))

require.NoError(t, os.MkdirAll(temp, 0755))
t.Cleanup(func() {
Expand Down
12 changes: 9 additions & 3 deletions integration/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ import (
)

const (
tempDir = "temp"
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
version = "0.0.1"
artifactArch = "linux_amd64"
)

func createTempDir(wd string) (string, error) {
target := filepath.Join(wd, tempDir, randStringBytes(7))
// integrationTestsDir returns the path to the integrationTests directory at repo root.
// This is outside the integration/ package directory to avoid interference with `go test ./integration/...`.
func integrationTestsDir() string {
_, filename, _, _ := runtime.Caller(0)
return filepath.Join(filepath.Dir(filename), "..", "integrationTests")
}

func createTempDir(_ string) (string, error) {
target := filepath.Join(integrationTestsDir(), randStringBytes(7))
if err := os.Mkdir(target, 0o755); err != nil {
return "", err
}
Expand Down
16 changes: 5 additions & 11 deletions integration/workflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -282,16 +281,11 @@ func (r *subprocessRunner) Run() error {
func execute(t *testing.T, wd string, args ...string) Runnable {
t.Helper()

// Use pre-built binary if available (set by TestMain), otherwise fall back to go run
var execCmd *exec.Cmd
if prebuiltBinary != "" {
execCmd = exec.Command(prebuiltBinary, args...)
} else {
_, filename, _, _ := runtime.Caller(0)
baseFolder := filepath.Join(filepath.Dir(filename), "..")
mainGo := filepath.Join(baseFolder, "main.go")
execCmd = exec.Command("go", append([]string{"run", mainGo}, args...)...)
}
// Build the binary lazily on first execute() call
binaryPath, err := ensureBinary()
require.NoError(t, err, "failed to build speakeasy binary")

execCmd := exec.Command(binaryPath, args...)
execCmd.Env = os.Environ()
execCmd.Dir = wd

Expand Down
2 changes: 1 addition & 1 deletion internal/patches/pregeneration.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func PrepareForGeneration(outDir string, autoYes bool, promptFunc PromptFunc, wa
warnFunc("Failed to save lockfile with file change markers: %v", err)
}
}
} else if !persistentEdits.IsNever() && !env.IsCI() && os.Getenv("PROMPT_CUSTOM_CODE") == "true" {
} else if !persistentEdits.IsNever() && !env.IsCI() {
// Not enabled and not "never" - check for dirty files and prompt
isDirty, modifiedPaths, err := DetectFileChanges(outDir, cfg.LockFile)
if err != nil {
Expand Down
Loading