Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Pre-push hook — runs all check scripts before allowing a push.
# Install with: git config core.hooksPath .githooks

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
exec "$REPO_ROOT/scripts/runChecks.sh"
56 changes: 56 additions & 0 deletions .github/workflows/ci-global-commits-signed.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
# Fails a pull request when any of its commits is not verified by GitHub.
#
# Uses GitHub's own commit verification (keys uploaded to each author's
# account, plus GitHub's web-flow key for UI-made merge commits) rather than
# running `git verify-commit` in CI, which would need every signer's key in
# the runner's keyring. Locally, the same rule is enforced by
# scripts/checks/verifyGitLogs.sh via the pre-push hook.
#
# Make this check required on `main` in the branch-protection settings so a
# merge cannot happen while it is failing.
name: ci-global-commits-signed
on:
workflow_dispatch:

pull_request: {}

permissions:
contents: read
pull-requests: read

jobs:
verify:
name: all commits verified
runs-on: ubuntu-latest
steps:
- name: Check every commit in the pull request
if: github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
fail=0
total=0
while IFS=$'\t' read -r sha verified reason subject; do
total=$((total + 1))
if [ "$verified" = "true" ]; then
printf ' ✓ %s [%s] %s\n' "${sha:0:7}" "$reason" "$subject"
else
printf ' ✗ %s [%s] %s\n' "${sha:0:7}" "$reason" "$subject"
fail=$((fail + 1))
fi
done < <(gh api --paginate "repos/${REPO}/pulls/${PR}/commits" \
--jq '.[] | [.sha, (.commit.verification.verified|tostring), .commit.verification.reason, (.commit.message|split("\n")[0])] | @tsv')
echo
if [ "$fail" -gt 0 ]; then
echo "::error::${fail} of ${total} commit(s) in this pull request are not verified."
exit 1
fi
echo "All ${total} commit(s) are verified."

- name: Nothing to verify outside a pull request
if: github.event_name != 'pull_request'
run: echo "Manual run — this check only inspects pull request commits."
11 changes: 11 additions & 0 deletions .yamllint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/yamllint.json
extends: default

rules:
comments: disable
line-length: disable
braces: disable
brackets: disable
truthy:
check-keys: false
33 changes: 33 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Contributing

This repo only houses the organization profile README (`profile/README.md`), but it
follows the same conventions as the other AI Crafting repositories.

## Signed commits are required

Every commit must carry a valid signature. Enforced in two places:

- **Locally**, by the pre-push hook. Enable it once per clone:

```bash
git config core.hooksPath .githooks
```

The hook runs `scripts/runChecks.sh`, which runs every executable check in
`scripts/checks/`: `lint.sh` (yamllint + shellcheck) and `verifyGitLogs.sh`, which
rejects any unpushed commit whose signature is not good (`%G?` of `G` or `U`).
`U` is accepted because a good signature from a key you have not personally
trusted is still a valid signature; GitHub's own merge commits show as `U` until
you trust its web-flow key.

- **In CI**, by the `ci-global-commits-signed` workflow, which fails a pull request if
GitHub reports any of its commits as unverified. That check is required on `main`.

Run the checks on demand with:

```bash
scripts/runChecks.sh
```

Code style: tabs (width 4), trailing whitespace trimmed on save except in `.md`
files, one final newline. See `AGENTS.md`.
58 changes: 58 additions & 0 deletions scripts/checks/lint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# Local pre-push lint check — mirrors the CI lint workflow.
# Can be run from anywhere:
# lint.sh — run all checks
# lint.sh yaml — yamllint only
# lint.sh shell — shellcheck only
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$REPO_ROOT"

pass=0
fail=0
filter="${1:-all}"

run_check() {
local name="$1"
shift
printf '\033[1m▶ %s\033[0m\n' "$name"
if "$@"; then
printf '\033[0;32m ✓ %s passed\033[0m\n\n' "$name"
pass=$((pass + 1))
else
printf '\033[0;31m ✗ %s failed\033[0m\n\n' "$name"
fail=$((fail + 1))
fi
}

should_run() {
[ "$filter" = "all" ] || [ "$filter" = "$1" ]
}

# yamllint (exclude node_modules)
if should_run yaml; then
if command -v yamllint &>/dev/null; then
run_check "yamllint" bash -c \
'find . \( -name "*.yml" -o -name "*.yaml" \) -not -path "*/node_modules/*" -not -path "./external/*" | xargs yamllint'
else
printf '\033[0;31m ⚠ yamllint not installed — skipping\033[0m\n\n'
fi
fi

