diff --git a/.actrc b/.actrc new file mode 100644 index 000000000..5c08277ae --- /dev/null +++ b/.actrc @@ -0,0 +1 @@ +-P ubuntu-latest=catthehacker/ubuntu:act-latest diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 000000000..9befe1c4a --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,20 @@ +# Github Workflows + +## Testing CI Locally + +Test GitHub Actions workflows locally using [act](https://nektosact.com/): + +```bash +# Test all PR checks +act pull_request + +# Test specific job +act pull_request -j nix-flake-validate + +# Dry run to see what would execute +act pull_request --dryrun +``` + +The `.actrc` file configures act to use the appropriate Docker image. + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff1eae8f9..3bea232dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -156,6 +156,64 @@ jobs: exit 1 fi + nix-flake-validate: + name: Nix Flake Validation + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@v13 + + - name: Setup Nix cache + uses: DeterminateSystems/magic-nix-cache-action@v8 + + - name: Build with Nix + run: nix build + + - name: Verify build output + run: | + if [ ! -e "result" ]; then + echo "Error: Nix build output 'result' symlink not found" + exit 1 + fi + if [ ! -f "result/bin/openspec" ]; then + echo "Error: openspec binary not found in build output" + exit 1 + fi + echo "✅ Build output verified" + + - name: Test binary execution + run: | + VERSION=$(nix run . -- --version) + echo "OpenSpec version: $VERSION" + if [ -z "$VERSION" ]; then + echo "Error: Version command returned empty output" + exit 1 + fi + echo "✅ Binary execution successful" + + - name: Validate update script + run: | + echo "Testing update-flake.sh script..." + bash scripts/update-flake.sh + echo "✅ Update script executed successfully" + + - name: Check flake.nix modifications + run: | + if git diff --quiet flake.nix; then + echo "⚠️ Warning: flake.nix was not modified by update script" + else + echo "✅ flake.nix was updated by script" + git diff flake.nix + fi + + - name: Restore flake.nix + if: always() + run: git checkout -- flake.nix || true + validate-changesets: name: Validate Changesets runs-on: ubuntu-latest @@ -191,7 +249,7 @@ jobs: required-checks-pr: name: All checks passed runs-on: ubuntu-latest - needs: [test_pr, lint] + needs: [test_pr, lint, nix-flake-validate] if: always() && github.event_name == 'pull_request' steps: - name: Verify all checks passed @@ -204,12 +262,16 @@ jobs: echo "Lint job failed" exit 1 fi + if [[ "${{ needs.nix-flake-validate.result }}" != "success" ]]; then + echo "Nix flake validation job failed" + exit 1 + fi echo "All required checks passed!" required-checks-main: name: All checks passed runs-on: ubuntu-latest - needs: [test_matrix, lint] + needs: [test_matrix, lint, nix-flake-validate] if: always() && github.event_name != 'pull_request' steps: - name: Verify all checks passed @@ -222,4 +284,8 @@ jobs: echo "Lint job failed" exit 1 fi + if [[ "${{ needs.nix-flake-validate.result }}" != "success" ]]; then + echo "Nix flake validation job failed" + exit 1 + fi echo "All required checks passed!" diff --git a/.gitignore b/.gitignore index 5ecf229d4..743fdf018 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,4 @@ CLAUDE.md # Pnpm .pnpm-store/ +result diff --git a/README.md b/README.md index b7deed521..3424bbff3 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,8 @@ These tools automatically read workflow instructions from `openspec/AGENTS.md`. #### Step 1: Install the CLI globally +**Option A: Using npm** + ```bash npm install -g @fission-ai/openspec@latest ``` @@ -149,6 +151,39 @@ Verify installation: openspec --version ``` +**Option B: Using Nix (NixOS and Nix package manager)** + +Run OpenSpec directly without installation: +```bash +nix run github:Fission-AI/OpenSpec -- init +``` + +Or install to your profile: +```bash +nix profile install github:Fission-AI/OpenSpec +``` + +Or add to your development environment in `flake.nix`: +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + openspec.url = "github:Fission-AI/OpenSpec"; + }; + + outputs = { nixpkgs, openspec, ... }: { + devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell { + buildInputs = [ openspec.packages.x86_64-linux.default ]; + }; + }; +} +``` + +Verify installation: +```bash +openspec --version +``` + #### Step 2: Initialize OpenSpec in your project Navigate to your project directory: diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..45e7b5aa9 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1767640445, + "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..cb1087883 --- /dev/null +++ b/flake.nix @@ -0,0 +1,87 @@ +{ + description = "OpenSpec - AI-native system for spec-driven development"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + + forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); + in + { + packages = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + default = pkgs.stdenv.mkDerivation (finalAttrs: { + pname = "openspec"; + version = "0.20.0"; + + src = ./.; + + pnpmDeps = pkgs.fetchPnpmDeps { + inherit (finalAttrs) pname version src; + pnpm = pkgs.pnpm_9; + fetcherVersion = 3; + hash = "sha256-m/7IdY1ou9ljjYAcx3W8AyEJvIZfCBWIWxproQ/INPA="; + }; + + nativeBuildInputs = with pkgs; [ + nodejs_20 + npmHooks.npmInstallHook + pnpmConfigHook + pnpm_9 + ]; + + buildPhase = '' + runHook preBuild + + pnpm run build + + runHook postBuild + ''; + + dontNpmPrune = true; + + meta = with pkgs.lib; { + description = "AI-native system for spec-driven development"; + homepage = "https://github.com/Fission-AI/OpenSpec"; + license = licenses.mit; + maintainers = [ ]; + mainProgram = "openspec"; + }; + }); + }); + + apps = forAllSystems (system: { + default = { + type = "app"; + program = "${self.packages.${system}.default}/bin/openspec"; + }; + }); + + devShells = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + default = pkgs.mkShell { + buildInputs = with pkgs; [ + nodejs_20 + pnpm_9 + ]; + + shellHook = '' + echo "OpenSpec development environment" + echo "Node version: $(node --version)" + echo "pnpm version: $(pnpm --version)" + echo "Run 'pnpm install' to install dependencies" + ''; + }; + }); + }; +} diff --git a/openspec/changes/archive/2026-01-07-add-nix-flake-support/.openspec.yaml b/openspec/changes/archive/2026-01-07-add-nix-flake-support/.openspec.yaml new file mode 100644 index 000000000..75b7b3e31 --- /dev/null +++ b/openspec/changes/archive/2026-01-07-add-nix-flake-support/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-01-07 diff --git a/openspec/changes/archive/2026-01-07-add-nix-flake-support/design.md b/openspec/changes/archive/2026-01-07-add-nix-flake-support/design.md new file mode 100644 index 000000000..ec4cba3b7 --- /dev/null +++ b/openspec/changes/archive/2026-01-07-add-nix-flake-support/design.md @@ -0,0 +1,94 @@ +## Context + +OpenSpec is a TypeScript CLI tool using pnpm for dependency management. The project requires Node.js ≥20.19.0. Nix uses its own build system that needs to understand how to fetch dependencies and build the project reproducibly. + +The Nix ecosystem has specific patterns for packaging Node.js/pnpm projects that differ from the traditional npm ecosystem. + +## Goals + +- Enable OpenSpec to be run directly via `nix run github:Fission-AI/OpenSpec` +- Support all major platforms (Linux x86/ARM, macOS x86/ARM) +- Use existing pnpm-lock.yaml for reproducible builds +- Provide development environment for Nix users + +## Non-Goals + +- Replace existing npm/pnpm publishing workflow +- Publish to nixpkgs (can be done later as separate effort) +- Support Windows (Nix doesn't run natively on Windows) + +## Decisions + +### Use stdenv.mkDerivation instead of buildNpmPackage + +**Decision**: Package OpenSpec using `stdenv.mkDerivation` with pnpm hooks. + +**Rationale**: The zigbee2mqtt package in nixpkgs demonstrates the current best practice for pnpm projects. Using `buildNpmPackage` with pnpm requires complex configuration, while `mkDerivation` with the right hooks is more straightforward and better supported. + +**Alternative considered**: Using `buildNpmPackage` with `npmConfigHook = pkgs.pnpmConfigHook` - this is the older pattern and causes issues with dependency fetching. + +### Use fetchPnpmDeps with explicit pnpm version + +**Decision**: Use `pkgs.fetchPnpmDeps` with `pnpm = pkgs.pnpm_9` and `fetcherVersion = 3`. + +**Rationale**: +- pnpm lockfile version 9.0 requires fetcherVersion 3 +- Explicit pnpm_9 ensures consistency between fetch and build +- This is the documented way to handle pnpm projects in nixpkgs + +### Multi-platform support without flake-utils + +**Decision**: Implement multi-platform support using plain Nix with `nixpkgs.lib.genAttrs`. + +**Rationale**: Per user request, avoid extra dependencies. The `genAttrs` pattern is simple and well-understood in the Nix community. + +### Node.js 20 instead of latest + +**Decision**: Pin to nodejs_20 to match package.json engines requirement. + +**Rationale**: Ensures consistency with development environment and npm package requirements. Avoids potential compatibility issues with newer Node versions. + +## Key Implementation Details + +### Dependency Hash Management + +The `pnpmDeps.hash` field must be updated whenever dependencies change. The workflow: +1. Set hash to fake value (all zeros) +2. Run `nix build` +3. Nix fails with actual hash +4. Update flake.nix with correct hash + +This is standard Nix workflow for fixed-output derivations. + +### Build Inputs + +Required nativeBuildInputs: +- `nodejs_20` - runtime +- `npmHooks.npmInstallHook` - handles installation phase +- `pnpmConfigHook` - configures pnpm environment +- `pnpm_9` - pnpm executable + +The `dontNpmPrune = true` is important to keep all dependencies after build. + +## Risks / Trade-offs + +**[Risk]** Hash needs updating when dependencies change → **Mitigation**: Document this clearly; error message from Nix provides correct hash + +**[Risk]** Nix builds might lag behind npm releases → **Mitigation**: This is fine; Nix users can still use npm if they need bleeding edge + +**[Trade-off]** Additional maintenance burden for hash updates → **Benefit**: Better experience for Nix ecosystem users + +## Migration Plan + +1. Add flake.nix to repository +2. Test builds on multiple platforms (can use GitHub Actions with Nix) +3. Update README with Nix installation instructions +4. Optionally add to CI pipeline to catch hash mismatches early + +No breaking changes - this is purely additive. + +## Open Questions + +- Should we add automatic hash updating to CI? (Could use nix-update-script) +- Should we submit to nixpkgs after validation? (Separate decision) +- Do we want to support older Node versions in flake? (Probably no - stick to package.json requirement) diff --git a/openspec/changes/archive/2026-01-07-add-nix-flake-support/proposal.md b/openspec/changes/archive/2026-01-07-add-nix-flake-support/proposal.md new file mode 100644 index 000000000..77cb3973c --- /dev/null +++ b/openspec/changes/archive/2026-01-07-add-nix-flake-support/proposal.md @@ -0,0 +1,25 @@ +## Why + +OpenSpec users on NixOS or using the Nix package manager cannot easily install or run OpenSpec without going through npm. Adding a Nix flake makes OpenSpec a first-class citizen in the Nix ecosystem, enabling users to run `nix run github:Fission-AI/OpenSpec -- init` or include OpenSpec in their development environments declaratively. + +## What Changes + +- Add `flake.nix` to repository root with multi-platform support (x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin) +- Package uses pnpm for dependency management (matching existing development workflow) +- Support both direct execution via `nix run` and installation via `nix profile install` +- Provide dev shell for contributors using Nix + +## Capabilities + +### New Capabilities +- `nix-flake-support`: Nix flake configuration for building and running OpenSpec + +### Modified Capabilities +- None + +## Impact + +- **New files**: `flake.nix` in repository root +- **Documentation**: Should add installation instructions for Nix users +- **CI/CD**: Could add flake checking to CI pipeline (optional) +- **Maintenance**: Requires updating pnpmDeps hash when dependencies change diff --git a/openspec/changes/archive/2026-01-07-add-nix-flake-support/specs/nix-flake-support/spec.md b/openspec/changes/archive/2026-01-07-add-nix-flake-support/specs/nix-flake-support/spec.md new file mode 100644 index 000000000..867249035 --- /dev/null +++ b/openspec/changes/archive/2026-01-07-add-nix-flake-support/specs/nix-flake-support/spec.md @@ -0,0 +1,79 @@ +## ADDED Requirements + +### Requirement: Multi-platform Nix flake +The system SHALL provide a Nix flake that builds OpenSpec for multiple platforms. + +#### Scenario: Build on Linux x86_64 +- **WHEN** user runs `nix build` on x86_64-linux system +- **THEN** system builds OpenSpec package successfully +- **AND** package includes the `openspec` binary + +#### Scenario: Build on macOS ARM +- **WHEN** user runs `nix build` on aarch64-darwin system +- **THEN** system builds OpenSpec package successfully +- **AND** package includes the `openspec` binary + +#### Scenario: Build on Linux ARM +- **WHEN** user runs `nix build` on aarch64-linux system +- **THEN** system builds OpenSpec package successfully + +#### Scenario: Build on macOS x86_64 +- **WHEN** user runs `nix build` on x86_64-darwin system +- **THEN** system builds OpenSpec package successfully + +### Requirement: Direct execution via nix run +The system SHALL allow users to run OpenSpec directly from GitHub without installing. + +#### Scenario: Run init command from GitHub +- **WHEN** user runs `nix run github:Fission-AI/OpenSpec -- init` +- **THEN** system downloads and builds OpenSpec +- **AND** executes `openspec init` command + +#### Scenario: Run any OpenSpec command +- **WHEN** user runs `nix run github:Fission-AI/OpenSpec -- ` +- **THEN** system executes `openspec ` + +### Requirement: pnpm dependency management +The system SHALL use pnpm for building OpenSpec in the Nix flake. + +#### Scenario: Fetch dependencies with pnpm +- **WHEN** Nix builds the package +- **THEN** system uses `fetchPnpmDeps` to download dependencies +- **AND** uses pnpm-lock.yaml for reproducible builds +- **AND** uses fetcherVersion 3 for lockfile version 9.0 + +#### Scenario: Build with pnpm +- **WHEN** Nix runs the build phase +- **THEN** system executes `pnpm run build` +- **AND** produces dist directory with compiled TypeScript + +### Requirement: Node.js version compatibility +The system SHALL use Node.js 20 as specified in package.json engines field. + +#### Scenario: Build with correct Node version +- **WHEN** Nix builds OpenSpec +- **THEN** system uses nodejs_20 from nixpkgs +- **AND** build succeeds without version compatibility errors + +### Requirement: Development shell +The system SHALL provide a Nix development shell for contributors. + +#### Scenario: Enter dev shell +- **WHEN** user runs `nix develop` in OpenSpec repository +- **THEN** system provides shell with nodejs_20 and pnpm_9 +- **AND** displays welcome message with versions +- **AND** provides instructions to run `pnpm install` + +### Requirement: Proper binary installation +The system SHALL install the openspec binary correctly. + +#### Scenario: Binary in PATH +- **WHEN** package is built or installed +- **THEN** `openspec` binary is available in `$out/bin/openspec` +- **AND** binary is executable +- **AND** binary can be invoked without full path when installed + +#### Scenario: Binary executes correctly +- **WHEN** user runs the installed `openspec` command +- **THEN** system executes the CLI entry point +- **AND** all subcommands work correctly diff --git a/openspec/changes/archive/2026-01-07-add-nix-flake-support/tasks.md b/openspec/changes/archive/2026-01-07-add-nix-flake-support/tasks.md new file mode 100644 index 000000000..190d9ec4b --- /dev/null +++ b/openspec/changes/archive/2026-01-07-add-nix-flake-support/tasks.md @@ -0,0 +1,65 @@ +## 1. Create Flake Structure + +- [x] 1.1 Create flake.nix in repository root +- [x] 1.2 Define inputs (nixpkgs only, no flake-utils) +- [x] 1.3 Set up supportedSystems list (4 platforms) +- [x] 1.4 Create forAllSystems helper function + +## 2. Configure Package Build + +- [x] 2.1 Set up stdenv.mkDerivation with finalAttrs pattern +- [x] 2.2 Configure pnpmDeps with fetchPnpmDeps +- [x] 2.3 Set pnpm = pnpm_9 and fetcherVersion = 3 +- [x] 2.4 Add placeholder hash (all zeros) +- [x] 2.5 Configure nativeBuildInputs (nodejs_20, hooks, pnpm_9) +- [x] 2.6 Set dontNpmPrune = true + +## 3. Define Build Phase + +- [x] 3.1 Add buildPhase with runHook preBuild +- [x] 3.2 Add pnpm run build command +- [x] 3.3 Add runHook postBuild + +## 4. Configure Installation + +- [x] 4.1 Let npmInstallHook handle installation automatically +- [x] 4.2 Verify binary ends up in $out/bin/openspec + +## 5. Add Metadata + +- [x] 5.1 Set meta.description +- [x] 5.2 Set meta.homepage +- [x] 5.3 Set meta.license (MIT) +- [x] 5.4 Set meta.mainProgram = "openspec" + +## 6. Configure App Entry Point + +- [x] 6.1 Add apps output with forAllSystems +- [x] 6.2 Set default app to openspec binary +- [x] 6.3 Test that nix run works + +## 7. Add Development Shell + +- [x] 7.1 Add devShells output with forAllSystems +- [x] 7.2 Include nodejs_20 and pnpm_9 in buildInputs +- [x] 7.3 Add shellHook with welcome message and instructions + +## 8. Get Correct Dependency Hash + +- [x] 8.1 Run nix build to trigger hash mismatch +- [x] 8.2 Copy correct hash from error message +- [x] 8.3 Update pnpmDeps.hash in flake.nix +- [x] 8.4 Verify build succeeds + +## 9. Testing + +- [x] 9.1 Test `nix build` on x86_64-linux +- [x] 9.2 Test `nix run . -- --version` works +- [x] 9.3 Test `nix develop` provides correct environment +- [ ] 9.4 Test on macOS if available +- [ ] 9.5 Test `nix run github:Fission-AI/OpenSpec -- init` after merge to main + +## 10. Documentation + +- [x] 10.1 Add Nix installation section to README +- [x] 10.2 Include example commands for common Nix workflows in README diff --git a/openspec/changes/archive/2026-01-09-add-flake-update-script/.openspec.yaml b/openspec/changes/archive/2026-01-09-add-flake-update-script/.openspec.yaml new file mode 100644 index 000000000..e5cb8126b --- /dev/null +++ b/openspec/changes/archive/2026-01-09-add-flake-update-script/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-01-09 diff --git a/openspec/changes/archive/2026-01-09-add-flake-update-script/design.md b/openspec/changes/archive/2026-01-09-add-flake-update-script/design.md new file mode 100644 index 000000000..0cfb0d556 --- /dev/null +++ b/openspec/changes/archive/2026-01-09-add-flake-update-script/design.md @@ -0,0 +1,117 @@ +## Context + +The Nix flake added in the previous change requires manual maintenance when: +1. Package version changes (must update flake.nix version field) +2. Dependencies change (must update pnpmDeps hash) + +Currently this requires maintainers to: +- Manually edit flake.nix version +- Set placeholder hash +- Run nix build to get error +- Copy hash from error message +- Update flake.nix again +- Verify build works + +This is tedious and error-prone, especially for maintainers unfamiliar with Nix. + +## Goals + +- Automate version and hash updates for flake.nix +- Make script idempotent and safe to run multiple times +- Provide clear feedback during execution +- Integrate easily into release workflow + +## Non-Goals + +- Automatically commit changes (maintainer decides when to commit) +- Support non-pnpm package managers +- Handle complex Nix configurations beyond OpenSpec's use case + +## Decisions + +### Use Bash instead of Node.js script + +**Decision**: Implement as bash script rather than Node.js. + +**Rationale**: +- Needs to call Nix commands which are bash-native +- Parsing Nix output is simpler in bash with grep/sed +- Maintainers updating flake.nix likely have Nix installed (bash environment) +- Node.js would add unnecessary complexity for shell operations + +**Alternative considered**: Node.js script with child_process - adds dependency on extra npm packages for shell operations, less natural for Nix tooling. + +### Extract hash from build error output + +**Decision**: Trigger intentional build failure with placeholder hash to get correct hash. + +**Rationale**: This is the standard Nix workflow for updating fixed-output derivations. No API exists to compute the hash without building. + +**Alternative considered**: Pre-compute hash from pnpm-lock.yaml - would require understanding Nix's hash algorithm and pnpm's lockfile structure, fragile and non-standard. + +### Use sed for in-place file editing + +**Decision**: Use `sed -i` for updating flake.nix in place. + +**Rationale**: Simple, available on all Unix-like systems, handles the specific replacement patterns needed. + +**Alternative considered**: +- Using Node.js to parse/modify: Overkill for simple string replacement +- Manual `sed` without `-i`: Requires temp files, more complex + +### Verify build after hash update + +**Decision**: Always run verification build after updating hash. + +**Rationale**: Catches errors immediately, gives maintainer confidence the update worked. + +**Trade-off**: Takes extra time (~30s) but prevents broken flake.nix commits. + +## Key Implementation Details + +### Path Resolution + +Script calculates paths relative to its own location: +```bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +``` + +This allows running from any working directory. + +### Error Handling + +Uses `set -euo pipefail` for strict error handling: +- `-e`: Exit on any command failure +- `-u`: Exit on undefined variable access +- `-o pipefail`: Catch failures in pipes + +### Hash Extraction Pattern + +Uses grep with Perl regex to extract hash: +```bash +grep -oP 'got:\s+\Ksha256-[A-Za-z0-9+/=]+' +``` + +This reliably extracts the hash regardless of surrounding text. + +## Risks / Trade-offs + +**[Risk]** Script assumes standard Nix error message format → **Mitigation**: If extraction fails, script exits with error and shows full output + +**[Risk]** Build might fail for reasons other than hash mismatch → **Mitigation**: Script checks for hash in output before proceeding + +**[Trade-off]** Requires Nix installed to run → **Benefit**: Only maintainers updating flake need to run this, and they have Nix + +## Migration Plan + +1. Add script to scripts directory +2. Document in scripts/README.md +3. Use in next version bump to verify workflow +4. Update CONTRIBUTING.md if needed to mention script + +No breaking changes - purely additive tooling. + +## Open Questions + +None - straightforward automation script. diff --git a/openspec/changes/archive/2026-01-09-add-flake-update-script/proposal.md b/openspec/changes/archive/2026-01-09-add-flake-update-script/proposal.md new file mode 100644 index 000000000..a81b10db1 --- /dev/null +++ b/openspec/changes/archive/2026-01-09-add-flake-update-script/proposal.md @@ -0,0 +1,23 @@ +## Why + +Maintaining the Nix flake requires manual updates to version and dependency hash when releasing new versions or updating dependencies. This is error-prone and requires maintainers to understand Nix internals. Automating this process ensures consistency and reduces friction for releases. + +## What Changes + +- Add `scripts/update-flake.sh` to automatically update flake.nix version and dependency hash +- Add `scripts/README.md` documenting all maintenance scripts +- Script extracts version from package.json and determines correct pnpm dependency hash automatically + +## Capabilities + +### New Capabilities +- `flake-update-script`: Automation script for maintaining flake.nix + +### Modified Capabilities +- None + +## Impact + +- **New files**: `scripts/update-flake.sh`, `scripts/README.md` +- **Maintainer workflow**: Version bumps now include running `./scripts/update-flake.sh` +- **Dependencies**: Script requires Node.js (already a dependency) and Nix (for maintainers using Nix) diff --git a/openspec/changes/archive/2026-01-09-add-flake-update-script/specs/flake-update-script/spec.md b/openspec/changes/archive/2026-01-09-add-flake-update-script/specs/flake-update-script/spec.md new file mode 100644 index 000000000..476bb4ab7 --- /dev/null +++ b/openspec/changes/archive/2026-01-09-add-flake-update-script/specs/flake-update-script/spec.md @@ -0,0 +1,86 @@ +## ADDED Requirements + +### Requirement: Automatic Version Update +The script SHALL automatically update the version in flake.nix to match package.json. + +#### Scenario: Version extraction from package.json +- **WHEN** script runs +- **THEN** version is read from package.json using Node.js +- **AND** version field in flake.nix is updated to match + +#### Scenario: Version already up-to-date +- **WHEN** script runs and flake.nix version already matches package.json +- **THEN** script reports version is up-to-date +- **AND** continues to hash update + +### Requirement: Automatic Hash Determination +The script SHALL automatically determine and update the correct pnpm dependency hash. + +#### Scenario: Trigger build to get hash +- **WHEN** script needs to determine correct hash +- **THEN** script sets placeholder hash in flake.nix +- **AND** runs nix build which fails with correct hash +- **AND** extracts correct hash from build error output + +#### Scenario: Hash extraction from build output +- **WHEN** nix build fails with hash mismatch +- **THEN** script parses "got: sha256-..." from error output +- **AND** updates flake.nix with correct hash + +#### Scenario: Hash update failure +- **WHEN** script cannot extract hash from build output +- **THEN** script exits with error +- **AND** displays build output for debugging + +### Requirement: Build Verification +The script SHALL verify that flake.nix builds successfully after updates. + +#### Scenario: Successful verification +- **WHEN** hash has been updated +- **THEN** script runs nix build to verify +- **AND** reports success if build completes + +#### Scenario: Dirty git tree warning +- **WHEN** build succeeds but git tree is dirty +- **THEN** script reports warning about dirty tree +- **AND** still indicates build success + +### Requirement: User Feedback +The script SHALL provide clear progress information and next steps. + +#### Scenario: Progress reporting +- **WHEN** script runs +- **THEN** each step is reported with descriptive message +- **AND** detected version and hash are displayed + +#### Scenario: Success summary +- **WHEN** script completes successfully +- **THEN** summary shows updated version and hash +- **AND** next steps are displayed (test, commit, etc.) + +### Requirement: Script Safety +The script SHALL fail fast on errors and use safe defaults. + +#### Scenario: Bash error handling +- **WHEN** script encounters an error +- **THEN** script exits immediately (set -e) +- **AND** undefined variables cause exit (set -u) +- **AND** pipe failures are caught (set -o pipefail) + +#### Scenario: File path resolution +- **WHEN** script determines file locations +- **THEN** paths are calculated relative to script location +- **AND** script works regardless of working directory + +### Requirement: Documentation +The system SHALL provide documentation for the update script. + +#### Scenario: Script usage documentation +- **WHEN** maintainer needs to use update script +- **THEN** scripts/README.md explains when and how to use it +- **AND** example workflow is provided + +#### Scenario: Script listing +- **WHEN** maintainer views scripts/README.md +- **THEN** all maintenance scripts are documented +- **AND** purpose of each script is clear diff --git a/openspec/changes/archive/2026-01-09-add-flake-update-script/tasks.md b/openspec/changes/archive/2026-01-09-add-flake-update-script/tasks.md new file mode 100644 index 000000000..b97887656 --- /dev/null +++ b/openspec/changes/archive/2026-01-09-add-flake-update-script/tasks.md @@ -0,0 +1,55 @@ +## 1. Create Update Script + +- [x] 1.1 Create scripts/update-flake.sh file +- [x] 1.2 Add shebang and error handling (set -euo pipefail) +- [x] 1.3 Add path resolution for project root and files +- [x] 1.4 Make script executable (chmod +x) + +## 2. Implement Version Update Logic + +- [x] 2.1 Extract version from package.json using Node.js +- [x] 2.2 Use sed to update version in flake.nix +- [x] 2.3 Report if version already up-to-date +- [x] 2.4 Display detected version to user + +## 3. Implement Hash Update Logic + +- [x] 3.1 Set placeholder hash in flake.nix +- [x] 3.2 Run nix build and capture output (allow failure) +- [x] 3.3 Extract correct hash from build error using grep +- [x] 3.4 Handle case where hash extraction fails +- [x] 3.5 Update flake.nix with correct hash +- [x] 3.6 Display detected hash to user + +## 4. Add Build Verification + +- [x] 4.1 Run nix build after hash update +- [x] 4.2 Check for dirty git tree warning +- [x] 4.3 Report success or failure clearly + +## 5. Add User Feedback + +- [x] 5.1 Add progress messages for each step +- [x] 5.2 Add success summary with version and hash +- [x] 5.3 Add next steps instructions (test, commit) +- [x] 5.4 Add error messages with context + +## 6. Create Documentation + +- [x] 6.1 Create scripts/README.md +- [x] 6.2 Document update-flake.sh purpose and usage +- [x] 6.3 Add example workflow +- [x] 6.4 Document other existing scripts + +## 7. Testing + +- [x] 7.1 Test script runs successfully +- [x] 7.2 Verify version is extracted correctly +- [x] 7.3 Verify hash is updated correctly +- [x] 7.4 Verify build succeeds after update +- [x] 7.5 Test idempotency (running twice works) + +## 8. Integration + +- [ ] 8.1 Add note to release process documentation +- [ ] 8.2 Use in next actual version bump to validate workflow diff --git a/openspec/changes/archive/2026-01-15-add-nix-ci-validation/design.md b/openspec/changes/archive/2026-01-15-add-nix-ci-validation/design.md new file mode 100644 index 000000000..25cd6d2b6 --- /dev/null +++ b/openspec/changes/archive/2026-01-15-add-nix-ci-validation/design.md @@ -0,0 +1,206 @@ +# Design: Nix CI Validation + +## Context + +OpenSpec recently added Nix flake support to enable Nix users to install the tool. This includes: +- `flake.nix`: Nix package definition with pnpm dependency fetching +- `scripts/update-flake.sh`: Automation script to update version and hash when releasing + +Currently, there is no CI validation ensuring these Nix artifacts remain functional. The existing CI workflow (.github/workflows/ci.yml) validates Node.js builds, tests, and linting across multiple platforms (Linux, macOS, Windows) but does not validate Nix builds. + +**Stakeholders**: Nix users, maintainers, contributors who need confidence that Nix support works. + +**Constraints**: +- Must work in GitHub Actions Linux runners +- Should minimize CI runtime impact (<5 minutes added) +- Should support local testing with `act` for rapid iteration +- Must integrate with existing required checks + +## Goals / Non-Goals + +**Goals**: +- Validate `nix build` succeeds on every PR/push +- Validate `scripts/update-flake.sh` executes without errors +- Ensure Nix support doesn't regress silently +- Support local testing with `act` +- Optimize with caching to minimize CI time + +**Non-Goals**: +- Testing on macOS (GitHub-hosted macOS runners are slower and more expensive; Nix flake already declares macOS support) +- Building for all declared systems (x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin) - focus on most common platform +- Validating Nix flake quality/style (nixpkgs-fmt, etc.) - can be added later if needed +- Running OpenSpec's full test suite through Nix build - existing CI already does this + +## Decisions + +### Decision 1: Use DeterminateSystems nix-installer-action + +**What**: Use `determinatesystems/nix-installer-action` for installing Nix in CI. + +**Why**: +- Official GitHub Action maintained by Determinate Systems (Nix experts) +- Handles GitHub Actions environment quirks automatically +- Includes automatic caching configuration +- More reliable than curl | sh installation script +- Better error messages and diagnostics + +**Alternatives considered**: +- Official Nix installer (`curl -L https://nixos.org/nix/install | sh`): Works but requires manual setup of flakes, caching, and CI-specific configuration +- `cachix/install-nix-action`: Popular alternative but determinatesystems is more actively maintained and has better GHA integration + +### Decision 2: Use Magic Nix Cache for performance + +**What**: Use `determinatesystems/magic-nix-cache-action` for automatic binary caching. + +**Why**: +- Zero-configuration caching for Nix store +- Significantly reduces CI time on subsequent runs (from ~5min to ~1-2min) +- Free for public repositories +- Handles cache keys automatically + +**Alternatives considered**: +- Manual Nix store caching with GitHub Actions cache: More complex, requires manual cache key management +- Cachix: Excellent tool but requires account setup and token management +- No caching: Acceptable for initial implementation, but poor developer experience + +### Decision 3: Separate job for Nix validation + +**What**: Create a dedicated `nix-validate` job in .github/workflows/ci.yml that runs in parallel with other jobs. + +**Why**: +- Keeps Nix validation isolated from Node.js validation +- Allows parallel execution for faster CI +- Easier to debug when Nix-specific issues occur +- Can be marked as required check independently + +**Alternatives considered**: +- Add Nix steps to existing jobs: Creates coupling between Node.js and Nix validation, harder to maintain +- Separate workflow file: Overkill for a single job, harder to manage required checks + +### Decision 4: Validate update script by executing it + +**What**: Run `scripts/update-flake.sh` as part of CI validation. + +**Why**: +- Ensures the script doesn't break due to changes in package.json format, nix build output, or dependencies +- Tests the full workflow users will follow when releasing +- Catches errors early + +**Implementation approach**: +- Execute script in a way that doesn't modify git state (or discard changes after) +- Verify script exits with code 0 +- Optionally validate that flake.nix contains expected patterns after execution + +**Alternatives considered**: +- Mock/dry-run mode: Would require modifying the script significantly +- Skip validation: Risky - script could break and only be discovered at release time +- Only run on release branches: Misses issues early in development + +### Decision 5: Run on pull_request and push to main + +**What**: Configure Nix validation job to run on: +- `pull_request` events (any PR to main) +- `push` events (direct pushes to main) +- `workflow_dispatch` (manual trigger for testing) + +**Why**: +- Catches issues before merge (pull_request) +- Validates main branch stays healthy (push) +- Allows manual testing without creating PRs (workflow_dispatch) + +### Decision 6: Support act for local testing + +**What**: Ensure workflow is compatible with `act` tool for local CI testing. + +**Why**: +- Faster iteration when developing CI changes +- Allows testing without pushing to GitHub +- Reduces commit noise from CI debugging + +**Requirements**: +- Use standard GitHub Actions syntax +- Document any act-specific configuration needed +- Test that Nix can be installed in act's Docker containers + +**Limitations**: +- act may not perfectly replicate GitHub's runners, but close enough for validation + +## Risks / Trade-offs + +### Risk: CI runtime increase + +**Impact**: Adding Nix validation will increase total CI time by 2-5 minutes per run. + +**Mitigation**: +- Run Nix job in parallel with existing jobs (no blocking delay) +- Use magic-nix-cache for subsequent runs (~1-2 min with cache) +- Configure appropriate timeout (10 minutes max) + +**Acceptance**: The benefit of preventing Nix regressions outweighs the cost. + +### Risk: Nix installer failures in CI + +**Impact**: Transient failures in Nix installation could block PRs. + +**Mitigation**: +- Use determinatesystems action which has retry logic +- Monitor for flaky failures and adjust if needed +- Document troubleshooting steps + +**Acceptance**: Nix installation is generally stable in GHA; this is low risk. + +### Risk: Update script modifies git state + +**Impact**: Running update-flake.sh modifies flake.nix, which could cause CI to fail if git state is checked. + +**Mitigation**: +- Run script in isolation without committing changes +- Add `git checkout -- flake.nix` after validation +- Or accept dirty git state in CI (doesn't affect build validation) + +**Acceptance**: Script validation is important enough to handle this carefully. + +### Risk: act compatibility issues + +**Impact**: Workflow might not work perfectly with act due to Docker environment differences. + +**Mitigation**: +- Document known limitations +- Focus on GitHub Actions as primary validation target +- Use act as best-effort local testing + +**Acceptance**: act support is nice-to-have, not required. + +## Migration Plan + +### Phase 1: Add Nix job (new, non-required) +1. Add `nix-validate` job to .github/workflows/ci.yml +2. Configure to run in parallel with existing jobs +3. Do NOT mark as required check initially +4. Monitor for ~1 week to ensure stability + +### Phase 2: Make required +1. After validation is stable, add to required checks +2. Update branch protection rules in GitHub settings +3. Document in CONTRIBUTING.md or README + +### Rollback Plan +If Nix validation causes issues: +1. Remove job from required checks in GitHub settings (immediate) +2. Comment out or remove job from workflow (permanent fix) +3. Investigate and fix issues +4. Re-enable following same phased approach + +## Open Questions + +- **Q**: Should we test update-flake.sh on every CI run, or only when package.json or pnpm-lock.yaml changes? + - **A**: Test on every run for simplicity. The script is fast (<30 seconds) and catching regressions is valuable. + +- **Q**: Should we validate on macOS as well? + - **A**: No for initial implementation. Linux validation is sufficient and macOS runners are slower/more expensive. Can add later if users report macOS-specific issues. + +- **Q**: Should we run full OpenSpec tests through the Nix build? + - **A**: No. The Nix build already runs `pnpm test` as part of its build phase. Existing CI jobs cover testing thoroughly. Nix validation focuses on build success. + +- **Q**: What timeout should we use for the Nix validation job? + - **A**: Start with 10 minutes. With caching, jobs should complete in 1-3 minutes. Without cache (first run), 5-7 minutes is expected. diff --git a/openspec/changes/archive/2026-01-15-add-nix-ci-validation/proposal.md b/openspec/changes/archive/2026-01-15-add-nix-ci-validation/proposal.md new file mode 100644 index 000000000..c53b0e3ff --- /dev/null +++ b/openspec/changes/archive/2026-01-15-add-nix-ci-validation/proposal.md @@ -0,0 +1,21 @@ +# Add Nix CI Validation + +## Why + +The project recently added Nix flake support (flake.nix) and an automated update script (scripts/update-flake.sh) to enable Nix users to install OpenSpec. However, there is no CI validation ensuring these Nix artifacts continue to work as the project evolves. This creates risk that breaking changes could be merged without detection. + +## What Changes + +- Add a new GitHub Actions workflow job to validate Nix flake builds successfully +- Add validation that the update-flake.sh script executes without errors +- Test on Linux (where Nix support is most common) +- Ensure CI fails if Nix build or update script breaks +- Enable local testing with `act` for developers + +## Impact + +- Affected specs: New capability `ci-nix-validation` +- Affected code: `.github/workflows/ci.yml` (add new job) +- Affected infrastructure: GitHub Actions runners with Nix installed +- Benefits: Prevents regressions in Nix support, gives confidence to Nix users +- Trade-offs: Adds ~2-3 minutes to CI runtime diff --git a/openspec/changes/archive/2026-01-15-add-nix-ci-validation/specs/ci-nix-validation/spec.md b/openspec/changes/archive/2026-01-15-add-nix-ci-validation/specs/ci-nix-validation/spec.md new file mode 100644 index 000000000..4bda1b8f4 --- /dev/null +++ b/openspec/changes/archive/2026-01-15-add-nix-ci-validation/specs/ci-nix-validation/spec.md @@ -0,0 +1,104 @@ +# CI Nix Validation Specification + +## ADDED Requirements + +### Requirement: Nix Flake Build Validation + +The CI system SHALL validate that the Nix flake builds successfully on every pull request and push to main. + +#### Scenario: Successful flake build + +- **WHEN** a pull request or push to main is made +- **THEN** the CI SHALL execute `nix build` and verify it completes with exit code 0 +- **AND** the build output SHALL contain the openspec binary + +#### Scenario: Flake build failure + +- **WHEN** the Nix flake configuration is broken +- **THEN** the CI job SHALL fail with a non-zero exit code +- **AND** the CI SHALL prevent merging of the pull request + +#### Scenario: Multi-platform support check + +- **WHEN** the flake declares support for multiple systems +- **THEN** the CI SHALL validate the flake builds on at least Linux (x86_64-linux) + +### Requirement: Update Script Validation + +The CI system SHALL validate that the update-flake.sh script executes successfully and produces valid output. + +#### Scenario: Update script execution + +- **WHEN** the CI runs the update script validation +- **THEN** the script SHALL execute without errors +- **AND** the script SHALL correctly extract the version from package.json +- **AND** the script SHALL update flake.nix with the correct version + +#### Scenario: Update script with mock hash + +- **WHEN** validating the update script in CI +- **THEN** the script SHALL be able to detect and extract the correct pnpm dependency hash +- **AND** the flake.nix SHALL be updated with a valid sha256 hash + +### Requirement: CI Job Integration + +The Nix validation jobs SHALL be integrated into the existing GitHub Actions workflow and required for merge. + +#### Scenario: PR merge requirements + +- **WHEN** a pull request is created +- **THEN** the Nix validation job SHALL be included in required checks +- **AND** the PR SHALL NOT be mergeable until Nix validation passes + +#### Scenario: Job execution triggers + +- **WHEN** code is pushed to a pull request OR pushed to main OR manually triggered +- **THEN** the Nix validation job SHALL execute automatically + +### Requirement: Local Testing Support + +The CI workflow SHALL be testable locally using the `act` tool to enable rapid iteration. + +#### Scenario: Local CI execution with act + +- **WHEN** a developer runs `act` with the Nix validation workflow +- **THEN** the workflow SHALL execute in the local Docker environment +- **AND** the developer SHALL receive feedback on Nix build status without pushing to GitHub + +#### Scenario: Act configuration compatibility + +- **WHEN** the workflow is designed +- **THEN** it SHALL use standard GitHub Actions syntax compatible with `act` +- **AND** any Nix-specific setup SHALL work in the act Docker environment + +### Requirement: Nix Installation in CI + +The CI environment SHALL have Nix properly installed and configured before running validation. + +#### Scenario: Nix installation step + +- **WHEN** the Nix validation job starts +- **THEN** Nix SHALL be installed using the official Nix installer or determinatesystems/nix-installer-action +- **AND** the Nix installation SHALL be cached for subsequent runs to improve performance + +#### Scenario: Nix configuration for CI + +- **WHEN** Nix is installed in CI +- **THEN** it SHALL be configured to work in the GitHub Actions environment +- **AND** experimental features (flakes, nix-command) SHALL be enabled + +### Requirement: CI Performance Optimization + +The Nix validation SHALL be optimized to minimize CI runtime impact. + +#### Scenario: Acceptable runtime + +- **WHEN** the Nix validation job runs +- **THEN** it SHALL complete in under 5 minutes on a clean run +- **AND** with caching, it SHALL complete in under 3 minutes on subsequent runs + +#### Scenario: Parallel execution + +- **WHEN** multiple CI jobs are running +- **THEN** the Nix validation job SHALL run in parallel with other validation jobs (tests, lint) +- **AND** SHALL NOT block other independent checks diff --git a/openspec/changes/archive/2026-01-15-add-nix-ci-validation/tasks.md b/openspec/changes/archive/2026-01-15-add-nix-ci-validation/tasks.md new file mode 100644 index 000000000..e1e759b58 --- /dev/null +++ b/openspec/changes/archive/2026-01-15-add-nix-ci-validation/tasks.md @@ -0,0 +1,49 @@ +# Implementation Tasks + +## 1. Add Nix Installation to CI + +- [x] 1.1 Research Nix installation options for GitHub Actions (nix-installer-action vs manual install) +- [x] 1.2 Add Nix installation step to .github/workflows/ci.yml +- [x] 1.3 Configure Nix with experimental features enabled (flakes, nix-command) +- [x] 1.4 Add Nix store caching to improve CI performance + +## 2. Create Nix Build Validation Job + +- [x] 2.1 Add new `nix-flake-validate` job to .github/workflows/ci.yml +- [x] 2.2 Implement `nix build` step with proper error handling +- [x] 2.3 Add verification step to confirm binary exists in build output +- [x] 2.4 Add step to test binary execution (`nix run . -- --version`) + +## 3. Add Update Script Validation + +- [x] 3.1 Add job step to run scripts/update-flake.sh in dry-run or test mode +- [x] 3.2 Verify script executes without errors +- [x] 3.3 Add validation that version is correctly extracted from package.json +- [x] 3.4 Verify flake.nix is updated with correct format (version and hash) + +## 4. Configure Job Dependencies and Requirements + +- [x] 4.1 Configure Nix validation job to run on pull_request and push events +- [x] 4.2 Add Nix validation to required checks list +- [x] 4.3 Configure job to run in parallel with existing test/lint jobs +- [x] 4.4 Set appropriate timeout (5-10 minutes) + +## 5. Test with act Locally + +- [x] 5.1 Install act locally if not already available +- [x] 5.2 Test Nix validation job using `act pull_request` +- [x] 5.3 Verify act can run the workflow with Nix installed +- [x] 5.4 Document any act-specific configuration needed in .actrc or README + +## 6. Documentation and Finalization + +- [x] 6.1 Add documentation about Nix CI validation to README or CONTRIBUTING.md +- [x] 6.2 Document how to test CI locally with act +- [ ] 6.3 Update CI badge or status indicators if needed +- [ ] 6.4 Test end-to-end by creating a test PR + +## 7. Archive Change + +- [x] 7.1 After merge and verification, create new spec file at openspec/specs/ci-nix-validation/spec.md +- [x] 7.2 Move change directory to openspec/changes/archive/[date]-add-nix-ci-validation/ +- [x] 7.3 Run `openspec validate --strict` to confirm archived change passes diff --git a/openspec/specs/ci-nix-validation/spec.md b/openspec/specs/ci-nix-validation/spec.md new file mode 100644 index 000000000..b7d9b6e80 --- /dev/null +++ b/openspec/specs/ci-nix-validation/spec.md @@ -0,0 +1,107 @@ +# ci-nix-validation Specification + +## Purpose + +Validates Nix flake builds and maintenance scripts in CI to ensure Nix users can reliably install and use OpenSpec. Prevents regressions in Nix support by testing builds and the update-flake.sh script on every pull request and push to main. +## Requirements +### Requirement: Nix Flake Build Validation + +The CI system SHALL validate that the Nix flake builds successfully on every pull request and push to main. + +#### Scenario: Successful flake build + +- **WHEN** a pull request or push to main is made +- **THEN** the CI SHALL execute `nix build` and verify it completes with exit code 0 +- **AND** the build output SHALL contain the openspec binary + +#### Scenario: Flake build failure + +- **WHEN** the Nix flake configuration is broken +- **THEN** the CI job SHALL fail with a non-zero exit code +- **AND** the CI SHALL prevent merging of the pull request + +#### Scenario: Multi-platform support check + +- **WHEN** the flake declares support for multiple systems +- **THEN** the CI SHALL validate the flake builds on at least Linux (x86_64-linux) + +### Requirement: Update Script Validation + +The CI system SHALL validate that the update-flake.sh script executes successfully and produces valid output. + +#### Scenario: Update script execution + +- **WHEN** the CI runs the update script validation +- **THEN** the script SHALL execute without errors +- **AND** the script SHALL correctly extract the version from package.json +- **AND** the script SHALL update flake.nix with the correct version + +#### Scenario: Update script with mock hash + +- **WHEN** validating the update script in CI +- **THEN** the script SHALL be able to detect and extract the correct pnpm dependency hash +- **AND** the flake.nix SHALL be updated with a valid sha256 hash + +### Requirement: CI Job Integration + +The Nix validation jobs SHALL be integrated into the existing GitHub Actions workflow and required for merge. + +#### Scenario: PR merge requirements + +- **WHEN** a pull request is created +- **THEN** the Nix validation job SHALL be included in required checks +- **AND** the PR SHALL NOT be mergeable until Nix validation passes + +#### Scenario: Job execution triggers + +- **WHEN** code is pushed to a pull request OR pushed to main OR manually triggered +- **THEN** the Nix validation job SHALL execute automatically + +### Requirement: Local Testing Support + +The CI workflow SHALL be testable locally using the `act` tool to enable rapid iteration. + +#### Scenario: Local CI execution with act + +- **WHEN** a developer runs `act` with the Nix validation workflow +- **THEN** the workflow SHALL execute in the local Docker environment +- **AND** the developer SHALL receive feedback on Nix build status without pushing to GitHub + +#### Scenario: Act configuration compatibility + +- **WHEN** the workflow is designed +- **THEN** it SHALL use standard GitHub Actions syntax compatible with `act` +- **AND** any Nix-specific setup SHALL work in the act Docker environment + +### Requirement: Nix Installation in CI + +The CI environment SHALL have Nix properly installed and configured before running validation. + +#### Scenario: Nix installation step + +- **WHEN** the Nix validation job starts +- **THEN** Nix SHALL be installed using the official Nix installer or determinatesystems/nix-installer-action +- **AND** the Nix installation SHALL be cached for subsequent runs to improve performance + +#### Scenario: Nix configuration for CI + +- **WHEN** Nix is installed in CI +- **THEN** it SHALL be configured to work in the GitHub Actions environment +- **AND** experimental features (flakes, nix-command) SHALL be enabled + +### Requirement: CI Performance Optimization + +The Nix validation SHALL be optimized to minimize CI runtime impact. + +#### Scenario: Acceptable runtime + +- **WHEN** the Nix validation job runs +- **THEN** it SHALL complete in under 5 minutes on a clean run +- **AND** with caching, it SHALL complete in under 3 minutes on subsequent runs + +#### Scenario: Parallel execution + +- **WHEN** multiple CI jobs are running +- **THEN** the Nix validation job SHALL run in parallel with other validation jobs (tests, lint) +- **AND** SHALL NOT block other independent checks + diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 000000000..32779c528 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,38 @@ +# OpenSpec Scripts + +Utility scripts for OpenSpec maintenance and development. + +## update-flake.sh + +Updates `flake.nix` version and dependency hash automatically. + +**When to use**: After updating dependencies or releasing a new version. + +**Usage**: +```bash +./scripts/update-flake.sh +``` + +**What it does**: +1. Extracts version from `package.json` +2. Updates version in `flake.nix` +3. Automatically determines the correct pnpm dependency hash +4. Updates the hash in `flake.nix` +5. Verifies the build succeeds + +**Example workflow**: +```bash +# After version bump and dependency updates +pnpm install +./scripts/update-flake.sh +git add flake.nix +git commit -m "chore: update flake.nix for v0.18.0" +``` + +## postinstall.js + +Post-installation script that runs after package installation. + +## pack-version-check.mjs + +Validates package version consistency before publishing. diff --git a/scripts/update-flake.sh b/scripts/update-flake.sh new file mode 100755 index 000000000..022c97196 --- /dev/null +++ b/scripts/update-flake.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Script to update flake.nix version and dependency hash +# Run this after updating package.json version + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +FLAKE_FILE="$PROJECT_ROOT/flake.nix" +PACKAGE_JSON="$PROJECT_ROOT/package.json" + +# Detect OS and set sed in-place flag +if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS (BSD sed) requires empty string argument for -i + SED_INPLACE=(-i '') +else + # Linux (GNU sed) + SED_INPLACE=(-i) +fi + +echo "==> Updating flake.nix..." + +# Extract version from package.json +VERSION=$(node -p "require('$PACKAGE_JSON').version") +echo " Detected version: $VERSION" + +# Update version in flake.nix +if ! grep -q "version = \"$VERSION\"" "$FLAKE_FILE"; then + echo " Updating version in flake.nix..." + sed "${SED_INPLACE[@]}" "s|version = \"[^\"]*\"|version = \"$VERSION\"|" "$FLAKE_FILE" +else + echo " Version already up-to-date in flake.nix" +fi + +# Set placeholder hash to trigger error +echo " Setting placeholder hash..." +PLACEHOLDER="sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" +sed "${SED_INPLACE[@]}" "s|hash = \"sha256-[^\"]*\"|hash = \"$PLACEHOLDER\"|" "$FLAKE_FILE" + +# Try to build and capture the correct hash +echo " Building to get correct hash (this will fail)..." +BUILD_OUTPUT=$(nix build 2>&1 || true) + +# Extract the correct hash from error output +CORRECT_HASH=$(echo "$BUILD_OUTPUT" | grep -oP 'got:\s+\Ksha256-[A-Za-z0-9+/=]+' | head -1) + +if [ -z "$CORRECT_HASH" ]; then + echo "❌ Error: Could not extract hash from build output" + echo "Build output:" + echo "$BUILD_OUTPUT" + exit 1 +fi + +echo " Detected hash: $CORRECT_HASH" + +# Update flake.nix with correct hash +sed "${SED_INPLACE[@]}" "s|hash = \"$PLACEHOLDER\"|hash = \"$CORRECT_HASH\"|" "$FLAKE_FILE" + +# Verify the build works +echo " Verifying build..." +if nix build 2>&1 | grep -q "warning: Git tree.*is dirty"; then + echo "⚠️ Warning: Git tree is dirty, but build succeeded" +else + echo "✅ Build successful" +fi + +echo "" +echo "✅ flake.nix updated successfully!" +echo " Version: $VERSION" +echo " Hash: $CORRECT_HASH" +echo "" +echo "Next steps:" +echo " 1. Test: nix run . -- --version" +echo " 2. Commit: git add flake.nix" +echo " 3. Include in version bump commit"