Skip to content

Commit 290a0dc

Browse files
Jonathan D.A. Jewellclaude
andcommitted
chore: add RSR enforcement workflows
Added standard enforcement workflows: - CodeQL security scanning - OSSF Scorecard - Code quality checks - Mirror to forges - Guix/Nix policy - Security policy - Well-known standards - Workflow linter - Secret scanner - RSR anti-pattern check - npm/bun blocker - TypeScript blocker 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent ed73a90 commit 290a0dc

File tree

12 files changed

+740
-67
lines changed

12 files changed

+740
-67
lines changed

.github/workflows/codeql.yml

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,40 @@
11
# SPDX-License-Identifier: AGPL-3.0-or-later
2-
name: CodeQL
2+
name: CodeQL Security Analysis
3+
34
on:
45
push:
5-
branches: [main]
6+
branches: [main, master]
67
pull_request:
7-
branches: [main]
8+
branches: [main, master]
89
schedule:
9-
- cron: "0 6 * * 1"
10+
- cron: '0 6 * * 1'
11+
1012
permissions: read-all
13+
1114
jobs:
1215
analyze:
13-
name: Analyze
1416
runs-on: ubuntu-latest
1517
permissions:
16-
actions: read
1718
contents: read
1819
security-events: write
1920
strategy:
2021
fail-fast: false
2122
matrix:
22-
language: [actions]
23+
include:
24+
- language: actions
25+
build-mode: none
26+
2327
steps:
2428
- name: Checkout
25-
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
29+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
30+
2631
- name: Initialize CodeQL
27-
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3
32+
uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.28.1
2833
with:
2934
languages: ${{ matrix.language }}
30-
- name: Autobuild
31-
uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3
35+
build-mode: ${{ matrix.build-mode }}
36+
3237
- name: Perform CodeQL Analysis
33-
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3
38+
uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.28.1
3439
with:
3540
category: "/language:${{ matrix.language }}"
36-
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: Guix/Nix Package Policy
3+
on: [push, pull_request]
4+
5+
permissions: read-all
6+
7+
jobs:
8+
check:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
steps:
13+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
14+
- name: Enforce Guix primary / Nix fallback
15+
run: |
16+
# Check for package manager files
17+
HAS_GUIX=$(find . -name "*.scm" -o -name ".guix-channel" -o -name "guix.scm" 2>/dev/null | head -1)
18+
HAS_NIX=$(find . -name "*.nix" 2>/dev/null | head -1)
19+
20+
# Block new package-lock.json, yarn.lock, Gemfile.lock, etc.
21+
NEW_LOCKS=$(git diff --name-only --diff-filter=A HEAD~1 2>/dev/null | grep -E 'package-lock\.json|yarn\.lock|Gemfile\.lock|Pipfile\.lock|poetry\.lock|cargo\.lock' || true)
22+
if [ -n "$NEW_LOCKS" ]; then
23+
echo "⚠️ Lock files detected. Prefer Guix manifests for reproducibility."
24+
fi
25+
26+
# Prefer Guix, fallback to Nix
27+
if [ -n "$HAS_GUIX" ]; then
28+
echo "✅ Guix package management detected (primary)"
29+
elif [ -n "$HAS_NIX" ]; then
30+
echo "✅ Nix package management detected (fallback)"
31+
else
32+
echo "ℹ️ Consider adding guix.scm or flake.nix for reproducible builds"
33+
fi
34+
35+
echo "✅ Package policy check passed"

.github/workflows/mirror.yml

Lines changed: 100 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,144 @@
11
# SPDX-License-Identifier: AGPL-3.0-or-later
2-
name: Mirror to GitLab/Codeberg/Bitbucket
2+
# SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
3+
name: Mirror to Git Forges
34

45
on:
56
push:
6-
branches: [main, master]
7+
branches: [main]
78
workflow_dispatch:
89

9-
# Prevent concurrent mirror operations to avoid race conditions
10-
concurrency:
11-
group: mirror-${{ github.ref }}
12-
cancel-in-progress: false
13-
14-
permissions:
15-
contents: read
16-
17-
env:
18-
# SSH known hosts for security - prevents MITM attacks
19-
# These are the official SSH host keys for each platform
20-
GITLAB_HOST_KEY: "gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf"
21-
CODEBERG_HOST_KEY: "codeberg.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIVIC02vnjFyL+I4RHfvIGNtOgJMe769VTF1VR4EB3ZB"
22-
BITBUCKET_HOST_KEY: "bitbucket.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIazEu89wgQZ4bqs3d63QSMzYVa0MuJ2e2gKTKqu+UUO"
10+
permissions: read-all
2311