# ShellCheck
if should_run shell; then
if command -v shellcheck &>/dev/null; then
run_check "shellcheck" bash -c \
'find . -type f -name "*.sh" -not -path "./node_modules/*" -not -path "./external/*" -print0 | xargs -0 -r shellcheck'
else
printf '\033[0;31m ⚠ shellcheck not installed — skipping\033[0m\n\n'
fi
fi

printf '\033[1m────────────────────────────\033[0m\n'
printf '\033[0;32mPassed: %d\033[0m \033[0;31mFailed: %d\033[0m\n' "$pass" "$fail"

if [ "$fail" -gt 0 ]; then
exit 1
fi
79 changes: 79 additions & 0 deletions scripts/checks/verifyGitLogs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# Verify that recent commits carry valid signatures.
# Can be run from anywhere:
# verifyGitLogs.sh — check the unpushed commits (vs the upstream),
# or the last 10 if there are none / no upstream
# verifyGitLogs.sh N — check the most recent N commits
#
# A commit passes when `git` reports its signature as good (`%G?` = G or U,
# i.e. cryptographically valid; U = valid but the signing key isn't trusted).
# Bad, missing, expired, revoked, or unverifiable signatures fail.
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$REPO_ROOT"

green=$'\033[0;32m'
red=$'\033[0;31m'
bold=$'\033[1m'
reset=$'\033[0m'

# ---------------------------------------------------------------------------
# Determine how many commits to verify.
# ---------------------------------------------------------------------------
count="${1:-}"

if [ -z "$count" ]; then
unpushed=0
if upstream="$(git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null)"; then
unpushed="$(git rev-list --count "${upstream}..HEAD" 2>/dev/null || echo 0)"
echo "Upstream ${upstream}: ${unpushed} unpushed commit(s)."
else
echo "No upstream configured."
fi
if [ "$unpushed" -gt 0 ]; then
count="$unpushed"
else
count=10
echo "Falling back to the last ${count} commits."
fi
fi

if ! [[ "$count" =~ ^[0-9]+$ ]] || [ "$count" -eq 0 ]; then
echo "${red}error: commit count must be a positive integer (got '${count}')${reset}" >&2
exit 2
fi

# Don't ask for more commits than exist.
total="$(git rev-list --count HEAD)"
if [ "$count" -gt "$total" ]; then
count="$total"
fi

# ---------------------------------------------------------------------------
# Verify each commit's signature.
# ---------------------------------------------------------------------------
printf '%s▶ Verifying signatures on the most recent %s commit(s)%s\n' "$bold" "$count" "$reset"

fail=0
while read -r sha; do
status="$(git show --no-patch --format='%G?' "$sha")"
subject="$(git show --no-patch --format='%s' "$sha")"
short="$(git rev-parse --short "$sha")"
case "$status" in
G | U)
printf '%s ✓ %s [%s] %s%s\n' "$green" "$short" "$status" "${subject:0:60}" "$reset"
;;
*)
printf '%s ✗ %s [%s] %s%s\n' "$red" "$short" "$status" "${subject:0:60}" "$reset"
fail=$((fail + 1))
;;
esac
done < <(git rev-list -n "$count" HEAD)

echo
if [ "$fail" -gt 0 ]; then
printf '%s%d of %d commit(s) have invalid or missing signatures.%s\n' "$red" "$fail" "$count" "$reset" >&2
exit 1
fi
printf '%sAll %d commit(s) have valid signatures.%s\n' "$green" "$count" "$reset"
23 changes: 23 additions & 0 deletions scripts/runChecks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Runs all check scripts in scripts/checks/ and exits non-zero if any fail.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")/checks" && pwd)"
fail=0

for script in "$SCRIPT_DIR"/*.sh; do
[ -x "$script" ] || continue
printf '\033[1m━━━ %s ━━━\033[0m\n' "$(basename "$script")"
if "$script"; then
:
else
fail=$((fail + 1))
fi
done

if [ "$fail" -gt 0 ]; then
printf '\n\033[0;31m%d check script(s) failed.\033[0m\n' "$fail"
exit 1
fi

printf '\n\033[0;32mAll checks passed.\033[0m\n'