Skip to content

Commit 4d828e1

Browse files
Merge branch 'main' into chore/cicd-optimizations
2 parents e906e9b + e758a64 commit 4d828e1

9 files changed

Lines changed: 867 additions & 23 deletions

File tree

.github/dependabot.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ updates:
1313
directory: "/"
1414
schedule:
1515
interval: "daily"
16-
ignore:
17-
- dependency-name: "*"
18-
update-types: ["version-update:semver-patch"]
16+
open-pull-requests-limit: 0 # security PRs still flow; see rsr-template-repo@78b050e
17+
1918

2019
- package-ecosystem: "npm"
2120
directory: "/"

.github/workflows/ada.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-License-Identifier: MPL-2.0-or-later
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
22
name: Ada (GNAT)
33

44
on:
@@ -27,4 +27,4 @@ jobs:
2727
sudo apt-get install gnat gprbuild
2828
2929
- name: Build
30-
run: gprbuild -j0 -p
30+
run: gprbuild -P modshells.gpr -j0 -p
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
#
3+
# dependabot-automerge.yml — enable GitHub's native auto-merge on
4+
# Dependabot pull requests that match a declared severity / ecosystem
5+
# policy. Pairs with `.github/dependabot.yml`'s
6+
# `open-pull-requests-limit: 0` + security-only pattern (see the
7+
# cargo block there).
8+
#
9+
# What this does:
10+
# - Triggers on every Dependabot PR.
11+
# - Reads the PR's update-type metadata via the dependabot/fetch-metadata
12+
# action (no free-text parsing).
13+
# - Requires CI to be green before merge (GitHub's auto-merge enforces
14+
# required status checks).
15+
# - Gates merge behind a severity+ecosystem policy table. Default is
16+
# low+medium security updates only.
17+
#
18+
# Why auto-merge on GitHub (not via a bot like rhodibot) is the right
19+
# layer: GitHub enforces branch protection + required checks natively,
20+
# and the PR author is already `dependabot[bot]`. Rhodibot doesn't need
21+
# to know anything about ecosystems — GitHub handles the merge mechanics
22+
# once we approve.
23+
#
24+
# Threat model:
25+
# - A compromised upstream package with a bogus security advisory
26+
# could propose a malicious version bump. Mitigation: require at
27+
# least one non-automated reviewer for HIGH+CRITICAL severity
28+
# (done below — we explicitly refuse to auto-approve those).
29+
# - A compromised Dependabot itself is an Akerlof claim-grounder
30+
# problem. Not in scope here; track under
31+
# `project_claim_grounders_dual_use_akerlof.md`.
32+
#
33+
# Dogfooding: this workflow template is itself subject to the same
34+
# Dependabot config via the github-actions ecosystem block, so SHA
35+
# bumps for dependabot/fetch-metadata flow through the same path.
36+
37+
name: Dependabot Auto-Merge
38+
39+
on:
40+
pull_request:
41+
types: [opened, reopened, synchronize]
42+
43+
permissions:
44+
contents: write # needed to enable auto-merge
45+
pull-requests: write # needed to approve
46+
# NB: keep narrow — do NOT add secrets: read or id-token: write here.
47+
48+
jobs:
49+
automerge:
50+
# Only run for PRs actually authored by Dependabot.
51+
if: github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]'
52+
runs-on: ubuntu-latest
53+
54+
steps:
55+
- name: Fetch Dependabot metadata
56+
id: meta
57+
uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0
58+
with:
59+
github-token: ${{ secrets.GITHUB_TOKEN }}
60+
61+
# --- Policy gate -------------------------------------------------------
62+
# Outputs from fetch-metadata we care about:
63+
# update-type → version-update:semver-{patch,minor,major}
64+
# dependency-type → direct:{development,production} | indirect
65+
# alert-state → AUTO_DISMISSED | DISMISSED | FIXED | OPEN
66+
# ghsa-id → GHSA-... if this is a security PR
67+
# --- Policy -------------------------------------------------------------
68+
# AUTO-APPROVE + AUTO-MERGE when:
69+
# 1. This is a SECURITY update (ghsa-id present), AND
70+
# 2. Update is patch or minor, AND
71+
# 3. Severity ≤ moderate (Dependabot doesn't expose severity
72+
# directly in fetch-metadata; infer from the absence of
73+
# HIGH/CRITICAL labels added by Dependabot).
74+
# Otherwise: do nothing. Human reviews HIGH+CRITICAL security
75+
# updates and all non-security bumps.
76+
- name: Decide policy outcome
77+
id: policy
78+
env:
79+
GHSA_ID: ${{ steps.meta.outputs.ghsa-id }}
80+
UPDATE_TYPE: ${{ steps.meta.outputs.update-type }}
81+
PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
82+
run: |
83+
set -euo pipefail
84+
85+
is_security=false
86+
is_patch_or_minor=false
87+
is_high_or_critical=false
88+
89+
[ -n "$GHSA_ID" ] && is_security=true
90+
case "$UPDATE_TYPE" in
91+
version-update:semver-patch|version-update:semver-minor)
92+
is_patch_or_minor=true ;;
93+
esac
94+
95+
# Dependabot adds severity labels like "severity: high",
96+
# "severity: critical". Look for those in the PR labels JSON.
97+
if echo "$PR_LABELS" | grep -qiE '"(severity: (high|critical))"'; then
98+
is_high_or_critical=true
99+
fi
100+
101+
if $is_security && $is_patch_or_minor && ! $is_high_or_critical; then
102+
echo "action=automerge" >> "$GITHUB_OUTPUT"
103+
else
104+
echo "action=skip" >> "$GITHUB_OUTPUT"
105+
fi
106+
echo "security=$is_security" >> "$GITHUB_OUTPUT"
107+
echo "update_type=$UPDATE_TYPE" >> "$GITHUB_OUTPUT"
108+
echo "ghsa=$GHSA_ID" >> "$GITHUB_OUTPUT"
109+
110+
- name: Approve PR (if policy allows)
111+
if: steps.policy.outputs.action == 'automerge'
112+
env:
113+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114+
PR_URL: ${{ github.event.pull_request.html_url }}
115+
run: |
116+
gh pr review --approve "$PR_URL" \
117+
--body "Auto-approving Dependabot security update (${{ steps.policy.outputs.ghsa }}, ${{ steps.policy.outputs.update_type }}). Policy: low/moderate security patches/minors only."
118+
119+
- name: Enable auto-merge (if policy allows)
120+
if: steps.policy.outputs.action == 'automerge'
121+
env:
122+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
123+
PR_URL: ${{ github.event.pull_request.html_url }}
124+
run: |
125+
gh pr merge --auto --squash "$PR_URL"
126+
127+
- name: Write decision to step summary
128+
env:
129+
ACTION: ${{ steps.policy.outputs.action }}
130+
IS_SECURITY: ${{ steps.policy.outputs.security }}
131+
UPDATE_TYPE: ${{ steps.policy.outputs.update_type }}
132+
GHSA: ${{ steps.policy.outputs.ghsa }}
133+
run: |
134+
{
135+
echo "## Dependabot Auto-Merge Decision"
136+
echo ""
137+
echo "| Field | Value |"
138+
echo "|-------|-------|"
139+
echo "| Policy action | \`$ACTION\` |"
140+
echo "| Security update | \`$IS_SECURITY\` |"
141+
echo "| Update type | \`$UPDATE_TYPE\` |"
142+
echo "| GHSA ID | \`${GHSA:-n/a}\` |"
143+
} >> "$GITHUB_STEP_SUMMARY"

