Skip to content

Commit 616d0b4

Browse files
committed
feat: bootstrap reusable CI (Py/TS) with SBOM & signing hooks
0 parents  commit 616d0b4

File tree

4,427 files changed

+1376228
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

4,427 files changed

+1376228
-0
lines changed

.github/workflows/build.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: CI / build
2+
on:
3+
push:
4+
branches: ["main"]
5+
pull_request:
6+
types: [opened, reopened, synchronize]
7+
branches: ["main"]
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
python:
16+
name: build (py)
17+
if: ${{ !github.event.pull_request.draft }}
18+
uses: ./.github/workflows/py-ci.yml
19+
with:
20+
python-versions: '["3.11","3.12"]'
21+
cov-min: 100
22+
node:
23+
name: build (node)
24+
if: ${{ !github.event.pull_request.draft }}
25+
uses: ./.github/workflows/ts-ci.yml
26+
with:
27+
node-version: "20"

.github/workflows/ci.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: CI / build
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repo
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: "3.12"
21+
22+
- name: Install Poetry
23+
run: pipx install poetry
24+
25+
- name: Install dependencies
26+
run: poetry install
27+
28+
- name: Run linters & type checks
29+
run: |
30+
poetry run ruff check .
31+
poetry run black . --check
32+
poetry run mypy .
33+
34+
- name: Run tests
35+
run: poetry run pytest -q

.github/workflows/codeql.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: CodeQL Analyze
2+
on:
3+
push:
4+
branches: ["main"]
5+
pull_request:
6+
branches: ["main"]
7+
schedule:
8+
- cron: "23 5 * * 1"
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
security-events: write
14+
15+
jobs:
16+
analyze:
17+
name: codeql
18+
runs-on: ubuntu-latest
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
language: ["javascript", "python"]
23+
steps:
24+
- uses: actions/checkout@v4
25+
- uses: github/codeql-action/init@v3
26+
with:
27+
languages: ${{ matrix.language }}
28+
- uses: github/codeql-action/autobuild@v3
29+
- uses: github/codeql-action/analyze@v3

.github/workflows/py-ci.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: py-ci (reusable)
2+
on:
3+
workflow_call:
4+
inputs:
5+
python-versions:
6+
required: false
7+
type: string
8+
default: '["3.11","3.12"]'
9+
cov-min:
10+
required: false
11+
type: number
12+
default: 100
13+
secrets:
14+
COSIGN_KEY:
15+
required: false
16+
COSIGN_PASSWORD:
17+
required: false
18+
19+
jobs:
20+
py:
21+
name: Python ${{ matrix.py }}
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 20
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
py: ${{ fromJson(inputs.python-versions) }}
28+
steps:
29+
- uses: actions/checkout@v4
30+
- uses: actions/setup-python@v5
31+
with:
32+
python-version: ${{ matrix.py }}
33+
cache: poetry
34+
- name: Install deps
35+
run: |
36+
python -m pip install -U pip
37+
pip install poetry
38+
poetry install --no-interaction
39+
- name: Lint & Format
40+
run: |
41+
poetry run ruff check .
42+
poetry run black --check .
43+
- name: Type check
44+
run: poetry run mypy src
45+
- name: Tests + coverage
46+
env:
47+
PYTHONPATH: src
48+
run: |
49+
poetry run pytest -q --cov=src --cov-report=xml:coverage.xml --cov-fail-under=${{ inputs.cov-min }}
50+
- name: SBOM (CycloneDX)
51+
run: |
52+
python -m pip install cyclonedx-bom || pip install cyclonedx-bom
53+
cyclonedx-py -o sbom-python-${{ matrix.py }}.xml || cyclonedx-bom -o sbom-python-${{ matrix.py }}.xml || true
54+
- uses: actions/upload-artifact@v4
55+
if: always()
56+
with:
57+
name: py-${{ matrix.py }}-artifacts
58+
path: |
59+
coverage.xml
60+
sbom-python-${{ matrix.py }}.xml
61+
sign:
62+
if: ${{ always() && secrets.COSIGN_KEY != '' }}
63+
needs: [py]
64+
runs-on: ubuntu-latest
65+
steps:
66+
- uses: actions/checkout@v4
67+
- name: Install cosign
68+
run: |
69+
COSIGN_VERSION="v2.4.0"
70+
curl -sSfL "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64" -o cosign
71+
chmod +x cosign
72+
- name: Sign SBOMs (attestation)
73+
env:
74+
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
75+
run: |
76+
for f in sbom-*.xml; do
77+
[ -f "$f" ] && ./cosign sign-blob --key env://COSIGN_KEY "$f" || true
78+
done

