Skip to content

docs: correct @fortemi/graph dependency direction in architecture + A… #7

docs: correct @fortemi/graph dependency direction in architecture + A…

docs: correct @fortemi/graph dependency direction in architecture + A… #7

Workflow file for this run

# Public npmjs.org publication for the GitHub mirror.
#
# Gitea remains the primary CI/internal-registry release path. This workflow is
# the final public distribution leg: it runs only from signed v* release tags,
# attaches npm provenance from GitHub Actions OIDC, and authenticates with the
# NPMJS_TOKEN secret configured on the GitHub mirror.
name: Publish to npmjs.org
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag_to_publish:
description: 'Signed v* tag to publish'
required: true
type: string
concurrency:
group: npmjs-publish-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
id-token: write
jobs:
publish:
name: Publish public npm packages with provenance
runs-on: ubuntu-latest
container: node:24@sha256:050bf2bbe33c1d6754e060bec89378a79ed831f04a7bb1a53fe45e997df7b3bb
timeout-minutes: 20
defaults:
run:
shell: bash
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Setup pnpm
uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
- name: Setup Node for npmjs.org
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Resolve release tag
id: release
env:
INPUT_TAG_TO_PUBLISH: ${{ github.event.inputs.tag_to_publish }}
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${INPUT_TAG_TO_PUBLISH}"
else
TAG="${GITHUB_REF#refs/tags/}"
fi
case "$TAG" in
v*) ;;
*)
echo "Release tag must start with v: $TAG" >&2
exit 1
;;
esac
echo "release_tag=$TAG" >> "$GITHUB_OUTPUT"
echo "GITHUB_REF=refs/tags/$TAG" >> "$GITHUB_ENV"
- name: Fetch release tag object
run: |
set -euo pipefail
git config --global --add safe.directory "$GITHUB_WORKSPACE"
TAG="${{ steps.release.outputs.release_tag }}"
git fetch origin "+refs/tags/${TAG}:refs/tags/${TAG}" --depth=1
git checkout --detach "refs/tags/${TAG}"
- name: Verify signed release tag
env:
RELEASE_TAG: ${{ steps.release.outputs.release_tag }}
run: bash tools/ci/verify-signed-tag.sh
- name: Verify pnpm supply-chain gates
run: |
set -euo pipefail
node - <<'CHECK'
const fs = require('node:fs');
const text = fs.readFileSync('pnpm-workspace.yaml', 'utf8');
const age = text.match(/^minimumReleaseAge:\s*(\d+)\s*$/m);
const exotic = text.match(/^blockExoticSubdeps:\s*(true|false)\s*$/m);
if (!age || Number(age[1]) < 10080) {
throw new Error('pnpm minimumReleaseAge must be at least 10080 minutes');
}
if (!exotic || exotic[1] !== 'true') {
throw new Error('pnpm blockExoticSubdeps must be true');
}
CHECK
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm typecheck
- name: Lint
run: pnpm lint
- name: Build
run: pnpm build
- name: Verify package versions
env:
RELEASE_TAG: ${{ steps.release.outputs.release_tag }}
run: |
set -euo pipefail
TAG_VERSION="${RELEASE_TAG#v}"
export TAG_VERSION
node - <<'CHECK'
const fs = require('node:fs');
const expected = process.env.TAG_VERSION;
for (const dir of ['packages/core', 'packages/graph', 'packages/react']) {
const pkg = JSON.parse(fs.readFileSync(dir + '/package.json', 'utf8'));
if (pkg.version !== expected) {
throw new Error(pkg.name + ' version ' + pkg.version + ' does not match release tag ' + expected);
}
}
CHECK
- name: Pack and inspect artifacts
env:
RELEASE_TAG: ${{ steps.release.outputs.release_tag }}
run: |
set -euo pipefail
TAG_VERSION="${RELEASE_TAG#v}"
export TAG_VERSION
PACK_DIR="/tmp/fortemi-publish-check"
rm -rf "$PACK_DIR"
mkdir -p "$PACK_DIR"
(cd packages/core && pnpm pack --pack-destination "$PACK_DIR")
(cd packages/graph && pnpm pack --pack-destination "$PACK_DIR")
(cd packages/react && pnpm pack --pack-destination "$PACK_DIR")
CORE_TGZ="$PACK_DIR/fortemi-core-${TAG_VERSION}.tgz"
GRAPH_TGZ="$PACK_DIR/fortemi-graph-${TAG_VERSION}.tgz"
REACT_TGZ="$PACK_DIR/fortemi-react-${TAG_VERSION}.tgz"
test -s "$CORE_TGZ"
test -s "$GRAPH_TGZ"
test -s "$REACT_TGZ"
tar -tzf "$CORE_TGZ" > "$PACK_DIR/core-files.txt"
tar -tzf "$GRAPH_TGZ" > "$PACK_DIR/graph-files.txt"
tar -tzf "$REACT_TGZ" > "$PACK_DIR/react-files.txt"
if grep -E 'package/(\.aiwg|\.agents|\.codex|\.claude)/' "$PACK_DIR/core-files.txt" "$PACK_DIR/graph-files.txt" "$PACK_DIR/react-files.txt"; then
echo "Packed artifact contains local AI/operator metadata" >&2
exit 1
fi
tar -xOf "$REACT_TGZ" package/package.json > "$PACK_DIR/react-package.json"
node - <<'CHECK'
const fs = require('node:fs');
const pkg = JSON.parse(fs.readFileSync('/tmp/fortemi-publish-check/react-package.json', 'utf8'));
const expected = process.env.TAG_VERSION;
for (const name of ['@fortemi/core', '@fortemi/graph']) {
const dep = pkg.dependencies && pkg.dependencies[name];
if (dep !== expected) {
throw new Error('@fortemi/react artifact depends on ' + name + ' ' + dep + ', expected ' + expected);
}
}
CHECK
- name: Publish packages to npmjs.org
env:
NODE_AUTH_TOKEN: ${{ secrets.NPMJS_TOKEN }}
RELEASE_TAG: ${{ steps.release.outputs.release_tag }}
run: |
set -euo pipefail
if [ -z "${NODE_AUTH_TOKEN:-}" ]; then
echo "NPMJS_TOKEN is required on the GitHub mirror" >&2
exit 1
fi
TAG_VERSION="${RELEASE_TAG#v}"
NPM_TAG="latest"
if [ "$TAG_VERSION" != "${TAG_VERSION%%-*}" ]; then
NPM_TAG="next"
if echo "$TAG_VERSION" | grep -qiE -- '-nightly\.'; then
NPM_TAG="nightly"
fi
fi
echo "Publishing npm dist-tag: $NPM_TAG"
publish_package() {
package_dir="$1"
package_name="$(node -p "require('./${package_dir}/package.json').name")"
package_version="$(node -p "require('./${package_dir}/package.json').version")"
if npm view "${package_name}@${package_version}" version >/dev/null 2>&1; then
echo "${package_name}@${package_version} already exists on npmjs.org; skipping publish"
else
echo "Publishing ${package_name}@${package_version} to npmjs.org"
(cd "$package_dir" && pnpm publish --provenance --access public --no-git-checks --tag "$NPM_TAG")
fi
for attempt in 1 2 3 4 5; do
published="$(npm view "${package_name}@${package_version}" version --prefer-online --no-update-notifier 2>/dev/null || echo "")"
if [ "$published" = "$package_version" ]; then
break
fi
echo "Attempt ${attempt}: ${package_name}@${package_version} not visible yet; retrying in 10s..."
sleep 10
if [ "$attempt" = "5" ]; then
echo "${package_name}@${package_version} did not become visible on npmjs.org" >&2
exit 1
fi
done
for attempt in 1 2 3 4 5; do
if npm dist-tag add "${package_name}@${package_version}" "$NPM_TAG"; then
break
fi
echo "Attempt ${attempt}: dist-tag promotion failed; retrying in 10s..."
sleep 10
if [ "$attempt" = "5" ]; then
echo "dist-tag promotion exhausted retries for ${package_name}@${package_version}" >&2
exit 1
fi
done
}
publish_package packages/core
publish_package packages/graph
publish_package packages/react