.github/workflows/dogfood-gate.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
3939
- name: Validate A2ML manifests
4040
if: steps.detect.outputs.count > 0
41-
uses: hyperpolymath/a2ml-validate-action@main
41+
uses: hyperpolymath/a2ml-validate-action@edad26fc392d7d9fd3d02f67ef131e26a7179a72 # main
4242
with:
4343
path: '.'
4444
strict: 'false'
@@ -86,7 +86,7 @@ jobs:
8686
8787
- name: Validate K9 contracts
8888
if: steps.detect.outputs.k9_count > 0
89-
uses: hyperpolymath/k9-validate-action@main
89+
uses: hyperpolymath/k9-validate-action@66cd8fea58e9b660260d1928bea266414b535396 # main
9090
with:
9191
path: '.'
9292
strict: 'false'

.github/workflows/hypatia-scan.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ on:
1313

1414
permissions:
1515
contents: read
16+
security-events: read
1617

1718
jobs:
1819
scan:
@@ -50,6 +51,8 @@ jobs:
5051
5152
- name: Run Hypatia scan
5253
id: scan
54+
env:
55+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5356
run: |
5457
echo "Scanning repository: ${{ github.repository }}"
5558
@@ -76,7 +79,7 @@ jobs:
7679
echo "- Medium: $MEDIUM" >> $GITHUB_STEP_SUMMARY
7780
7881
- name: Upload findings artifact
79-
uses: actions/upload-artifact@65c79d7f54e76e4e3c7a8f34db0f4ac8b515c478 # v4
82+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
8083
with:
8184
name: hypatia-findings
8285
path: hypatia-findings.json