2412
jobs:
2513
mirror-gitlab:
2614
runs-on: ubuntu-latest
27-
timeout-minutes: 10
2815
if: vars.GITLAB_MIRROR_ENABLED == 'true'
2916
steps:
30-
- name: Add GitLab to known hosts
31-
run: |
32-
mkdir -p ~/.ssh
33-
echo "${{ env.GITLAB_HOST_KEY }}" >> ~/.ssh/known_hosts
34-
chmod 600 ~/.ssh/known_hosts
35-
36-
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
17+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
3718
with:
3819
fetch-depth: 0
3920

40-
- uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
21+
- uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 # v0.9.0
4122
with:
4223
ssh-private-key: ${{ secrets.GITLAB_SSH_KEY }}
4324

4425
- name: Mirror to GitLab
4526
run: |
46-
git remote add gitlab "git@gitlab.com:${{ github.repository_owner }}/${GITHUB_REPOSITORY#*/}.git" || true
47-
git push gitlab --all --force
48-
git push gitlab --tags --force
27+
ssh-keyscan -t ed25519 gitlab.com >> ~/.ssh/known_hosts
28+
git remote add gitlab [email protected]:hyperpolymath/${{ github.event.repository.name }}.git || true
29+
git push --force gitlab main
4930
50-
mirror-codeberg:
31+
mirror-bitbucket:
5132
runs-on: ubuntu-latest
52-
timeout-minutes: 10
53-
if: vars.CODEBERG_MIRROR_ENABLED == 'true'
33+
if: vars.BITBUCKET_MIRROR_ENABLED == 'true'
5434
steps:
55-
- name: Add Codeberg to known hosts
35+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
36+
with:
37+
fetch-depth: 0
38+
39+
- uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 # v0.9.0
40+
with:
41+
ssh-private-key: ${{ secrets.BITBUCKET_SSH_KEY }}
42+
43+
- name: Mirror to Bitbucket
5644
run: |
57-
mkdir -p ~/.ssh
58-
echo "${{ env.CODEBERG_HOST_KEY }}" >> ~/.ssh/known_hosts
59-
chmod 600 ~/.ssh/known_hosts
45+
ssh-keyscan -t ed25519 bitbucket.org >> ~/.ssh/known_hosts
46+
git remote add bitbucket [email protected]:hyperpolymath/${{ github.event.repository.name }}.git || true
47+
git push --force bitbucket main
6048
61-
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
49+
mirror-codeberg:
50+
runs-on: ubuntu-latest
51+
if: vars.CODEBERG_MIRROR_ENABLED == 'true'
52+
steps:
53+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
6254
with:
6355
fetch-depth: 0
6456

65-
- uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
57+
- uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 # v0.9.0
6658
with:
6759
ssh-private-key: ${{ secrets.CODEBERG_SSH_KEY }}
6860

6961
- name: Mirror to Codeberg
7062
run: |
71-
git remote add codeberg "git@codeberg.org:${{ github.repository_owner }}/${GITHUB_REPOSITORY#*/}.git" || true
72-
git push codeberg --all --force
73-
git push codeberg --tags --force
63+
ssh-keyscan -t ed25519 codeberg.org >> ~/.ssh/known_hosts
64+
git remote add codeberg [email protected]:hyperpolymath/${{ github.event.repository.name }}.git || true
65+
git push --force codeberg main
7466
75-
mirror-bitbucket:
67+
mirror-sourcehut:
7668
runs-on: ubuntu-latest
77-
timeout-minutes: 10
78-
if: vars.BITBUCKET_MIRROR_ENABLED == 'true'
69+
if: vars.SOURCEHUT_MIRROR_ENABLED == 'true'
70+
steps:
71+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
72+
with:
73+
fetch-depth: 0
74+
75+
- uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 # v0.9.0
76+
with:
77+
ssh-private-key: ${{ secrets.SOURCEHUT_SSH_KEY }}
78+
79+
- name: Mirror to SourceHut
80+
run: |
81+
ssh-keyscan -t ed25519 git.sr.ht >> ~/.ssh/known_hosts
82+
git remote add sourcehut [email protected]:~hyperpolymath/${{ github.event.repository.name }} || true
83+
git push --force sourcehut main
84+
85+
mirror-disroot:
86+
runs-on: ubuntu-latest
87+
if: vars.DISROOT_MIRROR_ENABLED == 'true'
7988
steps:
80-
- name: Add Bitbucket to known hosts
89+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
90+
with:
91+
fetch-depth: 0
92+
93+
- uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 # v0.9.0
94+
with:
95+
ssh-private-key: ${{ secrets.DISROOT_SSH_KEY }}
96+
97+
- name: Mirror to Disroot
8198
run: |
82-
mkdir -p ~/.ssh
83-
echo "${{ env.BITBUCKET_HOST_KEY }}" >> ~/.ssh/known_hosts
84-
chmod 600 ~/.ssh/known_hosts
99+
ssh-keyscan -t ed25519 git.disroot.org >> ~/.ssh/known_hosts
100+
git remote add disroot [email protected]:hyperpolymath/${{ github.event.repository.name }}.git || true
101+
git push --force disroot main
85102
86-
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
103+
mirror-gitea:
104+
runs-on: ubuntu-latest
105+
if: vars.GITEA_MIRROR_ENABLED == 'true'
106+
steps:
107+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
87108
with:
88109
fetch-depth: 0
89110

