Repository navigation
140 lines (120 loc) · 5.21 KB
/
Copy pathpublish.yml
File metadata and controls
140 lines (120 loc) · 5.21 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
name: Release - Build, cross-OS validate, publish to PyPI
# Tag-driven release pipeline. A pushed v* tag fans out into:
# build -> cross-os-tests (3 OS x 4 Python = 12) -> publish
# Any matrix combo failure breaks the needs: chain so PyPI publish does
# not run unless every supported (OS, Python) pair installs the packaged
# wheel cleanly and runs the test suite green.
on:
push:
tags: ['v*']
# No workflow-level permissions; each job declares the minimum it needs.
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read # checkout reads the tagged ref
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-python@v7
with:
python-version: "3.11"
- name: Install build tooling
run: python -m pip install --upgrade pip build
- name: Build wheel + sdist
run: python -m build # outputs dist/*.whl and dist/*.tar.gz
- name: Verify package metadata
run: |
python -m pip install twine
twine check dist/*
- uses: actions/upload-artifact@v7
with:
name: dist
path: dist/
cross-os-tests:
needs: build
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python: ['3.10', '3.11', '3.12', '3.13']
runs-on: ${{ matrix.os }}
permissions:
contents: read # checkout reads the tagged ref for tests/ + tools/
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python }}
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: dist
path: dist
- name: Install packaged wheel (NOT editable)
shell: bash # POSIX shell on Windows runners too
run: |
# Install exactly what PyPI users will pip install. Transitive
# dependencies resolve from PyPI; the local dist/ only contains
# the forgelm wheel, not its dependency closure (so no
# --no-index --find-links here).
python -m pip install --upgrade pip
python -m pip install dist/*.whl
- name: Install Linux-only extras (qlora, ingestion, eval)
if: runner.os == 'Linux'
shell: bash
# qlora pulls bitsandbytes which only ships Linux wheels.
# ingestion + eval are cross-platform but bundled here so a single
# conditional install covers the breadth-test surface on Linux.
run: python -m pip install 'forgelm[qlora,ingestion,eval]'
- name: Install pytest
shell: bash
run: python -m pip install pytest pytest-cov
- name: Test (CPU-only, no-network)
shell: bash
# Two marker-driven exclusions, both following the marker rather
# than a brittle path (F-P8-C-20 made the marker the single source
# of truth: a renamed test keeps its marker and stays excluded).
#
# - fixture_drift: snapshot fixtures (test_cost_estimation's pricing
# table) that drift upstream on a different cadence than releases.
# - requires_editable_install: assertions that are only true under
# `pip install -e .`. This job installs the wheel NON-editably on
# purpose, and tools/check_import_origin.py correctly reports that
# as "forgelm resolves outside this checkout" — so its CLI-level
# tests would fail here for exactly the reason the guard exists.
# Its pure-function tests (TestVerdict) are unmarked and still run,
# so the contract itself stays covered in this matrix.
run: pytest tests/ -q -m 'not fixture_drift and not requires_editable_install'
- name: Generate SBOM (CycloneDX 1.5)
shell: bash
run: python tools/generate_sbom.py > sbom-${{ matrix.os }}-py${{ matrix.python }}.json
- uses: actions/upload-artifact@v7
with:
name: sbom-${{ matrix.os }}-py${{ matrix.python }}
path: sbom-${{ matrix.os }}-py${{ matrix.python }}.json
publish:
needs: cross-os-tests # blocked until every matrix combo is green
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/forgelm
permissions:
contents: read # download-artifact reads workflow run state
id-token: write # OIDC trusted publishing to PyPI
steps:
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: dist
path: dist/
- name: Publish to PyPI
# Pinned by commit SHA for supply-chain integrity (SonarCloud S7637 /
# OpenSSF Scorecard "pinned-dependencies"). The pin maps to PyPA's
# v1.14.0 release; bump deliberately when upgrading.
# v1.9.0 → v1.14.0: v1.9.0 bundled a pkginfo/twine that rejected
# Metadata-Version 2.4 wheels (setuptools ≥77 emits 2.4 for PEP 639
# license expressions) with "Metadata is missing required fields:
# Name, Version"; v1.14.0 parses 2.4 correctly.
uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3