.github/workflows/rsr-antipattern.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# SPDX-License-Identifier: MPL-2.0-or-later
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
22
# RSR Anti-Pattern CI Check
3-
# SPDX-License-Identifier: MPL-2.0-or-later
3+
# SPDX-License-Identifier: PMPL-1.0-or-later
44
#
55
# Enforces: No TypeScript, No Go, No Python (except SaltStack), No npm
66
# Allows: ReScript, Deno, WASM, Rust, OCaml, Haskell, Guile/Scheme
@@ -29,7 +29,7 @@ jobs:
2929
run: |
3030
# Exclude bindings/deno/ - those are Deno FFI files using Deno.dlopen, not plain TypeScript
3131
# Exclude .d.ts files - those are TypeScript type declarations for ReScript FFI
32-
TS_FILES=$(find . \( -name "*.ts" -o -name "*.tsx" \) | grep -v node_modules | grep -v 'bindings/deno' | grep -v '\.d\.ts$' || true)
32+
TS_FILES=$(find . \( -name "*.ts" -o -name "*.tsx" \) | grep -v node_modules | grep -v 'bindings/deno' | grep -v '\.d\.ts$' | grep -v '^./tests/' || true)
3333
if [ -n "$TS_FILES" ]; then
3434
echo "❌ TypeScript files detected - use ReScript instead"
3535
echo "$TS_FILES"

Justfile

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,34 @@ project := "modshells"
1010
default:
1111
@just --list --unsorted
1212

13-
# Build
13+
# Build the Ada executable via GNAT project file
1414
build:
15-
@echo "TODO: Add build command"
15+
gprbuild -P modshells.gpr -p
1616

17-
# Test
17+
# Build + run the Ada test suite (tests/tests.gpr → bin/test_shell_manager)
18+
# Also runs the bash smoke test if present.
1819
test:
19-
@echo "TODO: Add test command"
20+
gprbuild -P tests/tests.gpr -p
21+
./bin/test_shell_manager
22+
@if [ -x tests/smoke_test.sh ]; then tests/smoke_test.sh; fi
2023

21-
# Clean
24+
# Remove build artefacts
2225
clean:
23-
@echo "TODO: Add clean command"
26+
gprclean -P modshells.gpr
27+
@rm -rf obj bin 2>/dev/null || true
2428

25-
# Format
29+
# Format Ada sources with gnatformat when available
2630
fmt:
27-
@echo "TODO: Add format command"
28-
29-
# Lint
31+
@if command -v gnatformat >/dev/null 2>&1; then \
32+
find src -name '*.adb' -o -name '*.ads' | xargs gnatformat --inline; \
33+
else \
34+
echo "gnatformat not installed — install GNAT Pro or ALIRE toolchain"; \
35+
exit 0; \
36+
fi
37+
38+
# Lint Ada sources via -gnaty style checks (runs the compiler in check-only mode)
3039
lint:
31-
@echo "TODO: Add lint command"
40+
gprbuild -P modshells.gpr -gnaty -c -u
3241

3342
# Run panic-attacker pre-commit scan
3443
assail:

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SPDX-License-Identifier: MPL-2.0
1+
SPDX-License-Identifier: PMPL-1.0-or-later
22
SPDX-FileCopyrightText: 2024-2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
33

44
------------------------------------------------------------------------

0 commit comments

Comments
 (0)