.github/workflows/ts-ci.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: ts-ci (reusable)
2+
on:
3+
workflow_call:
4+
inputs:
5+
node-version:
6+
required: false
7+
type: string
8+
default: "20"
9+
secrets:
10+
COSIGN_KEY:
11+
required: false
12+
COSIGN_PASSWORD:
13+
required: false
14+
15+
jobs:
16+
ts:
17+
name: Node ${{ inputs.node-version }}
18+
runs-on: ubuntu-latest
19+
timeout-minutes: 20
20+
steps:
21+
- uses: actions/checkout@v4
22+
- uses: actions/setup-node@v4
23+
with:
24+
node-version: ${{ inputs.node-version }}
25+
cache: npm
26+
- name: Install deps
27+
run: npm ci
28+
- name: Lint & Format
29+
run: npx prettier --check . && npx eslint . --max-warnings=0
30+
- name: Type check
31+
run: |
32+
if [ -f tsconfig.json ]; then npx tsc --noEmit; else echo "no tsconfig.json"; fi
33+
- name: Tests + coverage
34+
run: |
35+
if npm run -s test --dry-run >/dev/null 2>&1; then
36+
npm test -- --coverage --coverageReporters=text --coverageReporters=json-summary;
37+
else
38+
echo "no tests";
39+
fi
40+
- name: SBOM (CycloneDX)
41+
run: |
42+
npx @cyclonedx/cyclonedx-npm --output-file sbom-node.json || true
43+
- uses: actions/upload-artifact@v4
44+
if: always()
45+
with:
46+
name: node-artifacts
47+
path: |
48+
coverage/coverage-summary.json
49+
sbom-node.json
50+
sign:
51+
if: ${{ always() && secrets.COSIGN_KEY != '' }}
52+
needs: [ts]
53+
runs-on: ubuntu-latest
54+
steps:
55+
- uses: actions/checkout@v4
56+
- name: Install cosign
57+
run: |
58+
COSIGN_VERSION="v2.4.0"
59+
curl -sSfL "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64" -o cosign
60+
chmod +x cosign
61+
- name: Sign SBOM (attestation)
62+
env:
63+
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
64+
run: |
65+
[ -f sbom-node.json ] && ./cosign sign-blob --key env://COSIGN_KEY sbom-node.json || true

.pre-commit-config.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.5.7
4+
hooks:
5+
- id: ruff
6+
- id: ruff-format
7+
8+
- repo: https://github.com/psf/black
9+
rev: 24.8.0
10+
hooks:
11+
- id: black
12+
13+
- repo: https://github.com/pre-commit/mirrors-mypy
14+
rev: v1.11.1
15+
hooks:
16+
- id: mypy

.prettierignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules/
2+
.venv/
3+
dist/
4+
coverage/
5+
_ci_logs/
6+
.mypy_cache/
7+
.pytest_cache/
8+
sbom-*.json

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# ci-matrix-starter
2+
3+
Reusable GitHub Actions workflows for Python and TypeScript:
4+
5+
- Lint/Format/Types/Tests
6+
- SBOM (CycloneDX)
7+
- Optional cosign signing
8+
9+
Jobs are exposed via `workflow_call` so downstream repos can consume them.

_ci_logs/preflight-ok.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Preflight ci-matrix-starter 2025-09-14T20:41:16Z
2+
Node: prettier+eslint+tsc+tests ✅
3+
Python: ruff+black+pytest(100%)+mypy ✅

eslint.config.mjs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import js from "@eslint/js";
2+
import tseslint from "typescript-eslint";
3+
4+
export default [
5+
{
6+
ignores: [
7+
"node_modules/**",
8+
"dist/**",
9+
".venv/**",
10+
"coverage/**",
11+
"_ci_logs/**",
12+
"**/*.mjs",
13+
],
14+
},
15+
js.configs.recommended,
16+
...tseslint.configs.recommended,
17+
{
18+
files: ["src/**/*.ts"],
19+
...tseslint.configs.recommendedTypeChecked[0],
20+
languageOptions: { parserOptions: { project: "./tsconfig.json" } },
21+
rules: {},
22+
},
23+
];

0 commit comments

Comments
 (0)