mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
fix(release): enforce publish and runtime policy
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
],
|
||||
"engines": {
|
||||
"claudeCode": ">=2.0.0",
|
||||
"node": ">=20.0.0"
|
||||
"node": ">=22.13.0"
|
||||
},
|
||||
"mcpServers": {
|
||||
"agentic-qe": {
|
||||
|
||||
@@ -144,12 +144,13 @@ if command -v node >/dev/null 2>&1; then
|
||||
node_version=$(node --version)
|
||||
log_success "Node.js installed: $node_version"
|
||||
|
||||
# Check if Node.js version is 20+
|
||||
# Check if Node.js version is 22.13+
|
||||
node_major=$(echo "$node_version" | cut -d'.' -f1 | sed 's/v//')
|
||||
if [ "$node_major" -ge 20 ]; then
|
||||
log_success "Node.js version meets requirements (≥20.0.0)"
|
||||
node_minor=$(echo "$node_version" | cut -d'.' -f2)
|
||||
if [ "$node_major" -gt 22 ] || { [ "$node_major" -eq 22 ] && [ "$node_minor" -ge 13 ]; }; then
|
||||
log_success "Node.js version meets requirements (≥22.13.0)"
|
||||
else
|
||||
log_error "Node.js version too old. Required: ≥20.0.0, Found: $node_version"
|
||||
log_error "Node.js version too old. Required: ≥22.13.0, Found: $node_version"
|
||||
fi
|
||||
else
|
||||
log_error "Node.js not installed"
|
||||
@@ -213,4 +214,4 @@ else
|
||||
fi
|
||||
log_error "Please fix critical errors before proceeding with V3 development."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Resilient AQE hook shim (#510 item 5 — ports ruflo's hook-shim contract).
|
||||
*
|
||||
* Written in Node (CommonJS) so ONE file is cross-platform — Node is already a
|
||||
* hard dependency of AQE (the CLI is Node >= 20), so this runs identically on
|
||||
* hard dependency of AQE (the CLI is Node >= 22.13), so this runs identically on
|
||||
* Windows / macOS / Linux with no POSIX `.sh` + `.cjs` twin. (It replaced an
|
||||
* earlier `aqe-hook.sh`, which forced a Windows twin and a `node_modules/.bin`
|
||||
* shell-wrapper spawn — see PR #512 discussion.)
|
||||
|
||||
@@ -4,12 +4,6 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Perform a dry run (no actual publish)'
|
||||
required: false
|
||||
default: 'false'
|
||||
type: boolean
|
||||
|
||||
# Required for npm trusted publishers (OIDC)
|
||||
permissions:
|
||||
@@ -360,7 +354,7 @@ jobs:
|
||||
pushd "${CONSUMER_DIR}" >/dev/null
|
||||
echo '{"name":"audit-consumer","version":"0.0.0","private":true}' > package.json
|
||||
# Install our tarball as a real consumer would.
|
||||
npm install --no-audit --no-fund "${GITHUB_WORKSPACE}/${{ steps.pack.outputs.tarball }}"
|
||||
npm install --no-audit --no-fund "${{ steps.pack.outputs.tarball }}"
|
||||
# Now the moment of truth: audit from the consumer's POV, where
|
||||
# our root-only overrides do NOT apply.
|
||||
npm audit --audit-level=high
|
||||
@@ -428,14 +422,23 @@ jobs:
|
||||
fi
|
||||
echo "Version verification passed: $PACKAGE_VERSION"
|
||||
|
||||
git fetch origin main --no-tags --depth=1
|
||||
TAG_SHA=$(git rev-parse HEAD)
|
||||
MAIN_SHA=$(git rev-parse origin/main)
|
||||
if [ "$TAG_SHA" != "$MAIN_SHA" ]; then
|
||||
echo "Error: release tag SHA ($TAG_SHA) is not current merged main ($MAIN_SHA)"
|
||||
exit 1
|
||||
fi
|
||||
echo "Release lineage verification passed: $TAG_SHA"
|
||||
|
||||
- name: Publish to npm (dry run)
|
||||
if: github.event.inputs.dry_run == 'true'
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: npm publish "${{ steps.pack.outputs.tarball }}" --access public --dry-run
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish to npm
|
||||
if: github.event.inputs.dry_run != 'true'
|
||||
if: github.event_name == 'release'
|
||||
run: npm publish "${{ steps.pack.outputs.tarball }}" --access public --provenance
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -451,7 +454,7 @@ jobs:
|
||||
echo "- **Version**: $PACKAGE_VERSION" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Registry**: https://www.npmjs.com/package/$PACKAGE_NAME" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
echo "> **Note**: This was a dry run. No package was actually published." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Install with: \`npm install $PACKAGE_NAME@$PACKAGE_VERSION\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ All notable changes to the Agentic QE project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [3.14.1] - 2026-09-06
|
||||
## [3.14.1] - 2026-09-07
|
||||
|
||||
This release moves AQE to maintained Node.js runtimes and strengthens the
|
||||
evidence boundaries around runtime diagnosis, verification, session recovery,
|
||||
|
||||
@@ -4,7 +4,7 @@ All Agentic QE release notes organized by version.
|
||||
|
||||
| Version | Date | Highlights |
|
||||
|---------|------|------------|
|
||||
| [v3.14.1](v3.14.1.md) | 2026-09-06 | Node 22+ support and stronger verification, recovery, and learning evidence. |
|
||||
| [v3.14.1](v3.14.1.md) | 2026-09-07 | Node 22+ support and stronger verification, recovery, and learning evidence. |
|
||||
| [v3.14.0](v3.14.0.md) | 2026-08-30 | Qualified learning evidence, embedding provenance, fail-closed RVF recovery, and packaged QE Court. |
|
||||
| [v3.13.12](v3.13.12.md) | 2026-08-21 | External LLM provider registration for downstream hosts. |
|
||||
| [v3.13.11](v3.13.11.md) | 2026-08-14 | Reliable diagnostics, native ESM exports, and learned pattern visibility. |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# v3.14.1 Release Notes
|
||||
|
||||
**Release Date:** 2026-09-06
|
||||
**Release Date:** 2026-09-07
|
||||
|
||||
## Highlights
|
||||
|
||||
|
||||
+4
-1
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import { toErrorMessage } from '../shared/error-utils.js';
|
||||
import { createRequire } from 'node:module';
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
import type { QEKernel } from '../kernel/interfaces.js';
|
||||
@@ -257,7 +258,9 @@ async function cleanupAndExit(code: number = 0): Promise<never> {
|
||||
|
||||
const program = new Command();
|
||||
|
||||
const VERSION = typeof __CLI_VERSION__ !== 'undefined' ? __CLI_VERSION__ : '0.0.0-dev';
|
||||
const require = createRequire(import.meta.url);
|
||||
const packageVersion = (require('../../package.json') as { version: string }).version;
|
||||
const VERSION = typeof __CLI_VERSION__ !== 'undefined' ? __CLI_VERSION__ : packageVersion;
|
||||
|
||||
program
|
||||
.name('aqe')
|
||||
|
||||
@@ -9,6 +9,7 @@ describe('packed package native ESM contracts', () => {
|
||||
const tempRoot = path.join(packageRoot, 'node_modules', '.cache', `aqe-pack-esm-${process.pid}`);
|
||||
const installedRoot = path.join(tempRoot, 'node_modules', 'agentic-qe');
|
||||
let packageJson: {
|
||||
version: string;
|
||||
exports: Record<string, { import?: string }>;
|
||||
bin?: Record<string, string>;
|
||||
};
|
||||
@@ -48,6 +49,19 @@ describe('packed package native ESM contracts', () => {
|
||||
expectNativeImport('./dist/integrations/ruvector/index.js', 'RuVector barrel');
|
||||
});
|
||||
|
||||
it('reports the package version from the public CLI export', () => {
|
||||
const cliEntry = packageJson.exports['./cli']?.import;
|
||||
expect(cliEntry).toBeDefined();
|
||||
const url = pathToFileURL(path.resolve(installedRoot, cliEntry!)).href;
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
['--input-type=module', '--eval', `await import(${JSON.stringify(url)});`, '--', '--version'],
|
||||
{ cwd: installedRoot, encoding: 'utf8', timeout: 30_000 },
|
||||
);
|
||||
expect(result.status, result.stderr || result.stdout).toBe(0);
|
||||
expect(result.stdout.trim()).toBe(packageJson.version);
|
||||
});
|
||||
|
||||
it('exposes pattern mutation outcomes from the package root', () => {
|
||||
const packageEntry = packageJson.exports['.']?.import;
|
||||
expect(packageEntry).toBeDefined();
|
||||
|
||||
@@ -30,6 +30,26 @@ describe('Node support policy', () => {
|
||||
expect(lock.packages?.['']?.engines?.node).toBe('>=22.13.0');
|
||||
});
|
||||
|
||||
it('uses the same floor for the Claude Code plugin', () => {
|
||||
const manifest = JSON.parse(read('.claude-plugin/plugin.json')) as {
|
||||
engines?: { node?: string };
|
||||
};
|
||||
|
||||
expect(manifest.engines?.node).toBe('>=22.13.0');
|
||||
});
|
||||
|
||||
it('enforces the exact floor in the shipped environment validator', () => {
|
||||
const validator = read('.claude/helpers/validate-v3-config.sh');
|
||||
|
||||
expect(validator).toContain('[ "$node_major" -gt 22 ]');
|
||||
expect(validator).toContain('[ "$node_major" -eq 22 ] && [ "$node_minor" -ge 13 ]');
|
||||
expect(validator).toContain('Required: ≥22.13.0');
|
||||
});
|
||||
|
||||
it('documents the exact floor in the shipped lifecycle hook', () => {
|
||||
expect(read('.claude/hooks/aqe-hook.cjs')).toContain('CLI is Node >= 22.13');
|
||||
});
|
||||
|
||||
it('uses Node 24 for the production container', () => {
|
||||
expect(read('Dockerfile').match(/^FROM node:([^\s]+)/gm)).toEqual([
|
||||
'FROM node:24-alpine',
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const workflow = readFileSync(
|
||||
resolve(import.meta.dirname, '../../../.github/workflows/npm-publish.yml'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
describe('npm publish workflow policy', () => {
|
||||
it('keeps manual workflow dispatch dry-run only', () => {
|
||||
expect(workflow).toContain("if: github.event_name == 'workflow_dispatch'");
|
||||
expect(workflow).toContain("if: github.event_name == 'release'");
|
||||
expect(workflow).not.toContain('github.event.inputs.dry_run');
|
||||
});
|
||||
|
||||
it('passes the absolute sealed artifact directly to consumer install', () => {
|
||||
expect(workflow).toContain(
|
||||
'npm install --no-audit --no-fund "${{ steps.pack.outputs.tarball }}"',
|
||||
);
|
||||
expect(workflow).not.toContain(
|
||||
'"${GITHUB_WORKSPACE}/${{ steps.pack.outputs.tarball }}"',
|
||||
);
|
||||
});
|
||||
|
||||
it('binds release publication to current merged main', () => {
|
||||
expect(workflow).toContain('git fetch origin main --no-tags --depth=1');
|
||||
expect(workflow).toContain('if [ "$TAG_SHA" != "$MAIN_SHA" ]; then');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user