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
31 changes: 30 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ Closes #
## Checklist

<!-- All boxes must be ticked before a maintainer will merge. These mirror
the requirements in CONTRIBUTING.md — they are not suggestions. -->
the requirements in CONTRIBUTING.md and NA-03 — they are not suggestions. -->

### Process

- [ ] My PR **title** follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
- [ ] All of my commits are **signed** and show "Verified" on GitHub
Expand All @@ -72,6 +74,33 @@ Closes #
- [ ] If this is a breaking change, I have marked the box above and
included a `BREAKING CHANGE:` footer in at least one commit.

### NA-03 merge blockers

<!-- These checks mirror the Quick Reference in NA-03 §§ 3–7.
Tick only the boxes that are relevant to this PR. If none apply,
check "N/A" and leave the others blank. -->

- [ ] **N/A** — this change does not touch schema, APIs, dependencies,
cryptography, data placement, or smart-contract counts.
_(If ticked, skip the remaining boxes in this section.)_
- [ ] **Secret hygiene** — I confirmed no API keys, tokens, `.env` contents,
private keys, or credentials appear anywhere in the diff.
- [ ] **Prohibited dependencies** — this PR introduces no Flutter,
Couchbase, or CouchDB-as-datastore dependency, and contains no
reference to a fixed 10-billion MXT supply cap.
- [ ] **Schema.org compliance** — new database tables, columns, or API fields
map to Schema.org types, or the PR description justifies any deviation.
- [ ] **Locked counts respected** — no change to the platform's locked counts
(17 mini-apps · 7 Enterprise products · 7 data layers · 7 covenants ·
40 interest categories · 12 manifesto sections · 3 sources of truth)
without Founder approval documented in the PR description.
- [ ] **Frontier defaults** — for user-facing or infrastructure work:
offline / local-first behaviour has been considered; any new
cryptographic primitive has a documented post-quantum migration path;
edge-native placement has been addressed.
- [ ] **New GitHub Actions** are pinned to a 40-character commit SHA
(not a floating tag) per NA-03 §7.1.1.

## Screenshots / recordings

<!-- Optional. Drag-and-drop images or .mov/.mp4 files here. -->
Expand Down
147 changes: 147 additions & 0 deletions .github/workflows/reusable-ci-solidity.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Reusable workflow: Solidity smart-contract CI (Foundry).
#
# Call from a caller workflow in a consuming repo:
#
# jobs:
# ci:
# uses: nyuchi/.github/.github/workflows/reusable-ci-solidity.yml@main
# with:
# foundry-version: stable
#
# The caller must grant `contents: read`.
#
# Assumes:
# - A foundry.toml at the repo root.
# - Contracts under src/ (default Foundry layout).
# - Tests under test/ (default Foundry layout).
# - forge, cast, anvil available after installing the toolchain.
#
# Per NA-03 §6.1: Solidity, targeting Polygon, with Foundry for
# development and testing.
# Per NA-03 §10.4: comprehensive test coverage is required before
# any mainnet deployment; the coverage job surfaces the report.

name: Reusable / CI / Solidity (Foundry)

on:
workflow_call:
inputs:
foundry-version:
description: |
Foundry toolchain channel to install. Use `stable` for reproducible CI
and `nightly` only for repos that deliberately track nightly features.
type: string
default: stable

permissions:
contents: read

