openodd-py 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openodd_py-0.2.1/.github/workflows/ci.yml +35 -0
- openodd_py-0.2.1/.github/workflows/release-label.yml +72 -0
- openodd_py-0.2.1/.github/workflows/release.yml +322 -0
- openodd_py-0.2.1/.gitignore +7 -0
- openodd_py-0.2.1/Cargo.lock +731 -0
- openodd_py-0.2.1/Cargo.toml +36 -0
- openodd_py-0.2.1/LICENSE +202 -0
- openodd_py-0.2.1/NOTICE +7 -0
- openodd_py-0.2.1/PKG-INFO +70 -0
- openodd_py-0.2.1/README.md +54 -0
- openodd_py-0.2.1/docs/versioning.md +98 -0
- openodd_py-0.2.1/pyproject.toml +31 -0
- openodd_py-0.2.1/python/openodd/__init__.py +122 -0
- openodd_py-0.2.1/python/openodd/_core.pyi +56 -0
- openodd_py-0.2.1/python/openodd/backend.py +160 -0
- openodd_py-0.2.1/python/openodd/items.py +270 -0
- openodd_py-0.2.1/python/openodd/loader.py +193 -0
- openodd_py-0.2.1/python/openodd/model.py +585 -0
- openodd_py-0.2.1/python/openodd/odds/default/default.binding.yaml +78 -0
- openodd_py-0.2.1/python/openodd/odds/default/default.yaml +23 -0
- openodd_py-0.2.1/python/openodd/py.typed +0 -0
- openodd_py-0.2.1/python/openodd/report.py +190 -0
- openodd_py-0.2.1/python/openodd/sampler.py +299 -0
- openodd_py-0.2.1/python/openodd/sources.py +37 -0
- openodd_py-0.2.1/src/error.rs +40 -0
- openodd_py-0.2.1/src/items.rs +347 -0
- openodd_py-0.2.1/src/lib.rs +37 -0
- openodd_py-0.2.1/src/loader.rs +2321 -0
- openodd_py-0.2.1/src/model.rs +1248 -0
- openodd_py-0.2.1/src/python.rs +1574 -0
- openodd_py-0.2.1/src/report.rs +952 -0
- openodd_py-0.2.1/src/sampler.rs +873 -0
- openodd_py-0.2.1/src/sources.rs +268 -0
- openodd_py-0.2.1/src/transmission.rs +327 -0
- openodd_py-0.2.1/src/units.rs +247 -0
- openodd_py-0.2.1/src/value.rs +232 -0
- openodd_py-0.2.1/src/yaml.rs +220 -0
- openodd_py-0.2.1/tests/differential/cases.py +328 -0
- openodd_py-0.2.1/tests/differential/compare.py +35 -0
- openodd_py-0.2.1/tests/differential/difftest_probes.py +16 -0
- openodd_py-0.2.1/tests/differential/report_run.py +74 -0
- openodd_py-0.2.1/tests/differential/run.py +83 -0
- openodd_py-0.2.1/tests/test_api.py +233 -0
- openodd_py-0.2.1/tests/test_differential.py +47 -0
- openodd_py-0.2.1/tests/test_sampling.py +115 -0
- openodd_py-0.2.1/tools/bump_version.py +100 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
rust:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
14
|
+
with:
|
|
15
|
+
components: clippy, rustfmt
|
|
16
|
+
- uses: Swatinem/rust-cache@v2
|
|
17
|
+
- run: cargo fmt --check
|
|
18
|
+
- run: cargo clippy --all-features --all-targets -- -D warnings
|
|
19
|
+
- run: cargo test
|
|
20
|
+
|
|
21
|
+
python:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
strategy:
|
|
24
|
+
matrix:
|
|
25
|
+
python: ["3.10", "3.12", "3.13"]
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
29
|
+
- uses: Swatinem/rust-cache@v2
|
|
30
|
+
- uses: astral-sh/setup-uv@v6
|
|
31
|
+
with:
|
|
32
|
+
python-version: ${{ matrix.python }}
|
|
33
|
+
- run: uv venv && uv pip install maturin pytest hypothesis pyyaml
|
|
34
|
+
- run: uv run --no-project maturin develop
|
|
35
|
+
- run: uv run --no-project pytest -q
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: Release label
|
|
2
|
+
|
|
3
|
+
# The release workflow reads `release:major|minor|patch` off a PR once it is merged
|
|
4
|
+
# into main. This checks the same thing while the PR is still open, so a mistake shows
|
|
5
|
+
# up as a red check rather than as a failed (or missing) release after the merge:
|
|
6
|
+
#
|
|
7
|
+
# - the three labels exist in the repository, and are created if they do not;
|
|
8
|
+
# - the PR carries at most one of them (the release job refuses more than one);
|
|
9
|
+
# - the job summary says which bump the merge will cut, or that it cuts none.
|
|
10
|
+
#
|
|
11
|
+
# Its own workflow rather than a job in ci.yml, so that adding or removing a label
|
|
12
|
+
# re-runs this check and not the whole build.
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
pull_request:
|
|
16
|
+
types: [opened, reopened, synchronize, labeled, unlabeled]
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
issues: write # create a missing release label
|
|
21
|
+
pull-requests: read
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
release-label:
|
|
25
|
+
name: release label
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- name: the release labels exist in the repository
|
|
29
|
+
env:
|
|
30
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
31
|
+
REPO: ${{ github.repository }}
|
|
32
|
+
run: |
|
|
33
|
+
set -euo pipefail
|
|
34
|
+
existing=$(gh api "repos/$REPO/labels" --paginate --jq '.[].name')
|
|
35
|
+
for spec in "major:d73a4a:Merging this PR cuts a major release" \
|
|
36
|
+
"minor:fbca04:Merging this PR cuts a minor release" \
|
|
37
|
+
"patch:0e8a16:Merging this PR cuts a patch release"; do
|
|
38
|
+
IFS=: read -r lvl color desc <<< "$spec"
|
|
39
|
+
name="release:$lvl"
|
|
40
|
+
if grep -qxF "$name" <<< "$existing"; then
|
|
41
|
+
echo "$name exists"
|
|
42
|
+
continue
|
|
43
|
+
fi
|
|
44
|
+
# A PR from a fork gets a read-only token, so creating can fail; say what
|
|
45
|
+
# is missing instead of an opaque API error.
|
|
46
|
+
if gh api "repos/$REPO/labels" -f name="$name" -f color="$color" \
|
|
47
|
+
-f description="$desc" > /dev/null; then
|
|
48
|
+
echo "created $name"
|
|
49
|
+
else
|
|
50
|
+
echo "::error::label $name does not exist and could not be created"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
- name: at most one release label on this PR
|
|
56
|
+
env:
|
|
57
|
+
LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }}
|
|
58
|
+
run: |
|
|
59
|
+
set -euo pipefail
|
|
60
|
+
found=$(jq -r '.[] | select(test("^release:(major|minor|patch)$"))' <<< "$LABELS")
|
|
61
|
+
count=$(grep -c . <<< "$found" || true)
|
|
62
|
+
if [ "$count" -gt 1 ]; then
|
|
63
|
+
echo "::error::More than one release label on the PR ($(paste -sd, <<< "$found")) -- pick exactly one"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
if [ "$count" -eq 1 ]; then
|
|
67
|
+
msg="Merging this PR cuts a **${found#release:}** release."
|
|
68
|
+
else
|
|
69
|
+
msg="No release label: merging this PR cuts no release."
|
|
70
|
+
fi
|
|
71
|
+
echo "$msg"
|
|
72
|
+
echo "$msg" >> "$GITHUB_STEP_SUMMARY"
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# Cuts releases and publishes them to the GitHub release and PyPI. The mechanism is simple_lanelet2's.
|
|
2
|
+
#
|
|
3
|
+
# There are three ways in, and the first is the everyday one:
|
|
4
|
+
#
|
|
5
|
+
# push to main when a merged PR carries exactly one `release:major|minor|patch`
|
|
6
|
+
# label, the `release` job bumps the version, tags it `vX.Y.Z`, and the
|
|
7
|
+
# tag is built and published -- all in this one run.
|
|
8
|
+
# push a v* tag a hand-cut tag (a hotfix, say) is built and published directly; the
|
|
9
|
+
# `release` job is skipped.
|
|
10
|
+
# workflow_dispatch builds the wheels and sdist without tagging or publishing, to
|
|
11
|
+
# eyeball a release before committing to it.
|
|
12
|
+
#
|
|
13
|
+
# The publish happens as a follow-up job here rather than in a separate `on: push:
|
|
14
|
+
# tags` workflow on purpose: the auto-release tag is pushed with GITHUB_TOKEN, and a
|
|
15
|
+
# token-pushed tag does not start new workflow runs.
|
|
16
|
+
#
|
|
17
|
+
# Every release's wheels and sdist are attached to its GitHub release, whose assets
|
|
18
|
+
# page is a find-links index (`uv`/`pip --find-links
|
|
19
|
+
# https://github.com/hakuturu583/openodd_py/releases/expanded_assets/<tag>`).
|
|
20
|
+
#
|
|
21
|
+
# Every release is also published to PyPI with Trusted Publishing, so no token is
|
|
22
|
+
# stored here. Configure it once on PyPI: project openodd-py, owner hakuturu583,
|
|
23
|
+
# repository openodd_py, workflow release.yml, environment pypi. Until that exists the
|
|
24
|
+
# publish job fails, while the GitHub release still carries the files.
|
|
25
|
+
#
|
|
26
|
+
# A version can never be replaced on PyPI, so the tests run again here rather than
|
|
27
|
+
# trusting that CI was green on the same commit.
|
|
28
|
+
|
|
29
|
+
name: Release
|
|
30
|
+
|
|
31
|
+
on:
|
|
32
|
+
push:
|
|
33
|
+
branches: [main]
|
|
34
|
+
tags: ["v*"]
|
|
35
|
+
workflow_dispatch:
|
|
36
|
+
|
|
37
|
+
permissions:
|
|
38
|
+
contents: read
|
|
39
|
+
|
|
40
|
+
jobs:
|
|
41
|
+
# Read the release label off the merged PR and, if there is one, bump the version
|
|
42
|
+
# and push the tag. Runs only on a push to main; a tag push or a manual dispatch
|
|
43
|
+
# skips it and falls straight through to the build.
|
|
44
|
+
release:
|
|
45
|
+
name: cut the release tag
|
|
46
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
permissions:
|
|
49
|
+
contents: write
|
|
50
|
+
pull-requests: read # the commit -> PR lookup below
|
|
51
|
+
# Two release merges close together would both bump the same main and race on the
|
|
52
|
+
# push. Queue them instead: the second checks out main only once the first has
|
|
53
|
+
# pushed its bump, so it bumps from there.
|
|
54
|
+
concurrency:
|
|
55
|
+
group: release-bump
|
|
56
|
+
cancel-in-progress: false
|
|
57
|
+
outputs:
|
|
58
|
+
bump: ${{ steps.pr.outputs.bump }}
|
|
59
|
+
tag: ${{ steps.bump.outputs.tag }}
|
|
60
|
+
steps:
|
|
61
|
+
- name: read the release label off the merged PR
|
|
62
|
+
id: pr
|
|
63
|
+
env:
|
|
64
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
65
|
+
run: |
|
|
66
|
+
set -euo pipefail
|
|
67
|
+
# A failed lookup fails the job: swallowing it would read as "no PR" and
|
|
68
|
+
# drop a labelled merge's release without a trace.
|
|
69
|
+
pr_json=$(gh api repos/${{ github.repository }}/commits/${{ github.sha }}/pulls \
|
|
70
|
+
--jq '.[0] // empty')
|
|
71
|
+
if [ -z "$pr_json" ]; then
|
|
72
|
+
echo "No PR associated with this push -- nothing to release"
|
|
73
|
+
echo "bump=none" >> "$GITHUB_OUTPUT"
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
labels=$(echo "$pr_json" | jq -r '[.labels[].name] | join(",")')
|
|
78
|
+
bump=none
|
|
79
|
+
count=0
|
|
80
|
+
for lvl in major minor patch; do
|
|
81
|
+
if echo ",$labels," | grep -q ",release:${lvl},"; then
|
|
82
|
+
bump=$lvl
|
|
83
|
+
count=$((count + 1))
|
|
84
|
+
fi
|
|
85
|
+
done
|
|
86
|
+
if [ "$count" -gt 1 ]; then
|
|
87
|
+
echo "::error::More than one release label on the PR -- pick exactly one"
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
echo "bump=${bump}" >> "$GITHUB_OUTPUT"
|
|
91
|
+
echo "Resolved bump level: ${bump}"
|
|
92
|
+
|
|
93
|
+
- uses: actions/checkout@v4
|
|
94
|
+
if: steps.pr.outputs.bump != 'none'
|
|
95
|
+
with:
|
|
96
|
+
ref: main
|
|
97
|
+
fetch-depth: 0
|
|
98
|
+
|
|
99
|
+
- name: bump Cargo.toml and Cargo.lock together
|
|
100
|
+
if: steps.pr.outputs.bump != 'none'
|
|
101
|
+
id: bump
|
|
102
|
+
run: |
|
|
103
|
+
set -euo pipefail
|
|
104
|
+
next=$(python3 tools/bump_version.py "${{ steps.pr.outputs.bump }}")
|
|
105
|
+
echo "next=${next}" >> "$GITHUB_OUTPUT"
|
|
106
|
+
echo "tag=v${next}" >> "$GITHUB_OUTPUT"
|
|
107
|
+
echo "Bumped to ${next} (${{ steps.pr.outputs.bump }})"
|
|
108
|
+
|
|
109
|
+
- name: fail if the tag already exists
|
|
110
|
+
if: steps.pr.outputs.bump != 'none'
|
|
111
|
+
run: |
|
|
112
|
+
if git rev-parse "refs/tags/${{ steps.bump.outputs.tag }}" >/dev/null 2>&1; then
|
|
113
|
+
echo "Tag ${{ steps.bump.outputs.tag }} already exists" >&2
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
- name: commit, tag and push
|
|
118
|
+
if: steps.pr.outputs.bump != 'none'
|
|
119
|
+
run: |
|
|
120
|
+
set -euo pipefail
|
|
121
|
+
git config user.name "github-actions[bot]"
|
|
122
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
123
|
+
git add Cargo.toml Cargo.lock
|
|
124
|
+
git commit -m "chore(release): ${{ steps.bump.outputs.tag }}"
|
|
125
|
+
git tag -a "${{ steps.bump.outputs.tag }}" -m "Release ${{ steps.bump.outputs.tag }}"
|
|
126
|
+
git push origin "HEAD:main"
|
|
127
|
+
git push origin "${{ steps.bump.outputs.tag }}"
|
|
128
|
+
|
|
129
|
+
# One place to work out, for all three entry points, which commit to build and whether
|
|
130
|
+
# it may be published. Runs even when `release` is skipped (tag push, dispatch).
|
|
131
|
+
resolve:
|
|
132
|
+
name: resolve what to build
|
|
133
|
+
needs: [release]
|
|
134
|
+
if: always()
|
|
135
|
+
runs-on: ubuntu-latest
|
|
136
|
+
outputs:
|
|
137
|
+
proceed: ${{ steps.r.outputs.proceed }}
|
|
138
|
+
ref: ${{ steps.r.outputs.ref }}
|
|
139
|
+
publish: ${{ steps.r.outputs.publish }}
|
|
140
|
+
version: ${{ steps.r.outputs.version }}
|
|
141
|
+
steps:
|
|
142
|
+
- id: r
|
|
143
|
+
env:
|
|
144
|
+
EVENT: ${{ github.event_name }}
|
|
145
|
+
REF: ${{ github.ref }}
|
|
146
|
+
REF_NAME: ${{ github.ref_name }}
|
|
147
|
+
BUMP: ${{ needs.release.outputs.bump }}
|
|
148
|
+
TAG: ${{ needs.release.outputs.tag }}
|
|
149
|
+
run: |
|
|
150
|
+
set -euo pipefail
|
|
151
|
+
proceed=false; publish=false; ref="$REF_NAME"
|
|
152
|
+
if [ "$EVENT" = "push" ] && [[ "$REF" == refs/tags/v* ]]; then
|
|
153
|
+
# A hand-cut tag: build and publish it.
|
|
154
|
+
proceed=true; publish=true; ref="$REF_NAME"
|
|
155
|
+
elif [ "$EVENT" = "push" ] && [ "$REF" = "refs/heads/main" ]; then
|
|
156
|
+
# The auto-release path: build and publish only if a tag was cut.
|
|
157
|
+
if [ "$BUMP" != "none" ] && [ -n "$TAG" ]; then
|
|
158
|
+
proceed=true; publish=true; ref="$TAG"
|
|
159
|
+
fi
|
|
160
|
+
elif [ "$EVENT" = "workflow_dispatch" ]; then
|
|
161
|
+
# A dry run: build the artifacts, publish nothing.
|
|
162
|
+
proceed=true; publish=false; ref="$REF_NAME"
|
|
163
|
+
fi
|
|
164
|
+
version=""
|
|
165
|
+
if [ "$publish" = true ]; then version="${ref#v}"; fi
|
|
166
|
+
echo "proceed=$proceed" >> "$GITHUB_OUTPUT"
|
|
167
|
+
echo "publish=$publish" >> "$GITHUB_OUTPUT"
|
|
168
|
+
echo "ref=$ref" >> "$GITHUB_OUTPUT"
|
|
169
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
170
|
+
echo "event=$EVENT ref=$ref proceed=$proceed publish=$publish version=$version"
|
|
171
|
+
|
|
172
|
+
verify:
|
|
173
|
+
name: test the tagged commit
|
|
174
|
+
needs: [resolve]
|
|
175
|
+
# `release` is skipped on a tag push or a dispatch, and a skipped ancestor skips
|
|
176
|
+
# every job downstream unless its condition says otherwise -- hence `!cancelled()`.
|
|
177
|
+
if: ${{ !cancelled() && needs.resolve.result == 'success' && needs.resolve.outputs.proceed == 'true' }}
|
|
178
|
+
runs-on: ubuntu-latest
|
|
179
|
+
steps:
|
|
180
|
+
- uses: actions/checkout@v4
|
|
181
|
+
with:
|
|
182
|
+
ref: ${{ needs.resolve.outputs.ref }}
|
|
183
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
184
|
+
- uses: astral-sh/setup-uv@v6
|
|
185
|
+
with:
|
|
186
|
+
python-version: "3.12"
|
|
187
|
+
|
|
188
|
+
# pyproject.toml declares the version dynamic, so Cargo.toml is the one source;
|
|
189
|
+
# the lock file must agree with it or `cargo --locked` fails downstream.
|
|
190
|
+
- name: the version in the tag must be the version in the package
|
|
191
|
+
if: needs.resolve.outputs.publish == 'true'
|
|
192
|
+
env:
|
|
193
|
+
TAG: ${{ needs.resolve.outputs.version }}
|
|
194
|
+
run: |
|
|
195
|
+
CRATE=$(python3 tools/bump_version.py --current)
|
|
196
|
+
echo "tag=$TAG cargo=$CRATE"
|
|
197
|
+
test "$TAG" = "$CRATE"
|
|
198
|
+
cargo metadata --locked --format-version 1 --no-deps > /dev/null
|
|
199
|
+
|
|
200
|
+
- run: cargo test --locked
|
|
201
|
+
- run: uv venv && uv pip install maturin pytest hypothesis pyyaml
|
|
202
|
+
- run: uv run --no-project maturin develop
|
|
203
|
+
- run: uv run --no-project pytest -q
|
|
204
|
+
|
|
205
|
+
wheels:
|
|
206
|
+
name: wheel (${{ matrix.os }} ${{ matrix.target }})
|
|
207
|
+
needs: [resolve, verify]
|
|
208
|
+
if: ${{ !cancelled() && needs.verify.result == 'success' }}
|
|
209
|
+
runs-on: ${{ matrix.runner }}
|
|
210
|
+
strategy:
|
|
211
|
+
matrix:
|
|
212
|
+
# Every target on a runner of its own architecture, so the wheel that is
|
|
213
|
+
# uploaded is also the wheel that is tested below.
|
|
214
|
+
include:
|
|
215
|
+
- { os: linux, runner: ubuntu-latest, target: x86_64 }
|
|
216
|
+
- { os: linux, runner: ubuntu-24.04-arm, target: aarch64 }
|
|
217
|
+
- { os: macos, runner: macos-latest, target: universal2-apple-darwin }
|
|
218
|
+
steps:
|
|
219
|
+
- uses: actions/checkout@v4
|
|
220
|
+
with:
|
|
221
|
+
ref: ${{ needs.resolve.outputs.ref }}
|
|
222
|
+
# abi3-py310, so one wheel per platform serves every Python 3.10+.
|
|
223
|
+
# `manylinux` is ignored on macOS.
|
|
224
|
+
- uses: PyO3/maturin-action@v1
|
|
225
|
+
with:
|
|
226
|
+
target: ${{ matrix.target }}
|
|
227
|
+
args: --release --out dist
|
|
228
|
+
manylinux: auto
|
|
229
|
+
# The artifact itself, installed clean and run against the tests -- not a
|
|
230
|
+
# rebuild of the same sources.
|
|
231
|
+
- uses: astral-sh/setup-uv@v6
|
|
232
|
+
- name: test the wheel that is about to be published
|
|
233
|
+
run: |
|
|
234
|
+
uv venv --python 3.12 "$RUNNER_TEMP/fresh"
|
|
235
|
+
uv pip install --python "$RUNNER_TEMP/fresh" dist/*.whl pytest hypothesis pyyaml
|
|
236
|
+
# Run from a directory without python/openodd, so the installed wheel is
|
|
237
|
+
# what gets imported.
|
|
238
|
+
cp -r tests "$RUNNER_TEMP/tests"
|
|
239
|
+
cd "$RUNNER_TEMP" && "$RUNNER_TEMP/fresh/bin/python" -m pytest tests -q
|
|
240
|
+
- uses: actions/upload-artifact@v4
|
|
241
|
+
with:
|
|
242
|
+
name: wheel-${{ matrix.os }}-${{ matrix.target }}
|
|
243
|
+
path: dist
|
|
244
|
+
|
|
245
|
+
sdist:
|
|
246
|
+
name: sdist
|
|
247
|
+
needs: [resolve, verify]
|
|
248
|
+
if: ${{ !cancelled() && needs.verify.result == 'success' }}
|
|
249
|
+
runs-on: ubuntu-latest
|
|
250
|
+
steps:
|
|
251
|
+
- uses: actions/checkout@v4
|
|
252
|
+
with:
|
|
253
|
+
ref: ${{ needs.resolve.outputs.ref }}
|
|
254
|
+
- uses: PyO3/maturin-action@v1
|
|
255
|
+
with:
|
|
256
|
+
command: sdist
|
|
257
|
+
args: --out dist
|
|
258
|
+
- uses: actions/upload-artifact@v4
|
|
259
|
+
with:
|
|
260
|
+
name: sdist
|
|
261
|
+
path: dist
|
|
262
|
+
|
|
263
|
+
# The GitHub release, created for an auto-cut and a hand-cut tag alike, once the
|
|
264
|
+
# wheels have passed -- so a release page never exists without its files.
|
|
265
|
+
github-release:
|
|
266
|
+
name: GitHub release with the wheels attached
|
|
267
|
+
needs: [resolve, wheels, sdist]
|
|
268
|
+
if: ${{ !cancelled() && needs.wheels.result == 'success' && needs.sdist.result == 'success' && needs.resolve.outputs.publish == 'true' }}
|
|
269
|
+
runs-on: ubuntu-latest
|
|
270
|
+
permissions:
|
|
271
|
+
contents: write
|
|
272
|
+
steps:
|
|
273
|
+
- uses: actions/download-artifact@v4
|
|
274
|
+
with:
|
|
275
|
+
pattern: wheel-*
|
|
276
|
+
path: dist
|
|
277
|
+
merge-multiple: true
|
|
278
|
+
- uses: actions/download-artifact@v4
|
|
279
|
+
with:
|
|
280
|
+
name: sdist
|
|
281
|
+
path: dist
|
|
282
|
+
- uses: softprops/action-gh-release@v2
|
|
283
|
+
with:
|
|
284
|
+
tag_name: ${{ needs.resolve.outputs.ref }}
|
|
285
|
+
files: dist/*
|
|
286
|
+
generate_release_notes: true
|
|
287
|
+
|
|
288
|
+
publish:
|
|
289
|
+
name: publish to PyPI
|
|
290
|
+
needs: [resolve, wheels, sdist]
|
|
291
|
+
if: ${{ !cancelled() && needs.wheels.result == 'success' && needs.sdist.result == 'success' && needs.resolve.outputs.publish == 'true' }}
|
|
292
|
+
runs-on: ubuntu-latest
|
|
293
|
+
environment:
|
|
294
|
+
name: pypi
|
|
295
|
+
url: https://pypi.org/p/openodd-py
|
|
296
|
+
permissions:
|
|
297
|
+
id-token: write # Trusted Publishing; no token in secrets.
|
|
298
|
+
steps:
|
|
299
|
+
# Named explicitly rather than "everything in the run", so nothing else a later
|
|
300
|
+
# job uploads can end up in dist/.
|
|
301
|
+
- uses: actions/download-artifact@v4
|
|
302
|
+
with:
|
|
303
|
+
pattern: wheel-*
|
|
304
|
+
path: dist
|
|
305
|
+
merge-multiple: true
|
|
306
|
+
- uses: actions/download-artifact@v4
|
|
307
|
+
with:
|
|
308
|
+
name: sdist
|
|
309
|
+
path: dist
|
|
310
|
+
- run: ls -l dist
|
|
311
|
+
|
|
312
|
+
# PyPI rejects bad metadata only after the build and the OIDC exchange; failing
|
|
313
|
+
# here costs nothing.
|
|
314
|
+
- name: check the metadata before uploading
|
|
315
|
+
run: pipx run twine check --strict dist/*
|
|
316
|
+
|
|
317
|
+
# PyPI refuses to replace a file but accepts new ones for an existing release,
|
|
318
|
+
# so skipping what is already there lets a re-run add what an earlier attempt
|
|
319
|
+
# failed to upload.
|
|
320
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
321
|
+
with:
|
|
322
|
+
skip-existing: true
|