diff --git a/.gitignore b/.gitignore index 5ecf229d..743fdf01 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,4 @@ CLAUDE.md # Pnpm .pnpm-store/ +result diff --git a/README.md b/README.md index 7b6c7354..80cb0565 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,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 ``` @@ -144,6 +146,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 00000000..45e7b5aa --- /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 00000000..62930d29 --- /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.18.0"; + + src = ./.; + + pnpmDeps = pkgs.fetchPnpmDeps { + inherit (finalAttrs) pname version src; + pnpm = pkgs.pnpm_9; + fetcherVersion = 3; + hash = "sha256-ltG+wmr2aiRmghEMGOZVYifvrY8qdLDfizLq6a8Ds0s="; + }; + + 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 00000000..75b7b3e3 --- /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 00000000..ec4cba3b --- /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 00000000..77cb3973 --- /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 00000000..86724903 --- /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 00000000..190d9ec4 --- /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 00000000..e5cb8126 --- /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 00000000..0cfb0d55 --- /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 00000000..a81b10db --- /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 00000000..476bb4ab --- /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 00000000..b9788765 --- /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/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..32779c52 --- /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 00000000..f8f802b8 --- /dev/null +++ b/scripts/update-flake.sh @@ -0,0 +1,66 @@ +#!/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" + +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 -i "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 -i "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 -i "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"