jobs:
changes:
name: detect changes
runs-on: ubuntu-latest
outputs:
solidity: ${{ steps.filter.outputs.solidity }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- id: filter
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4
with:
filters: |
solidity:
- '**/*.sol'
- 'foundry.toml'
- 'remappings.txt'
- 'lib/**'
- '.github/workflows/**'

fmt:
name: forge fmt
needs: changes
if: needs.changes.outputs.solidity == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
submodules: recursive
# TODO(supply-chain): replace with SHA pin before merging to main.
# Run: gh api repos/foundry-rs/foundry-toolchain/git/refs/tags/v1 --jq .object.sha
- uses: foundry-rs/foundry-toolchain@v1
with:
version: ${{ inputs.foundry-version }}
- run: forge fmt --check

build:
name: forge build
needs: changes
if: needs.changes.outputs.solidity == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
submodules: recursive
# TODO(supply-chain): replace with SHA pin before merging to main.
- uses: foundry-rs/foundry-toolchain@v1
with:
version: ${{ inputs.foundry-version }}
- name: forge build
run: forge build --sizes

test:
name: forge test
needs: changes
if: needs.changes.outputs.solidity == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
submodules: recursive
# TODO(supply-chain): replace with SHA pin before merging to main.
- uses: foundry-rs/foundry-toolchain@v1
with:
version: ${{ inputs.foundry-version }}
- name: forge test
run: forge test -vvv

coverage:
name: forge coverage
needs: changes
if: needs.changes.outputs.solidity == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
submodules: recursive
# TODO(supply-chain): replace with SHA pin before merging to main.
- uses: foundry-rs/foundry-toolchain@v1
with:
version: ${{ inputs.foundry-version }}
- name: forge coverage (lcov)
run: forge coverage --report lcov
- name: Enforce minimum line coverage (60%)
shell: bash
run: |
set -euo pipefail
if [ ! -f lcov.info ]; then
echo "::error::lcov.info not found — forge coverage did not produce expected output."
exit 1
fi
total_lines=0
hit_lines=0
while IFS= read -r line; do
case "$line" in
LF:*) total_lines=$((total_lines + ${line#LF:})) ;;
LH:*) hit_lines=$((hit_lines + ${line#LH:})) ;;
esac
done < lcov.info
if [ "$total_lines" -eq 0 ]; then
echo "::warning::No instrumented lines found in lcov.info — is the test suite empty?"
exit 0
fi
# Compute coverage × 100 to avoid fractional shell arithmetic.
pct=$(( hit_lines * 100 / total_lines ))
echo "Line coverage: ${pct}% (${hit_lines}/${total_lines})"
if [ "$pct" -lt 60 ]; then
echo "::error::Line coverage ${pct}% is below the 60% floor required by NA-03 §6.3."
exit 1
fi
2 changes: 1 addition & 1 deletion .github/workflows/reusable-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ on:
default: ".github/workflows/*.yml"
prettier-version:
type: string
default: "3.3.3"
default: "3.5.3"
yamllint-version:
type: string
default: "1.35.1"
Expand Down
94 changes: 94 additions & 0 deletions .github/workflows/reusable-openssf-scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Reusable workflow: OpenSSF Scorecard.
#
# Runs the OpenSSF Scorecard tool, which checks a repository against
# supply-chain security best practices and publishes a public score.
# Results are uploaded to GitHub's Security tab as a SARIF file.
#
# Scorecard checks include: branch protection, CI/CD, code review,
# dependency version pinning, SAST, signed releases, token permissions,
# vulnerability reporting, and more.
#
# Call from a caller workflow in a consuming repo:
#
# name: Scorecard
# on:
# branch_protection_rule:
# schedule:
# - cron: '30 1 * * 1' # Weekly on Monday
# push:
# branches: [main]
#
# jobs:
# scorecard:
# uses: nyuchi/.github/.github/workflows/reusable-openssf-scorecard.yml@main
# with:
# publish-results: true
# permissions:
# security-events: write
# id-token: write
# contents: read
# actions: read
#
# The caller must grant:
# security-events: write (upload SARIF to GitHub Security tab)
# id-token: write (publish results to the OpenSSF API)
# contents: read
# actions: read (inspect workflow permissions)
#
# Note: `publish-results: true` requires the repository to be PUBLIC.
# For private repos set `publish-results: false`.

name: Reusable / OpenSSF Scorecard

on:
workflow_call:
inputs:
publish-results:
description: |
Publish results to the OpenSSF REST API so the public score badge
reflects the current state. Set to false for private repos.
type: boolean
default: false

permissions:
contents: read

jobs:
scorecard:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
security-events: write
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false

# TODO(supply-chain): replace with SHA pin before merging to main.
# Run: gh api repos/ossf/scorecard-action/git/refs/tags/v2 --jq '.object.sha'
# Then pin to the specific release SHA, e.g.:
# ossf/scorecard-action@<40-char-sha> # v2.x.x
- name: Run OpenSSF Scorecard
uses: ossf/scorecard-action@v2
with:
results_file: scorecard-results.sarif
results_format: sarif
publish_results: ${{ inputs.publish-results }}

# TODO(supply-chain): replace with SHA pin before merging to main.
# Run: gh api repos/actions/upload-artifact/git/refs/tags/v4 --jq '.object.sha'
- name: Upload SARIF artifact
uses: actions/upload-artifact@v4
with:
name: scorecard-results
path: scorecard-results.sarif
retention-days: 5

- name: Upload SARIF to GitHub Security tab
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
with:
sarif_file: scorecard-results.sarif
category: ossf-scorecard
4 changes: 3 additions & 1 deletion ORG_SETTINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ At **Settings → Actions → General**:
Swatinem/rust-cache@*,
dtolnay/rust-toolchain@*,
taiki-e/install-action@*,
astral-sh/setup-uv@*
astral-sh/setup-uv@*,
ossf/scorecard-action@*,
foundry-rs/foundry-toolchain@*
```

To audit drift against what's actually referenced in the
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ reusable by commit SHA rather than `@main`.
| `.github/workflows/reusable-lint.yml` | **Strict org-wide lint.** Five blocking jobs: actionlint, JSON validity, prettier, markdownlint, yamllint. No auto-fix. Every repo should call this. | ✅ |
| `.github/workflows/reusable-sbom.yml` | CycloneDX SBOM generation (anchore/sbom-action). Attaches SBOM to releases. Required by NA-03 §7.2. | ✅ |
| `.github/workflows/reusable-release.yml` | Standard release flow: semver tag validation, SBOM attachment, GitHub Release creation with auto-generated notes. Covers NA-03 §8.2. | ✅ |
| `.github/workflows/reusable-ci-solidity.yml` | Foundry CI for smart contracts. Jobs: `forge fmt`, `forge build`, `forge test`, `forge coverage` (≥60% line coverage). NA-03 §6.1, §10.4. | ✅ |
| `.github/workflows/reusable-openssf-scorecard.yml` | OpenSSF Scorecard analysis. Runs supply-chain security checks and uploads SARIF results to the GitHub Security tab. Inputs: `publish-results`. | ✅ |

Legend: ✅ shipped · ⏳ planned

Expand Down
Loading