chore: the home registry is The Source, never 'the forge' — sweep the misnomer out of the release rail, workflows, and release notes (Forge is a different product; the stored CI secret keeps its historical name)
This commit is contained in:
parent
c6c6ea6b57
commit
09352c2b37
4 changed files with 55 additions and 51 deletions
|
|
@ -3,7 +3,7 @@ name: CI
|
||||||
# Branch pushes only — a release TAG deliberately does not re-run CI: the
|
# Branch pushes only — a release TAG deliberately does not re-run CI: the
|
||||||
# tagged commit's CI already ran on its branch push, and the runner is
|
# tagged commit's CI already ran on its branch push, and the runner is
|
||||||
# sequential, so tag-triggered matrix jobs (~22 min) would queue AHEAD of the
|
# sequential, so tag-triggered matrix jobs (~22 min) would queue AHEAD of the
|
||||||
# tag's publish-forge run and starve every release (observed on 8.10.3 and
|
# tag's publish-source run and starve every release (observed on 8.10.3 and
|
||||||
# 9.0.0: the publish sat behind the tag's own redundant CI).
|
# 9.0.0: the publish sat behind the tag's own redundant CI).
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
name: Publish (forge)
|
name: Publish (The Source)
|
||||||
|
|
||||||
# Datacenter-side forge publish, moved off the laptop: an 87MB tarball PUT
|
# Datacenter-side publish to The Source (source.soulcraft.com — our
|
||||||
# over the laptop's WAN times out; the forge's own runner does it in seconds.
|
# self-hosted Forgejo; never call it "the forge", Forge is a different
|
||||||
|
# product), moved off the laptop: an 87MB tarball PUT over the laptop's WAN
|
||||||
|
# times out; The Source's own runner does it in seconds.
|
||||||
# scripts/release.sh tags + pushes, then polls this workflow's result (npm
|
# scripts/release.sh tags + pushes, then polls this workflow's result (npm
|
||||||
# view against the forge registry) before it ever touches the npmjs leg —
|
# view against The Source's registry) before it ever touches the npmjs leg —
|
||||||
# see the "delegation contract" in scripts/release.sh's forge-publish step.
|
# see the "delegation contract" in scripts/release.sh's home-publish step.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
@ -13,7 +15,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
name: Publish to the forge registry
|
name: Publish to The Source registry
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
@ -23,20 +25,21 @@ jobs:
|
||||||
cache: npm
|
cache: npm
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- name: Publish + readback-verify on the forge registry
|
- name: Publish + readback-verify on The Source registry
|
||||||
env:
|
env:
|
||||||
|
# The stored repo-settings secret keeps its historical name.
|
||||||
FORGE_NPM_TOKEN: ${{ secrets.FORGE_NPM_TOKEN }}
|
FORGE_NPM_TOKEN: ${{ secrets.FORGE_NPM_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
FORGE_NPM_REG="https://source.soulcraft.com/api/packages/soulcraft/npm/"
|
SOURCE_NPM_REG="https://source.soulcraft.com/api/packages/soulcraft/npm/"
|
||||||
VERSION="$(node -p "require('./package.json').version")"
|
VERSION="$(node -p "require('./package.json').version")"
|
||||||
echo "Publishing @soulcraft/brainy@${VERSION} to the forge registry..."
|
echo "Publishing @soulcraft/brainy@${VERSION} to The Source registry..."
|
||||||
|
|
||||||
TMPRC="$(mktemp)"
|
TMPRC="$(mktemp)"
|
||||||
chmod 600 "$TMPRC"
|
chmod 600 "$TMPRC"
|
||||||
{
|
{
|
||||||
echo "@soulcraft:registry=${FORGE_NPM_REG}"
|
echo "@soulcraft:registry=${SOURCE_NPM_REG}"
|
||||||
echo "//source.soulcraft.com/api/packages/soulcraft/npm/:_authToken=${FORGE_NPM_TOKEN}"
|
echo "//source.soulcraft.com/api/packages/soulcraft/npm/:_authToken=${FORGE_NPM_TOKEN}"
|
||||||
} > "$TMPRC"
|
} > "$TMPRC"
|
||||||
|
|
||||||
|
|
@ -56,12 +59,12 @@ jobs:
|
||||||
rm -f "$TMPRC"
|
rm -f "$TMPRC"
|
||||||
|
|
||||||
if [ "$LANDED_VERSION" != "$VERSION" ]; then
|
if [ "$LANDED_VERSION" != "$VERSION" ]; then
|
||||||
echo "::error::Readback verify FAILED — the forge registry reports version '${LANDED_VERSION:-<none>}', expected '${VERSION}'. This is a genuine publish failure, not a benign duplicate."
|
echo "::error::Readback verify FAILED — The Source registry reports version '${LANDED_VERSION:-<none>}', expected '${VERSION}'. This is a genuine publish failure, not a benign duplicate."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$PUBLISH_OK" = true ]; then
|
if [ "$PUBLISH_OK" = true ]; then
|
||||||
echo "Published and verified @soulcraft/brainy@${VERSION} on the forge registry."
|
echo "Published and verified @soulcraft/brainy@${VERSION} on The Source registry."
|
||||||
else
|
else
|
||||||
echo "::warning::npm publish reported failure, but readback confirms @soulcraft/brainy@${VERSION} is already live on the forge (a prior run or mirror landed it) — treating this run as successful, since the registry content is correct. Any OTHER failure mode would have failed the readback check above instead."
|
echo "::warning::npm publish reported failure, but readback confirms @soulcraft/brainy@${VERSION} is already live on The Source (a prior run or mirror landed it) — treating this run as successful, since the registry content is correct. Any OTHER failure mode would have failed the readback check above instead."
|
||||||
fi
|
fi
|
||||||
|
|
@ -70,8 +70,8 @@ to the caller today.
|
||||||
pre-existing meaning). **Migration-grade exports set `includeHidden: true`** — a
|
pre-existing meaning). **Migration-grade exports set `includeHidden: true`** — a
|
||||||
complete-canon export must carry every visibility tier; consumer-facing exports
|
complete-canon export must carry every visibility tier; consumer-facing exports
|
||||||
leave it off.
|
leave it off.
|
||||||
- **Ops note (consumer-invisible): the release pipeline's forge-registry publish now runs
|
- **Ops note (consumer-invisible): the release pipeline's home-registry publish (The
|
||||||
on CI**, triggered by the release tag, instead of PUTting the tarball from the laptop
|
Source, source.soulcraft.com) now runs on CI**, triggered by the release tag, instead of PUTting the tarball from the laptop
|
||||||
over WAN — no change to what gets published or how a consumer installs it.
|
over WAN — no change to what gets published or how a consumer installs it.
|
||||||
|
|
||||||
## v9.0.0 — 2026-08-04 (the field-addressing law: your names and system.*, nothing in between)
|
## v9.0.0 — 2026-08-04 (the field-addressing law: your names and system.*, nothing in between)
|
||||||
|
|
|
||||||
|
|
@ -175,78 +175,79 @@ echo -e "${BLUE}7️⃣ Creating git tag v${NEW_VERSION}...${NC}"
|
||||||
git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
|
git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
|
||||||
echo -e "${GREEN}✅ Tag created${NC}\n"
|
echo -e "${GREEN}✅ Tag created${NC}\n"
|
||||||
|
|
||||||
# Step 9: Push to origin — the forge is the one home (ruled 2026-07-23; the
|
# Step 9: Push to origin — The Source is the one home (ruled 2026-07-23; the
|
||||||
# old public GitHub repo is archived history, no longer part of any release).
|
# old public GitHub repo is archived history, no longer part of any release).
|
||||||
echo -e "${BLUE}8️⃣ Pushing to origin...${NC}"
|
echo -e "${BLUE}8️⃣ Pushing to origin...${NC}"
|
||||||
git push --follow-tags origin "$CURRENT_BRANCH"
|
git push --follow-tags origin "$CURRENT_BRANCH"
|
||||||
echo -e "${GREEN}✅ Pushed to origin${NC}\n"
|
echo -e "${GREEN}✅ Pushed to origin${NC}\n"
|
||||||
|
|
||||||
# Step 10: Forge publish is CI's job now, not the laptop's — a tag push (just
|
# Step 10: The home publish (The Source, source.soulcraft.com) is CI's job
|
||||||
# above) triggers .forgejo/workflows/publish-forge.yml, which builds and
|
# now, not the laptop's — a tag push (just above) triggers
|
||||||
# publishes on the forge's own runner (datacenter-side: seconds, not the
|
# .forgejo/workflows/publish-source.yml, which builds and publishes on The
|
||||||
# laptop's WAN timing out on an 87MB tarball PUT). The laptop holds no forge
|
# Source's own runner (datacenter-side: seconds, not the laptop's WAN timing
|
||||||
# publish credential anymore; it only waits for CI's result before trusting
|
# out on an 87MB tarball PUT). The laptop holds no home-registry publish
|
||||||
# the forge/npmjs pair enough to publish the storefront leg.
|
# credential anymore; it only waits for CI's result before trusting the
|
||||||
FORGE_NPM_REG="https://source.soulcraft.com/api/packages/soulcraft/npm/"
|
# home/npmjs pair enough to publish the storefront leg.
|
||||||
FORGE_POLL_INTERVAL_S=15
|
SOURCE_NPM_REG="https://source.soulcraft.com/api/packages/soulcraft/npm/"
|
||||||
FORGE_POLL_MAX_ATTEMPTS=200 # 200 × 15s = 50 minutes — the runner is sequential and a busy day's ci.yml
|
SOURCE_POLL_INTERVAL_S=15
|
||||||
|
SOURCE_POLL_MAX_ATTEMPTS=200 # 200 × 15s = 50 minutes — the runner is sequential and a busy day's ci.yml
|
||||||
# backlog has twice exceeded the old 20-minute window (8.10.3, 9.0.0);
|
# backlog has twice exceeded the old 20-minute window (8.10.3, 9.0.0);
|
||||||
# ci.yml no longer runs on tag pushes, but same-day branch pushes still queue ahead
|
# ci.yml no longer runs on tag pushes, but same-day branch pushes still queue ahead
|
||||||
echo -e "${BLUE}9️⃣ Waiting for CI to publish v${NEW_VERSION} to the forge registry (home)...${NC}"
|
echo -e "${BLUE}9️⃣ Waiting for CI to publish v${NEW_VERSION} to The Source registry (home)...${NC}"
|
||||||
FORGE_LANDED=false
|
SOURCE_LANDED=false
|
||||||
for ((attempt = 1; attempt <= FORGE_POLL_MAX_ATTEMPTS; attempt++)); do
|
for ((attempt = 1; attempt <= SOURCE_POLL_MAX_ATTEMPTS; attempt++)); do
|
||||||
LANDED_VERSION=$(npm view "@soulcraft/brainy@${NEW_VERSION}" version "--@soulcraft:registry=${FORGE_NPM_REG}" 2>/dev/null || echo "")
|
LANDED_VERSION=$(npm view "@soulcraft/brainy@${NEW_VERSION}" version "--@soulcraft:registry=${SOURCE_NPM_REG}" 2>/dev/null || echo "")
|
||||||
if [ "$LANDED_VERSION" = "$NEW_VERSION" ]; then
|
if [ "$LANDED_VERSION" = "$NEW_VERSION" ]; then
|
||||||
FORGE_LANDED=true
|
SOURCE_LANDED=true
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
echo -e "${YELLOW} … not yet on the forge (attempt ${attempt}/${FORGE_POLL_MAX_ATTEMPTS}); retrying in ${FORGE_POLL_INTERVAL_S}s${NC}"
|
echo -e "${YELLOW} … not yet on The Source (attempt ${attempt}/${SOURCE_POLL_MAX_ATTEMPTS}); retrying in ${SOURCE_POLL_INTERVAL_S}s${NC}"
|
||||||
sleep "$FORGE_POLL_INTERVAL_S"
|
sleep "$SOURCE_POLL_INTERVAL_S"
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$FORGE_LANDED" = true ]; then
|
if [ "$SOURCE_LANDED" = true ]; then
|
||||||
echo -e "${GREEN}✅ CI published v${NEW_VERSION} to the forge${NC}\n"
|
echo -e "${GREEN}✅ CI published v${NEW_VERSION} to The Source${NC}\n"
|
||||||
else
|
else
|
||||||
echo -e "${RED}❌ CI forge publish did not land — check the workflow run on The Source; the pair must not diverge.${NC}"
|
echo -e "${RED}❌ CI's home publish did not land — check the workflow run on The Source; the pair must not diverge.${NC}"
|
||||||
echo -e "${RED} v${NEW_VERSION} was tagged and pushed, but @soulcraft/brainy@${NEW_VERSION} never became visible on the${NC}"
|
echo -e "${RED} v${NEW_VERSION} was tagged and pushed, but @soulcraft/brainy@${NEW_VERSION} never became visible on the${NC}"
|
||||||
echo -e "${RED} forge registry after ${FORGE_POLL_MAX_ATTEMPTS} attempts, ${FORGE_POLL_INTERVAL_S}s apart. Aborting before npmjs.${NC}"
|
echo -e "${RED} Source registry after ${SOURCE_POLL_MAX_ATTEMPTS} attempts, ${SOURCE_POLL_INTERVAL_S}s apart. Aborting before npmjs.${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${BLUE}9️⃣½ Publishing to npmjs (storefront, dist-tag: ${NPM_TAG})...${NC}"
|
echo -e "${BLUE}9️⃣½ Publishing to npmjs (storefront, dist-tag: ${NPM_TAG})...${NC}"
|
||||||
# BYTE-IDENTITY LAW: the storefront republishes CI's EXACT artifact — download
|
# BYTE-IDENTITY LAW: the storefront republishes CI's EXACT artifact — download
|
||||||
# the tarball the forge serves and publish that file, never a fresh local pack
|
# the tarball The Source serves and publish that file, never a fresh local pack
|
||||||
# (a local rebuild can differ byte-wise, and the fleet verifies the pair by
|
# (a local rebuild can differ byte-wise, and the fleet verifies the pair by
|
||||||
# shasum across registries).
|
# shasum across registries).
|
||||||
STOREFRONT_TMP="$(mktemp -d)"
|
STOREFRONT_TMP="$(mktemp -d)"
|
||||||
(cd "$STOREFRONT_TMP" && npm pack "@soulcraft/brainy@${NEW_VERSION}" "--@soulcraft:registry=${FORGE_NPM_REG}" >/dev/null)
|
(cd "$STOREFRONT_TMP" && npm pack "@soulcraft/brainy@${NEW_VERSION}" "--@soulcraft:registry=${SOURCE_NPM_REG}" >/dev/null)
|
||||||
FORGE_TARBALL="$(ls "$STOREFRONT_TMP"/soulcraft-brainy-*.tgz)"
|
SOURCE_TARBALL="$(ls "$STOREFRONT_TMP"/soulcraft-brainy-*.tgz)"
|
||||||
echo -e "${BLUE} forge artifact: $(sha256sum "$FORGE_TARBALL" | cut -d' ' -f1)${NC}"
|
echo -e "${BLUE} home artifact: $(sha256sum "$SOURCE_TARBALL" | cut -d' ' -f1)${NC}"
|
||||||
npm publish "$FORGE_TARBALL" --tag "$NPM_TAG" "--@soulcraft:registry=https://registry.npmjs.org/"
|
npm publish "$SOURCE_TARBALL" --tag "$NPM_TAG" "--@soulcraft:registry=https://registry.npmjs.org/"
|
||||||
rm -rf "$STOREFRONT_TMP"
|
rm -rf "$STOREFRONT_TMP"
|
||||||
# Brainy is the only PUBLIC @soulcraft package — verify visibility after every publish.
|
# Brainy is the only PUBLIC @soulcraft package — verify visibility after every publish.
|
||||||
npm access get status @soulcraft/brainy "--@soulcraft:registry=https://registry.npmjs.org/" || true
|
npm access get status @soulcraft/brainy "--@soulcraft:registry=https://registry.npmjs.org/" || true
|
||||||
# Verify the pair is byte-identical by registry-reported shasum — divergence here
|
# Verify the pair is byte-identical by registry-reported shasum — divergence here
|
||||||
# means the storefront leg must be treated as failed, loudly.
|
# means the storefront leg must be treated as failed, loudly.
|
||||||
FORGE_SHA=$(npm view "@soulcraft/brainy@${NEW_VERSION}" dist.shasum "--@soulcraft:registry=${FORGE_NPM_REG}" 2>/dev/null || echo "forge-unavailable")
|
SOURCE_SHA=$(npm view "@soulcraft/brainy@${NEW_VERSION}" dist.shasum "--@soulcraft:registry=${SOURCE_NPM_REG}" 2>/dev/null || echo "source-unavailable")
|
||||||
NPMJS_SHA=$(npm view "@soulcraft/brainy@${NEW_VERSION}" dist.shasum "--@soulcraft:registry=https://registry.npmjs.org/" 2>/dev/null || echo "npmjs-unavailable")
|
NPMJS_SHA=$(npm view "@soulcraft/brainy@${NEW_VERSION}" dist.shasum "--@soulcraft:registry=https://registry.npmjs.org/" 2>/dev/null || echo "npmjs-unavailable")
|
||||||
if [ "$FORGE_SHA" = "$NPMJS_SHA" ]; then
|
if [ "$SOURCE_SHA" = "$NPMJS_SHA" ]; then
|
||||||
echo -e "${GREEN}✅ Published to npmjs — byte-identical pair (shasum ${NPMJS_SHA})${NC}\n"
|
echo -e "${GREEN}✅ Published to npmjs — byte-identical pair (shasum ${NPMJS_SHA})${NC}\n"
|
||||||
else
|
else
|
||||||
echo -e "${RED}❌ REGISTRY DIVERGENCE: forge shasum ${FORGE_SHA} != npmjs shasum ${NPMJS_SHA} — investigate before announcing${NC}\n"
|
echo -e "${RED}❌ REGISTRY DIVERGENCE: The Source shasum ${SOURCE_SHA} != npmjs shasum ${NPMJS_SHA} — investigate before announcing${NC}\n"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 11: Release object on the forge (presentational — the tag, CHANGELOG,
|
# Step 11: Release object on The Source (presentational — the tag, CHANGELOG,
|
||||||
# and RELEASES.md are the record; this just gives the forge UI a release page).
|
# and RELEASES.md are the record; this just gives The Source's UI a release page).
|
||||||
echo -e "${BLUE}🔟 Creating forge release...${NC}"
|
echo -e "${BLUE}🔟 Creating release page on The Source...${NC}"
|
||||||
if [ -n "${FORGEJO_RELEASE_TOKEN:-}" ]; then
|
if [ -n "${FORGEJO_RELEASE_TOKEN:-}" ]; then
|
||||||
if curl -sf -X POST "https://source.soulcraft.com/api/v1/repos/soulcraft/brainy/releases" \
|
if curl -sf -X POST "https://source.soulcraft.com/api/v1/repos/soulcraft/brainy/releases" \
|
||||||
-H "Authorization: token ${FORGEJO_RELEASE_TOKEN}" -H "Content-Type: application/json" \
|
-H "Authorization: token ${FORGEJO_RELEASE_TOKEN}" -H "Content-Type: application/json" \
|
||||||
-d "{\"tag_name\":\"v${NEW_VERSION}\",\"name\":\"v${NEW_VERSION}\",\"prerelease\":${PRERELEASE}}" >/dev/null; then
|
-d "{\"tag_name\":\"v${NEW_VERSION}\",\"name\":\"v${NEW_VERSION}\",\"prerelease\":${PRERELEASE}}" >/dev/null; then
|
||||||
echo -e "${GREEN}✅ Forge release created${NC}\n"
|
echo -e "${GREEN}✅ Release page created on The Source${NC}\n"
|
||||||
else
|
else
|
||||||
echo -e "${RED}⚠️ Forge release API call failed — tag + CHANGELOG remain the record; create the release page via the forge UI if wanted${NC}\n"
|
echo -e "${RED}⚠️ Release-page API call failed — tag + CHANGELOG remain the record; create the page via The Source's UI if wanted${NC}\n"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo -e "${RED}⚠️ FORGEJO_RELEASE_TOKEN unset — no release page created; tag + CHANGELOG remain the record${NC}\n"
|
echo -e "${RED}⚠️ FORGEJO_RELEASE_TOKEN unset — no release page created; tag + CHANGELOG remain the record${NC}\n"
|
||||||
|
|
@ -269,4 +270,4 @@ echo -e "${GREEN}🎉 Release ${NEW_VERSION} complete!${NC}"
|
||||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "📦 npm: ${BLUE}https://www.npmjs.com/package/@soulcraft/brainy/v/${NEW_VERSION}${NC}"
|
echo -e "📦 npm: ${BLUE}https://www.npmjs.com/package/@soulcraft/brainy/v/${NEW_VERSION}${NC}"
|
||||||
echo -e "🏠 Forge: ${BLUE}https://source.soulcraft.com/soulcraft/brainy/releases/tag/v${NEW_VERSION}${NC}"
|
echo -e "🏠 The Source: ${BLUE}https://source.soulcraft.com/soulcraft/brainy/releases/tag/v${NEW_VERSION}${NC}"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue