Skip to content

Commit 9310623

Browse files
committed
ci(workflows): fix boj-build YAML + gate mirror secrets on presence
Follow-up to #360 (the remaining real workflow_audit items; owner chose 'new separate job' for boj-build and 'harden all 6' for the mirrors). boj-build.yml — was invalid YAML: the 'K9-SVC Validation' and 'Contractile Check' steps were indented at job level instead of step level, so GitHub could not load the workflow at all. Moved them into a new ungated job 'validate-contractiles' that runs on every push (the trigger-boj job is gated on BOJ_SERVER_URL, which must not gate repo self-validation). Both jobs gained timeout-minutes: 10. The completeness check was also wrong (hard-coded 'must trust dust lust adjust intend' — 'lust' does not exist, 'bust' was missing, and intend's file is Intentfile.a2ml not Intendfile); it now reads the canonical verb set from the registry (.machine_readable/contractiles/INDEX.a2ml), as INDEX itself instructs consumers to do. Verified green locally: all 6 contractiles present. mirror-reusable.yml — the six SSH-based mirror jobs (gitlab, bitbucket, codeberg, sourcehut, disroot, gitea) ran webfactory/ssh-agent with an inherited SSH key gated only on vars.<FORGE>_MIRROR_ENABLED, so an enabled-but-unconfigured repo fed ssh-agent an empty key and failed. Applied the existing Radicle pattern: gate the ssh-agent and push steps on secrets.<FORGE>_SSH_KEY != '' and add a Skipped notice step on == '' so the job ends cleanly with an actionable ::notice:: instead of erroring. https://claude.ai/code/session_01XZhw6Fq27eoeyEB4LR3a2c
1 parent 6cd3772 commit 9310623

2 files changed

Lines changed: 92 additions & 28 deletions

File tree

.github/workflows/boj-build.yml

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ permissions:
1717

1818
jobs:
1919
trigger-boj:
20+
name: Trigger BoJ server
21+
timeout-minutes: 10
2022
runs-on: ubuntu-latest
2123
if: ${{ vars.BOJ_SERVER_URL != '' || secrets.BOJ_SERVER_URL != '' }}
2224
steps:
@@ -47,31 +49,57 @@ jobs:
4749
--data "$payload" \
4850
|| echo "BoJ server unreachable - skipping (non-fatal)"
4951
50-
- name: K9-SVC Validation
51-
run: |
52-
echo "Running K9-SVC contractile validation..."
53-
if [ -f .machine_readable/contractiles/must/Mustfile.a2ml ]; then
54-
echo "✅ Mustfile found - running validation"
55-
# Placeholder for actual K9 validation
56-
echo "K9 validation would run here"
57-
else
58-
echo "❌ Mustfile not found"
59-
exit 1
60-
fi
61-
62-
- name: Contractile Check
63-
run: |
64-
echo "Checking contractile completeness..."
65-
contractiles=("must" "trust" "dust" "lust" "adjust" "intend")
66-
missing=0
67-
for c in "${contractiles[@]}"; do
68-
if [ ! -f ".machine_readable/contractiles/$c/${c^}file.a2ml" ]; then
69-
echo "❌ Missing: $c"
70-
missing=$((missing + 1))
71-
fi
72-
done
73-
if [ $missing -gt 0 ]; then
74-
echo "❌ $missing contractiles missing"
75-
exit 1
76-
fi
77-
echo "✅ All contractiles present"
52+
# Contractile validation runs on every push, independent of whether a BoJ
53+
# server is configured — it validates THIS repo's own contractile set, so it
54+
# must not sit behind the trigger-boj `if:` guard.
55+
validate-contractiles:
56+
name: K9-SVC contractile validation
57+
timeout-minutes: 10
58+
runs-on: ubuntu-latest
59+
steps:
60+
- name: Checkout
61+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
62+
63+
- name: K9-SVC Validation
64+
run: |
65+
echo "Running K9-SVC contractile validation..."
66+
if [ -f .machine_readable/contractiles/must/Mustfile.a2ml ]; then
67+
echo "✅ Mustfile found - running validation"
68+
# Placeholder for actual K9 validation
69+
echo "K9 validation would run here"
70+
else
71+
echo "❌ Mustfile not found"
72+
exit 1
73+
fi
74+
75+
- name: Contractile Check
76+
run: |
77+
set -euo pipefail
78+
echo "Checking contractile completeness..."
79+
index=".machine_readable/contractiles/INDEX.a2ml"
80+
if [ ! -f "$index" ]; then
81+
echo "❌ Contractile registry not found: $index"
82+
exit 1
83+
fi
84+
# Read the canonical verb set from the registry rather than hard-coding
85+
# it (INDEX.a2ml §Registry: consumers SHOULD discover verbs from here).
86+
# Each verb's trident lists "<verb>/<Verb>file.a2ml" as its first entry.
87+
mapfile -t files < <(grep -oE '"[a-z]+/[A-Z][a-z]+file\.a2ml"' "$index" | tr -d '"' | sort -u)
88+
if [ "${#files[@]}" -eq 0 ]; then
89+
echo "❌ No contractile files discovered in $index"
90+
exit 1
91+
fi
92+
missing=0
93+
for rel in "${files[@]}"; do
94+
if [ -f ".machine_readable/contractiles/$rel" ]; then
95+
echo "✅ $rel"
96+
else
97+
echo "❌ Missing: $rel"
98+
missing=$((missing + 1))
99+
fi
100+
done
101+
if [ "$missing" -gt 0 ]; then
102+
echo "❌ $missing contractile(s) missing"
103+
exit 1
104+
fi
105+
echo "✅ All ${#files[@]} contractiles present"

