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
21 changes: 12 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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 }}
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -135,23 +137,23 @@ 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 }}
run: |
$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' }
$compiler = .\scripts\ensure-inno-setup.ps1
$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 }}
Expand All @@ -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
Expand Down
22 changes: 18 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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.
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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 ./...
4 changes: 2 additions & 2 deletions docs/WINDOWS_VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading