Repository navigation
151 lines (141 loc) · 6.66 KB
/
Copy pathpython-package.yml
File metadata and controls
151 lines (141 loc) · 6.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
name: Python package
on: [push, pull_request]
jobs:
# Catch version drift at commit time, not at release time. The publish workflow already
# refuses a tag that disagrees with the manifest, but that only fires on a tag push, so
# drift can sit in master until release day. Same script, no expected version: assert only
# that the repo agrees with itself.
version:
name: version consistency
runs-on: ubuntu-latest
steps:
# persist-credentials: false (2026-08) — nothing here pushes; a kept token is readable from .git/config.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Declared versions agree
run: bash .github/scripts/check-version.sh
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install --require-hashes -r requirements-dev.lock
# Dependency audit gate: fail on known advisories in the runtime floors, the
# committed lock (requirements.lock is the reproducible CI resolution), the
# dev toolchain — the dev set is where a build-time supply-chain attack lands,
# and it was the one requirement file nothing scanned until 2026-08 — and the
# hashed dev lock this job installs from.
- name: pip-audit
run: |
pip-audit -r requirements.txt
pip-audit -r requirements.lock
pip-audit -r requirements-dev.txt
pip-audit -r requirements-dev.lock
- name: Set up Node.js (for the ML-KEM noble bridge)
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '20'
- name: Install ML-KEM bridge deps (@noble/post-quantum)
working-directory: bin
run: npm ci
# libkcore for this runner, from the sha256-pinned KnishIO-Crypto-Core Release, so the suite
# runs once through kcore and once through the pure-Python/Node-bridge fallback.
- name: Fetch libkcore (linux-x64-gnu)
run: python scripts/fetch_kcore.py --target linux-x64-gnu
- name: Lint with ruff
run: |
# Enforced gate (config in pyproject.toml [tool.ruff.lint]): syntax errors (E9)
# + pyflakes real bugs (F) + None/bool comparison (E711/E712), over the shipped
# package + tests. F821 (undefined name) stays relaxed — the SDK uses quoted
# forward-reference annotations ("Molecule"/"KnishIOClient"/…) for circular-import
# avoidance, which pyflakes false-flags (the old flake8 gate omitted F82 for the same reason).
ruff check
- name: Test with pytest (libkcore required, then switched off)
run: |
KNISHIO_KCORE=require pytest
KNISHIO_KCORE=off pytest
wheels:
name: wheels
uses: ./.github/workflows/wheels.yml
# CycloneDX SBOM (pip runtime lock + the bin/ ML-KEM bridge + bundled libkcore), scanned
# with OSV. One definition (.github/scripts/sbom.sh) shared with audit.yml and publish.yml.
sbom:
name: SBOM (CycloneDX) + OSV scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: '3.x'
- name: Set up Node.js (for the ML-KEM noble bridge)
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '20'
- name: Install the SBOM generator
run: python -m pip install cyclonedx-bom==7.5.0
- name: Generate SBOM
run: bash .github/scripts/sbom.sh
- name: Export SBOM paths
run: cat sbom-out/paths.env >> "$GITHUB_ENV"
- name: OSV scan (SBOM + pinned upstream commits)
uses: google/osv-scanner-action/osv-scanner-action@b1d267aea5211fa648d9d442fa64b90dbdcbdd36 # v2.7.0
with:
scan-args: |-
-L
${{ env.SBOM_FILE }}
-L
osv-scanner:${{ env.OSV_COMMITS_FILE }}
- name: Upload SBOM
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: sbom
path: sbom-out/*.cdx.json
if-no-files-found: error
changelog:
name: changelog entry for version bump
runs-on: ubuntu-latest
steps:
# fetch-depth: 0 — the gate diffs against the base commit, which a shallow
# checkout would not have.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
# If knishioclient/__init__.py's __version__ changed relative to the base commit,
# CHANGELOG.md must carry a matching `## [<version>]` heading. No change → no-op.
# Versions are PEP 440 (e.g. 0.9.2.post1), so the match is on the literal string.
# Convention: docs/SDK-RELEASE-CONVENTIONS.md in the KnishIOClientSDK monorepo.
- name: CHANGELOG entry for the manifest version bump
# BASE arrives via env, never interpolated into the shell text (workflow-injection hygiene).
env:
BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
run: |
set -eu
BASE="${BASE:-}"
if [ -z "$BASE" ] || [ "$BASE" = '0000000000000000000000000000000000000000' ] \
|| ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then
echo "No comparable base commit — changelog gate skipped."; exit 0
fi
ver() { sed -n "s/^__version__[[:space:]]*=[[:space:]]*['\"]\([^'\"]*\)['\"].*/\1/p" "$1" | head -1; }
git show "$BASE:knishioclient/__init__.py" > /tmp/base-manifest 2>/dev/null || : > /tmp/base-manifest
NEW=$(ver knishioclient/__init__.py); OLD=$(ver /tmp/base-manifest)
if [ "$NEW" = "$OLD" ]; then echo "version unchanged ($NEW) — nothing to check."; exit 0; fi
echo "version bumped: ${OLD:-<none>} -> $NEW"
# index()==1 is a fixed-string match anchored at column 1: no regex escaping of the
# version, and a deeper `### [x]` heading does NOT satisfy `## [x]` (grep -F would match it).
if ! awk -v h="## [$NEW]" 'index($0, h) == 1 { f = 1 } END { exit !f }' CHANGELOG.md; then
echo "::error file=CHANGELOG.md::Version bumped to $NEW but CHANGELOG.md has no '## [$NEW]' entry."
exit 1
fi
echo "OK: CHANGELOG.md documents $NEW"