.github/workflows/mirror-reusable.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
with:
5757
fetch-depth: 0
5858
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
59+
if: ${{ secrets.GITLAB_SSH_KEY != '' }}
5960
with:
6061
ssh-private-key: ${{ secrets.GITLAB_SSH_KEY }}
6162
- name: Mirror to GitLab
@@ -65,10 +66,15 @@ jobs:
6566
# for Maintainers/Developers or remove the protection on the mirror repo.
6667
# Until then this step is advisory-only; failures do not red main.
6768
continue-on-error: true
69+
if: ${{ secrets.GITLAB_SSH_KEY != '' }}
6870
run: |
6971
ssh-keyscan -t ed25519 gitlab.com >> ~/.ssh/known_hosts
7072
git remote add gitlab git@gitlab.com:hyperpolymath/${{ github.event.repository.name }}.git || true
7173
git push --force gitlab main
74+
- name: Skipped (GITLAB_SSH_KEY not configured)
75+
if: ${{ secrets.GITLAB_SSH_KEY == '' }}
76+
run: |
77+
echo "::notice::GITLAB_MIRROR_ENABLED=true but secrets.GITLAB_SSH_KEY is empty. Skipping GitLab mirror. Configure the GITLAB_SSH_KEY org/repo secret to enable."
7278
7379
mirror-bitbucket:
7480
timeout-minutes: 20
@@ -79,13 +85,19 @@ jobs:
7985
with:
8086
fetch-depth: 0
8187
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
88+
if: ${{ secrets.BITBUCKET_SSH_KEY != '' }}
8289
with:
8390
ssh-private-key: ${{ secrets.BITBUCKET_SSH_KEY }}
8491
- name: Mirror to Bitbucket
92+
if: ${{ secrets.BITBUCKET_SSH_KEY != '' }}
8593
run: |
8694
ssh-keyscan -t ed25519 bitbucket.org >> ~/.ssh/known_hosts
8795
git remote add bitbucket git@bitbucket.org:hyperpolymath/${{ github.event.repository.name }}.git || true
8896
git push --force bitbucket main
97+
- name: Skipped (BITBUCKET_SSH_KEY not configured)
98+
if: ${{ secrets.BITBUCKET_SSH_KEY == '' }}
99+
run: |
100+
echo "::notice::BITBUCKET_MIRROR_ENABLED=true but secrets.BITBUCKET_SSH_KEY is empty. Skipping Bitbucket mirror. Configure the BITBUCKET_SSH_KEY org/repo secret to enable."
89101
90102
mirror-codeberg:
91103
timeout-minutes: 20
@@ -96,13 +108,19 @@ jobs:
96108
with:
97109
fetch-depth: 0
98110
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
111+
if: ${{ secrets.CODEBERG_SSH_KEY != '' }}
99112
with:
100113
ssh-private-key: ${{ secrets.CODEBERG_SSH_KEY }}
101114
- name: Mirror to Codeberg
115+
if: ${{ secrets.CODEBERG_SSH_KEY != '' }}
102116
run: |
103117
ssh-keyscan -t ed25519 codeberg.org >> ~/.ssh/known_hosts
104118
git remote add codeberg git@codeberg.org:hyperpolymath/${{ github.event.repository.name }}.git || true
105119
git push --force codeberg main
120+
- name: Skipped (CODEBERG_SSH_KEY not configured)
121+
if: ${{ secrets.CODEBERG_SSH_KEY == '' }}
122+
run: |
123+
echo "::notice::CODEBERG_MIRROR_ENABLED=true but secrets.CODEBERG_SSH_KEY is empty. Skipping Codeberg mirror. Configure the CODEBERG_SSH_KEY org/repo secret to enable."
106124
107125
mirror-sourcehut:
108126
timeout-minutes: 20
@@ -113,13 +131,19 @@ jobs:
113131
with:
114132
fetch-depth: 0
115133
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
134+
if: ${{ secrets.SOURCEHUT_SSH_KEY != '' }}
116135
with:
117136
ssh-private-key: ${{ secrets.SOURCEHUT_SSH_KEY }}
118137
- name: Mirror to SourceHut
138+
if: ${{ secrets.SOURCEHUT_SSH_KEY != '' }}
119139
run: |
120140
ssh-keyscan -t ed25519 git.sr.ht >> ~/.ssh/known_hosts
121141
git remote add sourcehut git@git.sr.ht:~hyperpolymath/${{ github.event.repository.name }} || true
122142
git push --force sourcehut main
143+
- name: Skipped (SOURCEHUT_SSH_KEY not configured)
144+
if: ${{ secrets.SOURCEHUT_SSH_KEY == '' }}
145+
run: |
146+
echo "::notice::SOURCEHUT_MIRROR_ENABLED=true but secrets.SOURCEHUT_SSH_KEY is empty. Skipping SourceHut mirror. Configure the SOURCEHUT_SSH_KEY org/repo secret to enable."
123147
124148
mirror-disroot:
125149
timeout-minutes: 20
@@ -130,13 +154,19 @@ jobs:
130154
with:
131155
fetch-depth: 0
132156
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
157+
if: ${{ secrets.DISROOT_SSH_KEY != '' }}
133158
with:
134159
ssh-private-key: ${{ secrets.DISROOT_SSH_KEY }}
135160
- name: Mirror to Disroot
161+
if: ${{ secrets.DISROOT_SSH_KEY != '' }}
136162
run: |
137163
ssh-keyscan -t ed25519 git.disroot.org >> ~/.ssh/known_hosts
138164
git remote add disroot git@git.disroot.org:hyperpolymath/${{ github.event.repository.name }}.git || true
139165
git push --force disroot main
166+
- name: Skipped (DISROOT_SSH_KEY not configured)
167+
if: ${{ secrets.DISROOT_SSH_KEY == '' }}
168+
run: |
169+
echo "::notice::DISROOT_MIRROR_ENABLED=true but secrets.DISROOT_SSH_KEY is empty. Skipping Disroot mirror. Configure the DISROOT_SSH_KEY org/repo secret to enable."
140170
141171
mirror-gitea:
142172
timeout-minutes: 20
@@ -147,13 +177,19 @@ jobs:
147177
with:
148178
fetch-depth: 0
149179
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
180+
if: ${{ secrets.GITEA_SSH_KEY != '' }}
150181
with:
151182
ssh-private-key: ${{ secrets.GITEA_SSH_KEY }}
152183
- name: Mirror to Gitea
184+
if: ${{ secrets.GITEA_SSH_KEY != '' }}
153185
run: |
154186
ssh-keyscan -t ed25519 ${{ vars.GITEA_HOST }} >> ~/.ssh/known_hosts
155187
git remote add gitea git@${{ vars.GITEA_HOST }}:hyperpolymath/${{ github.event.repository.name }}.git || true
156188
git push --force gitea main
189+
- name: Skipped (GITEA_SSH_KEY not configured)
190+
if: ${{ secrets.GITEA_SSH_KEY == '' }}
191+
run: |
192+
echo "::notice::GITEA_MIRROR_ENABLED=true but secrets.GITEA_SSH_KEY is empty. Skipping Gitea mirror. Configure the GITEA_SSH_KEY org/repo secret to enable."
157193
158194
mirror-radicle:
159195
timeout-minutes: 20

0 commit comments

Comments
 (0)