diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f0e2e7..a8f2ed3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,8 @@ permissions: attestations: read concurrency: - group: ci-${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + group: ci-${{ github.workflow }}-${{ inputs.source_ref || github.event.pull_request.number || github.ref }}-${{ inputs.source_ref && github.run_id || github.event_name }} + cancel-in-progress: ${{ !inputs.source_ref }} jobs: test: @@ -49,8 +49,8 @@ jobs: distribution: goreleaser version: v2.17.1 args: check - - name: Test - run: go test -race -coverprofile=coverage.out ./... + - name: Unit and core contracts + run: go test -p=1 -parallel=1 ./... - name: Vet run: go vet ./... - name: Scan reachable vulnerabilities @@ -59,9 +59,11 @@ jobs: run: go build -trimpath ./cmd/pushman portability: + needs: test name: Build (${{ matrix.os }}) strategy: fail-fast: false + max-parallel: 1 matrix: os: [macos-latest, windows-latest, windows-11-arm] runs-on: ${{ matrix.os }} @@ -76,7 +78,7 @@ jobs: cache: true - name: Test if: runner.os != 'Windows' - run: go test ./... + run: go test -p=1 -parallel=1 ./... - name: Record native Windows environment if: runner.os == 'Windows' shell: pwsh @@ -103,7 +105,7 @@ jobs: if: runner.os == 'Windows' env: PUSHMAN_TEST_NATIVE_KEYRING: '1' - run: go test -v ./... + run: go test -p=1 -parallel=1 -v ./... - name: Vet run: go vet ./... - name: Build @@ -135,7 +137,7 @@ jobs: if %ERRORLEVEL% NEQ 1 exit /b 1 exit /b 0 - name: Verify and execute the published Windows ZIP - if: runner.os == 'Windows' + if: runner.os == 'Windows' && inputs.source_ref != '' shell: pwsh env: GH_TOKEN: ${{ github.token }} @@ -143,7 +145,7 @@ jobs: $arch = if ('${{ matrix.os }}' -eq 'windows-11-arm') { 'arm64' } else { 'x86_64' } .\scripts\test-windows-release.ps1 -Tag v0.4.2 -Architecture $arch -CandidateExecutable .\.bin\pushman.exe - name: Qualify Windows installer lifecycle - if: runner.os == 'Windows' + if: runner.os == 'Windows' && inputs.source_ref != '' shell: pwsh run: | $arch = if ('${{ matrix.os }}' -eq 'windows-11-arm') { 'arm64' } else { 'x86_64' } @@ -151,7 +153,7 @@ jobs: $candidate = .\scripts\build-windows-installer.ps1 -Version 0.0.1-ci -Architecture $arch -Executable .bin\pushman.exe -Compiler $compiler .\scripts\test-windows-installer.ps1 -Installer $candidate.Installer -Executable .bin\pushman.exe -Version 0.0.1-ci -Architecture $arch -Compiler $compiler - name: Save qualified Windows candidate - if: runner.os == 'Windows' + if: runner.os == 'Windows' && inputs.source_ref != '' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: windows-candidate-${{ matrix.os }} @@ -160,6 +162,7 @@ jobs: retention-days: 7 winget-manifests: + if: inputs.source_ref != '' name: Validate WinGet manifests needs: portability runs-on: windows-11-arm diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7263b6d..6b9d0f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,7 +20,7 @@ git clone https://github.com/pushmanhq/pushman-cli.git cd pushman-cli go mod download go generate ./internal/api -go test -race ./... +go test -p=1 -parallel=1 ./... go vet ./... make pdev ARGS=help ``` @@ -73,7 +73,7 @@ try { The suite writes only a unique `com.pushman.test.*` entry with a synthetic value, registers cleanup before writing, and checks restart persistence/namespace isolation/deletion. A skipped suite is `NOT RUN`; an unavailable store in an opted-in run fails verification. -CI keeps Linux race/generated-client/script/vulnerability checks, macOS portability, and native Windows x64/ARM64 jobs. The Windows jobs record their image and native architecture and run credential/process tests, vet, an explicit `.exe` build, three-shell smoke, and checksum/provenance/version/help checks on the pinned published ZIP. Use the [verification record](docs/WINDOWS_VERIFICATION.md) for release and desktop-host evidence. A PowerShell script policy may disallow a local `.ps1` test wrapper; native Go tests and typed `.exe` commands do not require changing that policy. +CI keeps Linux unit/core/generated-client/script/vulnerability checks, then a sequential macOS/native Windows x64/ARM64 matrix. Go package/test schedulers and matrix concurrency use one. Windows records its image/native architecture and runs credential/process tests, vet, an explicit `.exe` build and three-shell smoke. These native keyring, console, shell and MCP contracts protect OS behavior that mock/Linux checks cannot establish; they use bounded synthetic, loopback fixtures on standard hosted workers on every source PR. Published-ZIP/provenance, installer lifecycle and WinGet qualification remain in the existing exact-source release workflow-call profile. Use the [verification record](docs/WINDOWS_VERIFICATION.md) for release and desktop-host evidence. A PowerShell script policy may disallow a local `.ps1` test wrapper; native Go tests and typed `.exe` commands do not require changing that policy. ### Windows packaging @@ -87,7 +87,7 @@ $candidate = .\scripts\build-windows-installer.ps1 -Version 0.0.1-ci -Architectu .\scripts\test-windows-installer.ps1 -Installer $candidate.Installer -Executable .bin\pushman.exe -Version 0.0.1-ci -Architecture x86_64 -Compiler $compiler ``` -Use `arm64` on native ARM64 Windows. Run lifecycle qualification on a disposable Windows account: it installs into an owned temporary directory, refuses an existing registered installation, and restores the exact user PATH value/type/existence in cleanup. It exercises real installation, repeat installation, update/rollback fixtures, PATH opt-out/preexisting entries, and removal while preserving unrelated files. It never logs in or sends a notification. CI runs both architectures and validates WinGet manifests. See [Windows distribution](docs/WINDOWS_DISTRIBUTION.md) for release gates and catalog submission. +Use `arm64` on native ARM64 Windows. Run lifecycle qualification on a disposable Windows account: it installs into an owned temporary directory, refuses an existing registered installation, and restores the exact user PATH value/type/existence in cleanup. It exercises real installation, repeat installation, update/rollback fixtures, PATH opt-out/preexisting entries, and removal while preserving unrelated files. It never logs in or sends a notification. The existing exact-source release CI profile runs both architectures and validates WinGet manifests. For related packaging changes, perform the same local qualification before submitting the PR and record native architecture, exact SHA/bytes, commands and results; unexecuted native/installer checks are explicit limits. See [Windows distribution](docs/WINDOWS_DISTRIBUTION.md) for release gates and catalog submission. ### Pull request checks @@ -97,10 +97,24 @@ Before submitting a pull request: test -z "$(gofmt -l .)" go generate ./internal/api git diff --exit-code -- internal/api/api.gen.go -go test -race ./... +go test -p=1 -parallel=1 ./... go vet ./... ``` Keep changes focused. Add or update tests for user-visible behavior, preserve the stdout/stderr contract, and do not hand-edit the generated API client. Commit messages use `type: imperative summary`, for example `fix: reject an empty notification body`. By contributing, you agree that your contributions are licensed under the repository's [MIT License](LICENSE). All participants must follow the [Code of Conduct](CODE_OF_CONDUCT.md). + +### Additional qualification at PR submission + +Basic CI uses normal unit/core checks. Any additional component/integration/race/ +E2E needs a protected critical failure, why smaller checks cannot detect it, why +every relevant PR needs automation, and a stable isolated budget. No broad race +profile meets a separate per-PR need here; use `make test-race` locally for related +shared-memory changes and record exact SHA, environment, commands, scope, results +and skipped/untested boundaries in the PR body/comment. Runtime concurrency +assertions remain active. Packaging changes require the documented local native +ZIP/installer/WinGet contracts; release qualification still uses the original +automatic workflow call. Do not introduce manual Actions or nightly workflows +as a substitute. Diagnose setup, slow cases and resource pressure before adding +workers or sharding. Source CI never sends a notification or qualifies login. diff --git a/Makefile b/Makefile index 1d0baba..c51d793 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ PDEV_BINARY ?= .bin/pushman-dev PDEV_INSTALL_PATH ?= $(HOME)/.local/bin/pdev PDEV_LDFLAGS = -X main.version=dev-local -X main.commit=working-tree -X main.date=local -X main.defaultBaseURL=$(PDEV_API_URL) -X main.credentialNamespace=dev -X main.automationTokenEnvironment=PUSHMAN_DEV_TOKEN -.PHONY: build-dev dev pdev install-dev test +.PHONY: build-dev dev pdev install-dev test test-race build-dev: mkdir -p $(dir $(PDEV_BINARY)) @@ -17,5 +17,9 @@ install-dev: go build -trimpath -ldflags "$(PDEV_LDFLAGS)" -o $(PDEV_INSTALL_PATH) ./cmd/pushman test: - go test -race ./... + go test -p=1 -parallel=1 ./... + go vet ./... + +test-race: + go test -race -p=1 -parallel=1 ./... go vet ./... diff --git a/docs/WINDOWS_VERIFICATION.md b/docs/WINDOWS_VERIFICATION.md index 469c323..52dd649 100644 --- a/docs/WINDOWS_VERIFICATION.md +++ b/docs/WINDOWS_VERIFICATION.md @@ -12,9 +12,9 @@ The portability matrix keeps macOS and Windows x64 and adds native `windows-11-a - A hidden real console validates `CONIN$` detection. A directed `CTRL_BREAK_EVENT` to an owned test process exercises Go's `os.Interrupt` and login exit 130. This does not claim a human keyboard Ctrl-C test. [Windows console event semantics](https://learn.microsoft.com/en-us/windows/console/generateconsolectrlevent), [Go Windows signal behavior](https://pkg.go.dev/os/signal#hdr-Windows). - PowerShell 7/5.1 and cmd run the production-style binary for version/help and expected usage/self-update failure exit codes. - `scripts/test-windows-release.ps1 -Tag v0.4.2 -Architecture x86_64` (or `arm64`) downloads a matching published ZIP, requires exactly one checksum entry, verifies provenance and the PE machine architecture, and executes version/help. With `-CandidateExecutable .\.bin\pushman.exe`, it also verifies temporary user-directory installation/discovery, replacement with the source build, byte-exact rollback, and binary removal, restoring session PATH afterward. It does not change persistent PATH or authorization. The pinned tag is independent of the source being tested. Advance it deliberately after validating a new release. -- `scripts/test-windows-installer.ps1` qualifies an actual candidate setup on a disposable native account: install/reinstall/update/rollback fixtures, installed-app registration, executable bytes/version/help, PATH discovery/opt-out/preexisting entries, unrelated-file preservation, uninstall, and exact persistent user PATH value/type/existence restoration. The test refuses an existing registered installation. Both native CI architectures run it; WinGet manifests use the two qualified files and pass official schema/native validation. +- `scripts/test-windows-installer.ps1` qualifies an actual candidate setup on a disposable native account: install/reinstall/update/rollback fixtures, installed-app registration, executable bytes/version/help, PATH discovery/opt-out/preexisting entries, unrelated-file preservation, uninstall, and exact persistent user PATH value/type/existence restoration. The test refuses an existing registered installation. The existing exact-source release CI profile runs both native architectures; WinGet manifests use the two qualified files and pass official schema/native validation. -The process-test binary uses a test credential namespace. API requests use a loopback endpoint and synthetic token. Tests never access production credentials or send real notifications. Windows race prerequisites are not silently assumed; Linux's existing race and other checks remain mandatory. +The process-test binary uses a test credential namespace. API requests use a loopback endpoint and synthetic token. Tests never access production credentials or send real notifications. Windows race prerequisites are not silently assumed. Normal source CI keeps native keyring/shell/console/MCP correctness with scheduler one; broad Linux race is local related-PR evidence. Published-ZIP/installer/WinGet qualification is local for related changes and mandatory in the existing exact-source release workflow-call profile. ## Evidence and remaining acceptance