90-
- uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
111+
- uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 # v0.9.0
91112
with:
92-
ssh-private-key: ${{ secrets.BITBUCKET_SSH_KEY }}
113+
ssh-private-key: ${{ secrets.GITEA_SSH_KEY }}
93114

94-
- name: Mirror to Bitbucket
115+
- name: Mirror to Gitea
116+
run: |
117+
ssh-keyscan -t ed25519 ${{ vars.GITEA_HOST }} >> ~/.ssh/known_hosts
118+
git remote add gitea git@${{ vars.GITEA_HOST }}:hyperpolymath/${{ github.event.repository.name }}.git || true
119+
git push --force gitea main
120+
121+
mirror-radicle:
122+
runs-on: ubuntu-latest
123+
if: vars.RADICLE_MIRROR_ENABLED == 'true'
124+
steps:
125+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
126+
with:
127+
fetch-depth: 0
128+
129+
- name: Setup Rust
130+
uses: dtolnay/rust-toolchain@56f84321dbccf38fb67ce29ab63e4754056677e0 # stable
131+
with:
132+
toolchain: stable
133+
134+
- name: Install Radicle
135+
run: |
136+
# Install via cargo (safer than curl|sh)
137+
cargo install radicle-cli --locked
138+
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
139+
140+
- name: Mirror to Radicle
95141
run: |
96-
git remote add bitbucket "[email protected]:${{ github.repository_owner }}/${GITHUB_REPOSITORY#*/}.git" || true
97-
git push bitbucket --all --force
98-
git push bitbucket --tags --force
142+
echo "${{ secrets.RADICLE_KEY }}" > ~/.radicle/keys/radicle
143+
chmod 600 ~/.radicle/keys/radicle
144+
rad sync --announce || echo "Radicle sync attempted"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: NPM/Bun Blocker
3+
on: [push, pull_request]
4+
5+
permissions: read-all
6+
7+
jobs:
8+
check:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
steps:
13+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
14+
- name: Block npm/bun
15+
run: |
16+
if [ -f "package-lock.json" ] || [ -f "bun.lockb" ] || [ -f ".npmrc" ]; then
17+
echo "❌ npm/bun artifacts detected. Use Deno instead."
18+
exit 1
19+
fi
20+
echo "✅ No npm/bun violations"

.github/workflows/quality.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: Code Quality
3+
on: [push, pull_request]
4+
5+
6+
permissions: read-all
7+
8+
jobs:
9+
lint:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
steps:
14+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
15+
16+
- name: Check file permissions
17+
run: |
18+
find . -type f -perm /111 -name "*.sh" | head -10 || true
19+
20+
- name: Check for secrets
21+
uses: trufflesecurity/trufflehog@05cccb53bc9e13bc6d17997db5a6bcc3df44bf2f # v3.92.3
22+
with:
23+
path: ./
24+
base: ${{ github.event.pull_request.base.sha || github.event.before }}
25+
head: ${{ github.sha }}
26+
continue-on-error: true
27+
28+
- name: Check TODO/FIXME
29+
run: |
30+
echo "=== TODOs ==="
31+
grep -rn "TODO\|FIXME\|HACK\|XXX" --include="*.rs" --include="*.res" --include="*.py" --include="*.ex" . | head -20 || echo "None found"
32+
33+
- name: Check for large files
34+
run: |
35+
find . -type f -size +1M -not -path "./.git/*" | head -10 || echo "No large files"
36+
37+
- name: EditorConfig check
38+
uses: editorconfig-checker/action-editorconfig-checker@8c9b118d446fce7e6410b6c0a3ce2f83bd04e97a # v2.1.0
39+
continue-on-error: true
40+
41+
docs:
42+
runs-on: ubuntu-latest
43+
permissions:
44+
contents: read
45+
steps:
46+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
47+
- name: Check documentation
48+
run: |
49+
MISSING=""
50+
[ ! -f "README.md" ] && [ ! -f "README.adoc" ] && MISSING="$MISSING README"
51+
[ ! -f "LICENSE" ] && [ ! -f "LICENSE.txt" ] && [ ! -f "LICENSE.md" ] && MISSING="$MISSING LICENSE"
52+
[ ! -f "CONTRIBUTING.md" ] && [ ! -f "CONTRIBUTING.adoc" ] && MISSING="$MISSING CONTRIBUTING"
53+
54+
if [ -n "$MISSING" ]; then
55+
echo "::warning::Missing docs:$MISSING"
56+
else
57+
echo "✅ Core documentation present"
58+
fi

0 commit comments

Comments
 (0)