diff --git a/.github/scripts/README.md b/.github/scripts/README.md index 3d354c8..46738df 100644 --- a/.github/scripts/README.md +++ b/.github/scripts/README.md @@ -1,5 +1,15 @@ # CI Helpers +## Crabbox hydration + +The hydration workflow writes lease-scoped `.env` and `.env.sh` files +under `~/.crabbox/actions` and watches `.stop`, matching the Crabbox CLI. +The job name is metadata inside the readiness marker, not part of these paths. +Keep the Go setup inputs compatible with Crabbox's local Actions adapter; its +`setup-go` support does not accept an explicit `cache` input. +`crabbox-hydrate.test.cjs` executes the workflow's shell steps with a temporary +home to verify readiness discovery and immediate shutdown on the stop signal. + `downstream-compat.sh` compares the complete Gitcrawl CLI test suite against a pinned Crawlkit baseline and the candidate checkout. The source pins live in `ci.yml`. Update pins deliberately, with exact-head review. diff --git a/.github/scripts/crabbox-hydrate.test.cjs b/.github/scripts/crabbox-hydrate.test.cjs new file mode 100644 index 0000000..108efc5 --- /dev/null +++ b/.github/scripts/crabbox-hydrate.test.cjs @@ -0,0 +1,63 @@ +const assert = require('node:assert/strict'); +const { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } = require('node:fs'); +const { tmpdir } = require('node:os'); +const { join } = require('node:path'); +const { spawnSync } = require('node:child_process'); +const { test } = require('node:test'); + +const workflow = readFileSync(join(__dirname, '../workflows/crabbox-hydrate.yml'), 'utf8'); + +function runStep(name) { + const lines = workflow.split('\n'); + const step = lines.indexOf(` - name: ${name}`); + assert.notEqual(step, -1, `missing workflow step: ${name}`); + const start = lines.findIndex((line, index) => index > step && line === ' run: |'); + assert.notEqual(start, -1, `missing shell script: ${name}`); + const script = []; + for (const line of lines.slice(start + 1)) { + if (line && !line.startsWith(' ')) break; + script.push(line.slice(10)); + } + return script.join('\n'); +} + +function fixture(t) { + const home = mkdtempSync(join(tmpdir(), 'crawlkit-hydrate-')); + t.after(() => rmSync(home, { recursive: true, force: true })); + const env = { + PATH: process.env.PATH, + HOME: home, + CRABBOX_ID: 'cbx_test123', + CRABBOX_JOB: 'hydrate', + CRABBOX_KEEP_ALIVE_MINUTES: '90', + GITHUB_WORKSPACE: join(home, 'workspace'), + GITHUB_SHA: 'a'.repeat(40), + GITHUB_RUN_ID: '123', + }; + return { home, env, actions: join(home, '.crabbox/actions') }; +} + +test('hydration publishes the lease-scoped marker and environment used by Crabbox', (t) => { + const { home, env, actions } = fixture(t); + const result = spawnSync('bash', ['-c', runStep('Mark Crabbox ready')], { env, encoding: 'utf8' }); + assert.equal(result.status, 0, result.stderr); + const marker = readFileSync(join(actions, 'cbx_test123.env'), 'utf8'); + assert.match(marker, /^JOB=hydrate$/m); + assert.match(marker, /^RUN_ID=123$/m); + assert.ok(marker.includes(`WORKSPACE=${join(home, 'workspace')}\n`)); + assert.ok(marker.includes(`ENV_FILE=${join(actions, 'cbx_test123.env.sh')}\n`)); + assert.match(readFileSync(join(actions, 'cbx_test123.env.sh'), 'utf8'), /^export GITHUB_SHA=/m); +}); + +test('the lease-scoped stop signal ends the hydration job immediately', (t) => { + const { home, env, actions } = fixture(t); + mkdirSync(actions, { recursive: true }); + writeFileSync(join(actions, 'cbx_test123.stop'), ''); + const bin = join(home, 'bin'); + mkdirSync(bin); + // A missed stop signal must fail promptly instead of sleeping for 90 minutes. + writeFileSync(join(bin, 'sleep'), '#!/bin/sh\nexit 42\n', { mode: 0o755 }); + env.PATH = `${bin}:${env.PATH}`; + const result = spawnSync('bash', ['-c', runStep('Keep Crabbox job alive')], { env, encoding: 'utf8' }); + assert.equal(result.status, 0, result.stderr); +}); diff --git a/.github/workflows/crabbox-hydrate.yml b/.github/workflows/crabbox-hydrate.yml index ae94eff..78bd3ec 100644 --- a/.github/workflows/crabbox-hydrate.yml +++ b/.github/workflows/crabbox-hydrate.yml @@ -42,7 +42,6 @@ jobs: - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod - cache: true - name: Prepare Go workspace shell: bash @@ -74,7 +73,7 @@ jobs: ;; esac mkdir -p "$HOME/.crabbox/actions" - key="${CRABBOX_ID}.${job}" + key="${CRABBOX_ID}" state="$HOME/.crabbox/actions/${key}.env" env_file="$HOME/.crabbox/actions/${key}.env.sh" { @@ -122,7 +121,7 @@ jobs: case "$minutes" in ''|*[!0-9]*) minutes=90 ;; esac - stop="$HOME/.crabbox/actions/${CRABBOX_ID}.${job}.stop" + stop="$HOME/.crabbox/actions/${CRABBOX_ID}.stop" deadline=$(( $(date +%s) + minutes * 60 )) while [ "$(date +%s)" -lt "$deadline" ]; do if [ -f "$stop" ]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index af56fc6..682cdfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Fix Crabbox hydration readiness and shutdown with lease-scoped state files, and allow automatic local hydration by omitting an unsupported explicit cache option. +- Update SQLite's indirect pprof test dependency to the October 16 revision while preserving the required SQLite/libc version pair and Go 1.27.0 minimum. - Refresh pprof, Go analysis tooling, and the pinned TruffleHog action while retaining the Go 1.27.0 minimum and SQLite's required libc version. - Add an explicit release-workflow dry run to validate signed builds without creating a tag or publishing a release; preserve the existing-version guard for real releases. diff --git a/go.mod b/go.mod index 6d0f6bb..02a981b 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/dustin/go-humanize v1.1.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/google/pprof v0.0.0-20261014150452-c3b4e57cab2c // indirect + github.com/google/pprof v0.0.0-20261016150608-1facf8f91b50 // indirect github.com/google/uuid v1.6.0 // indirect github.com/lucasb-eyer/go-colorful v1.4.1 // indirect github.com/mattn/go-localereader v0.0.1 // indirect diff --git a/go.sum b/go.sum index 0efce5a..82a55cc 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/dustin/go-humanize v1.1.0 h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2 github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/google/pprof v0.0.0-20261014150452-c3b4e57cab2c h1:h/rBeOCBnD/SNCvq6FHunZaiCYqpbhx3FyGcVtpLaIE= -github.com/google/pprof v0.0.0-20261014150452-c3b4e57cab2c/go.mod h1:yUo1AMMpsPIDFGuwDK8nLcf8+vvAd5l2O6JDZ6hpwrM= +github.com/google/pprof v0.0.0-20261016150608-1facf8f91b50 h1:PlNWJRxu5APSV0pWqfIsZ0qt9OOGHSWfZQ4zvx+WgWs= +github.com/google/pprof v0.0.0-20261016150608-1facf8f91b50/go.mod h1:yUo1AMMpsPIDFGuwDK8nLcf8+vvAd5l2O6JDZ6hpwrM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=