fix: hand the proven release train on from a directory the artifact actions upload
CI / verify (pull_request) Successful in 1m4s
CI / infrastructure-smoke (pull_request) Successful in 38s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / package-release (pull_request) Successful in 32s
CI / image-build (pull_request) Successful in 22s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 1m39s
CI / package-publication (pull_request) Successful in 59s

The CI package-release gate passed, but the upload found no files: the
artifact actions leave hidden files out, and .package-release starts
with a dot. Keep the release in package-train instead, in both
workflows, and test that no artifact path is hidden.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ekklesia
2026-10-08 09:56:06 -05:00
co-authored by Claude Opus 5.5
parent d3c62e63bf
commit 1f30c86d4f
4 changed files with 22 additions and 14 deletions
+8 -7
View File
@@ -165,12 +165,12 @@ jobs:
# packages, and verifies. Nothing is published and no credential exists here. A future publish # packages, and verifies. Nothing is published and no credential exists here. A future publish
# job must depend on this job and publish only the artifacts it proved. # job must depend on this job and publish only the artifacts it proved.
- name: Build, reproduce, and consume the public package train - name: Build, reproduce, and consume the public package train
run: pnpm test:package-release --keep .package-release run: pnpm test:package-release --keep package-train
- name: Describe the proven release train - name: Describe the proven release train
id: release id: release
run: | run: |
release="$(find .package-release -mindepth 1 -maxdepth 1 -type d)" release="$(find package-train -mindepth 1 -maxdepth 1 -type d)"
manifest="$release/ekklesia-package-train.json" manifest="$release/ekklesia-package-train.json"
{ {
echo "version=$(node -p 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).version' "$manifest")" echo "version=$(node -p 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).version' "$manifest")"
@@ -179,14 +179,15 @@ jobs:
} >>"$GITHUB_OUTPUT" } >>"$GITHUB_OUTPUT"
cat "$manifest" cat "$manifest"
# The exact release artifacts, for the jobs that consume them. Version 3 of the artifact # The exact release artifacts, for the jobs that consume them, from a directory whose name does
# not start with a dot: the artifact actions leave hidden files out. Version 3 of the artifact
# actions: version 4 refuses any server that is not github.com, which this Gitea is not, while # actions: version 4 refuses any server that is not github.com, which this Gitea is not, while
# Gitea implements the protocol version 3 uses. A consumer checks the files, not the wrapper. # Gitea implements the protocol version 3 uses. A consumer checks the files, not the wrapper.
- name: Hand the release train on - name: Hand the release train on
uses: actions/upload-artifact@c6a3b2bd78b3985e4b2f15397fec357f0fd808de # v3.2.2-node20 uses: actions/upload-artifact@c6a3b2bd78b3985e4b2f15397fec357f0fd808de # v3.2.2-node20
with: with:
name: package-train name: package-train
path: .package-release/ path: package-train/
if-no-files-found: error if-no-files-found: error
retention-days: 7 retention-days: 7
@@ -228,13 +229,13 @@ jobs:
uses: actions/download-artifact@ad191675b41f6a5b46da9a048cb6893812da158b # v3.1.0-node20 uses: actions/download-artifact@ad191675b41f6a5b46da9a048cb6893812da158b # v3.1.0-node20
with: with:
name: package-train name: package-train
path: .package-release/ path: package-train/
- name: Check it is exactly that train - name: Check it is exactly that train
env: env:
EXPECTED_MANIFEST_SHA256: ${{ needs.package-release.outputs.manifest_sha256 }} EXPECTED_MANIFEST_SHA256: ${{ needs.package-release.outputs.manifest_sha256 }}
run: | run: |
release="$(find .package-release -mindepth 1 -maxdepth 1 -type d)" release="$(find package-train -mindepth 1 -maxdepth 1 -type d)"
actual="$(sha256sum "$release/ekklesia-package-train.json" | cut -d' ' -f1)" actual="$(sha256sum "$release/ekklesia-package-train.json" | cut -d' ' -f1)"
if [[ -z "$EXPECTED_MANIFEST_SHA256" || "$actual" != "$EXPECTED_MANIFEST_SHA256" ]]; then if [[ -z "$EXPECTED_MANIFEST_SHA256" || "$actual" != "$EXPECTED_MANIFEST_SHA256" ]]; then
echo "The received release manifest is not the one the package-release job proved." >&2 echo "The received release manifest is not the one the package-release job proved." >&2
@@ -243,7 +244,7 @@ jobs:
(cd "$release" && sha256sum --check --strict SHA256SUMS) (cd "$release" && sha256sum --check --strict SHA256SUMS)
- name: Publish it to a disposable Gitea, and prove the round trip and recovery - name: Publish it to a disposable Gitea, and prove the round trip and recovery
run: bash scripts/test-package-publication.sh --release "$(find .package-release -mindepth 1 -maxdepth 1 -type d)" run: bash scripts/test-package-publication.sh --release "$(find package-train -mindepth 1 -maxdepth 1 -type d)"
# Whatever happened above, this job's own stacks do not outlive it. # Whatever happened above, this job's own stacks do not outlive it.
- name: Remove this job's CI stacks - name: Remove this job's CI stacks
+7 -6
View File
@@ -59,12 +59,12 @@ jobs:
# The same credential-free gate as CI: build, reproduce, and consume the train. # The same credential-free gate as CI: build, reproduce, and consume the train.
- name: Build, reproduce, and consume the public package train - name: Build, reproduce, and consume the public package train
run: pnpm test:package-release --keep .package-release run: pnpm test:package-release --keep package-train
- name: Describe the proven release train - name: Describe the proven release train
id: release id: release
run: | run: |
release="$(find .package-release -mindepth 1 -maxdepth 1 -type d)" release="$(find package-train -mindepth 1 -maxdepth 1 -type d)"
manifest="$release/ekklesia-package-train.json" manifest="$release/ekklesia-package-train.json"
{ {
echo "version=$(node -p 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).version' "$manifest")" echo "version=$(node -p 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).version' "$manifest")"
@@ -72,12 +72,13 @@ jobs:
echo "manifest_sha256=$(sha256sum "$manifest" | cut -d' ' -f1)" echo "manifest_sha256=$(sha256sum "$manifest" | cut -d' ' -f1)"
} >>"$GITHUB_OUTPUT" } >>"$GITHUB_OUTPUT"
# Artifact actions version 3: version 4 refuses any server that is not github.com. # Artifact actions version 3: version 4 refuses any server that is not github.com. The directory
# name must not start with a dot: the artifact actions leave hidden files out.
- name: Hand the release train on - name: Hand the release train on
uses: actions/upload-artifact@c6a3b2bd78b3985e4b2f15397fec357f0fd808de # v3.2.2-node20 uses: actions/upload-artifact@c6a3b2bd78b3985e4b2f15397fec357f0fd808de # v3.2.2-node20
with: with:
name: package-train name: package-train
path: .package-release/ path: package-train/
if-no-files-found: error if-no-files-found: error
retention-days: 90 retention-days: 90
@@ -102,7 +103,7 @@ jobs:
uses: actions/download-artifact@ad191675b41f6a5b46da9a048cb6893812da158b # v3.1.0-node20 uses: actions/download-artifact@ad191675b41f6a5b46da9a048cb6893812da158b # v3.1.0-node20
with: with:
name: package-train name: package-train
path: .package-release/ path: package-train/
# The tool validates the whole train against its manifest, that manifest against the gate's, # The tool validates the whole train against its manifest, that manifest against the gate's,
# its version against the request, and its revision against this commit, before it sends # its version against the request, and its revision against this commit, before it sends
@@ -115,7 +116,7 @@ jobs:
MANIFEST_SHA256: ${{ needs.package-release.outputs.manifest_sha256 }} MANIFEST_SHA256: ${{ needs.package-release.outputs.manifest_sha256 }}
run: | run: |
node scripts/publish-package-train.ts \ node scripts/publish-package-train.ts \
--release "$(find .package-release -mindepth 1 -maxdepth 1 -type d)" \ --release "$(find package-train -mindepth 1 -maxdepth 1 -type d)" \
--registry "$EKKLESIA_PACKAGES_REGISTRY" \ --registry "$EKKLESIA_PACKAGES_REGISTRY" \
--version "$TRAIN_VERSION" \ --version "$TRAIN_VERSION" \
--revision "$TRAIN_REVISION" \ --revision "$TRAIN_REVISION" \
+2
View File
@@ -26,3 +26,5 @@ packages/module-pack/.fixtures/
# `pnpm packages:release` writes package release trains here (scripts/package-release.ts). # `pnpm packages:release` writes package release trains here (scripts/package-release.ts).
.package-release/ .package-release/
# CI keeps the proven release here to hand it on as a workflow artifact (scripts/test-package-release.sh --keep).
/package-train/
+5 -1
View File
@@ -531,12 +531,16 @@ describe("when the real registry can be written to", () => {
expect(publishRuns).not.toMatch(/pnpm|npm (?:install|ci|run|pack)|package-release\.ts/u); expect(publishRuns).not.toMatch(/pnpm|npm (?:install|ci|run|pack)|package-release\.ts/u);
}); });
it("uses the artifact actions that work on this Gitea, pinned by commit", async () => { it("uses the artifact actions that work on this Gitea, pinned by commit, on visible paths", async () => {
for (const file of ["ci.yml", "publish-packages.yml"]) { for (const file of ["ci.yml", "publish-packages.yml"]) {
const text = await readFile(path.join(repositoryRoot, ".github/workflows", file), "utf8"); const text = await readFile(path.join(repositoryRoot, ".github/workflows", file), "utf8");
for (const use of text.matchAll(/uses: (actions\/(?:up|down)load-artifact)@(\S+)/gu)) { for (const use of text.matchAll(/uses: (actions\/(?:up|down)load-artifact)@(\S+)/gu)) {
expect(use[2], `${file} ${use[1]}`).toMatch(/^[0-9a-f]{40}$/u); expect(use[2], `${file} ${use[1]}`).toMatch(/^[0-9a-f]{40}$/u);
} }
// The artifact actions leave hidden files out: a path starting with a dot uploads nothing.
for (const artifactPath of text.matchAll(/^\s+path: (\S+)$/gmu)) {
expect(artifactPath[1], file).not.toMatch(/^\.|\/\./u);
}
} }
}); });
}); });