diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 2698b86a1..3596181b1 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,7 +12,7 @@ on: jobs: - build_and_test: + build: name: launcher runs-on: ${{ matrix.os }} strategy: @@ -79,9 +79,6 @@ jobs: run: make github-launcherapp if: ${{ contains(matrix.os, 'macos') }} - - name: Test - run: make test - - name: Cache build output uses: actions/cache@v4 with: @@ -89,21 +86,13 @@ jobs: key: ${{ runner.os }}-${{ github.run_id }} enableCrossOsArchive: true - # upload coverage here, because we don't cache it with the build - - name: Upload coverage - uses: actions/upload-artifact@v4 - with: - name: ${{ runner.os }}-coverage.out - path: ./coverage.out - if-no-files-found: error - # this job captures the version of launcher on one of the runners then that version is # compared to the version of all other runners during exec testing. This is to ensure # that the version of launcher is the same across all runners. version_baseline: name: Version Baseline runs-on: ubuntu-20.04 - needs: build_and_test + needs: build outputs: version: ${{ steps.version.outputs.version }} steps: @@ -120,6 +109,68 @@ jobs: shell: bash run: ./launcher --version 2>/dev/null | awk '/version /{print "version="$4}' >> "$GITHUB_OUTPUT" + launcher_test: + name: test + needs: build # a desktop runner test requires a build to exist + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-20.04 + - macos-13 + - windows-latest + steps: + - name: Check out code + id: checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # need a full checkout for `git describe` + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: './go.mod' + check-latest: true + cache: false + + - name: cache restore - build + uses: actions/cache/restore@v4 + with: + path: ./build + key: ${{ runner.os }}-${{ github.run_id }} + enableCrossOsArchive: true + + - id: go-cache-paths + shell: bash + run: | + echo "go-build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT" + echo "go-mod=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT" + + - name: cache restore - GOCACHE + uses: actions/cache/restore@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-build }} + key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }} + enableCrossOsArchive: true + + - name: cache restore - GOMODCACHE + uses: actions/cache/restore@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-mod }} + key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} + enableCrossOsArchive: true + + - name: Test + run: make test + + - name: Upload coverage + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-coverage.out + path: ./coverage.out + if-no-files-found: error + exec_testing: name: Exec Test runs-on: ${{ matrix.os }} @@ -270,6 +321,7 @@ jobs: steps: - run: true needs: - - build_and_test + - build + - launcher_test - package_builder_test - exec_testing diff --git a/Makefile b/Makefile index 564f3051a..6bccf9b6d 100644 --- a/Makefile +++ b/Makefile @@ -235,7 +235,7 @@ proto: @echo "Generated code from proto definitions." test: generate - go test -cover -coverprofile=coverage.out -race $(shell go list ./... | grep -v /vendor/) + go test -cover -coverprofile=coverage.out -race ./... ## ## Lint diff --git a/ee/desktop/runner/runner_test.go b/ee/desktop/runner/runner_test.go index 30b88a9f4..6b72d311d 100644 --- a/ee/desktop/runner/runner_test.go +++ b/ee/desktop/runner/runner_test.go @@ -42,21 +42,25 @@ func TestDesktopUserProcessRunner_Execute(t *testing.T) { } // due to flakey tests we are tracking the time it takes to build and attempting emit a meaningful error if we time out - timeout := time.Second * 60 + timeout := time.Minute * 2 ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() - cmd := exec.CommandContext(ctx, "go", "build", "-o", executablePath, "../../../cmd/launcher") //nolint:forbidigo // Fine to use exec.CommandContext in test - buildStartTime := time.Now() - out, err := cmd.CombinedOutput() - if err != nil { - err = fmt.Errorf("building launcher binary for desktop testing: %w", err) + // We may already have a built binary available -- check for that first + if err := symlinkPreexistingBinary(ctx, executablePath); err != nil { + // No binary available -- build one instead + cmd := exec.CommandContext(ctx, "go", "build", "-o", executablePath, "../../../cmd/launcher") //nolint:forbidigo // Fine to use exec.CommandContext in test + buildStartTime := time.Now() + out, err := cmd.CombinedOutput() + if err != nil { + err = fmt.Errorf("building launcher binary for desktop testing: %w", err) - if time.Since(buildStartTime) >= timeout { - err = fmt.Errorf("timeout (%v) met: %w", timeout, err) + if time.Since(buildStartTime) >= timeout { + err = fmt.Errorf("timeout (%v) met: %w", timeout, err) + } } + require.NoError(t, err, string(out)) } - require.NoError(t, err, string(out)) tests := []struct { name string @@ -238,6 +242,61 @@ func TestDesktopUserProcessRunner_Execute(t *testing.T) { } } +func symlinkPreexistingBinary(ctx context.Context, executablePath string) error { + builtBinaryPath := filepath.Join("..", "..", "..", "build", "launcher") + if runtime.GOOS == "windows" { + builtBinaryPath += "launcher.exe" + } + absPath, err := filepath.Abs(builtBinaryPath) + if err != nil { + return fmt.Errorf("getting absolute path for %s: %w", builtBinaryPath, err) + } + builtBinaryPath = filepath.Clean(absPath) + + // See if file exists + if _, err := os.Stat(builtBinaryPath); os.IsNotExist(err) { + return fmt.Errorf("no preexisting binary at %s", builtBinaryPath) + } + + // Get our current version + gitCmd := exec.CommandContext(ctx, "git", "describe", "--tags", "--always", "--dirty") //nolint:forbidigo // Fine to use exec.CommandContext in test + versionOut, err := gitCmd.CombinedOutput() + if err != nil { + return fmt.Errorf("getting current version: %w", err) + } + currentVersion := strings.TrimPrefix(strings.TrimSpace(string(versionOut)), "v") + + // Binary exists -- see if the version is a match + cmd := exec.CommandContext(ctx, builtBinaryPath, "-version") //nolint:forbidigo // Fine to use exec.CommandContext in test + cmd.Env = append(cmd.Environ(), "LAUNCHER_SKIP_UPDATES=TRUE") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("checking version: %w", err) + } + + lines := strings.Split(string(out), "\n") + binaryVersion := "" + for _, line := range lines { + if !strings.HasPrefix(line, "launcher - version") { + continue + } + + // We found the version + binaryVersion = strings.TrimSpace(strings.TrimPrefix(line, "launcher - version")) + break + } + + if binaryVersion != currentVersion { + return fmt.Errorf("built version %s does not match current version %s", binaryVersion, currentVersion) + } + + if err := os.MkdirAll(filepath.Dir(executablePath), 0755); err != nil { + return fmt.Errorf("making test dir: %w", err) + } + + return os.Symlink(builtBinaryPath, executablePath) +} + func launcherRootDir(t *testing.T) string { safeTestName := fmt.Sprintf("%s_%s", "launcher_desktop_test", ulid.New())