gitoxide-py 0.87.0__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.
- gitoxide_py-0.87.0/.github/windows-check/verify-wheel.sh +36 -0
- gitoxide_py-0.87.0/.github/workflows/ci.yml +128 -0
- gitoxide_py-0.87.0/.github/workflows/release.yml +273 -0
- gitoxide_py-0.87.0/.gitignore +8 -0
- gitoxide_py-0.87.0/Cargo.lock +1687 -0
- gitoxide_py-0.87.0/Cargo.toml +40 -0
- gitoxide_py-0.87.0/LICENSE +24 -0
- gitoxide_py-0.87.0/PKG-INFO +124 -0
- gitoxide_py-0.87.0/README.md +96 -0
- gitoxide_py-0.87.0/RELEASING.md +122 -0
- gitoxide_py-0.87.0/build.rs +35 -0
- gitoxide_py-0.87.0/pyproject.toml +40 -0
- gitoxide_py-0.87.0/scripts/check_version_tracks_gix.py +56 -0
- gitoxide_py-0.87.0/scripts/publish.py +147 -0
- gitoxide_py-0.87.0/src/lib.rs +67 -0
- gitoxide_py-0.87.0/src/repository.rs +1057 -0
- gitoxide_py-0.87.0/src/types.rs +562 -0
- gitoxide_py-0.87.0/src/util.rs +95 -0
- gitoxide_py-0.87.0/tests/conftest.py +167 -0
- gitoxide_py-0.87.0/tests/test_config.py +68 -0
- gitoxide_py-0.87.0/tests/test_edge_cases.py +183 -0
- gitoxide_py-0.87.0/tests/test_gil_release.py +31 -0
- gitoxide_py-0.87.0/tests/test_objects.py +119 -0
- gitoxide_py-0.87.0/tests/test_refs.py +117 -0
- gitoxide_py-0.87.0/tests/test_refs_write.py +156 -0
- gitoxide_py-0.87.0/tests/test_repository.py +231 -0
- gitoxide_py-0.87.0/tests/test_repr.py +87 -0
- gitoxide_py-0.87.0/tests/test_revision.py +205 -0
- gitoxide_py-0.87.0/tests/test_submodules.py +63 -0
- gitoxide_py-0.87.0/tests/test_worktree_state.py +132 -0
- gitoxide_py-0.87.0/uv.lock +227 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install the cross-built Windows wheel into a real Windows CPython and import
|
|
3
|
+
# it. Runs inside infra's windows-ci image (Wine plus a Windows Python), which
|
|
4
|
+
# the release workflow uses as a job container with the wheels mounted beside
|
|
5
|
+
# this script.
|
|
6
|
+
#
|
|
7
|
+
# This is the check the GitHub-hosted windows runner never performed: it built
|
|
8
|
+
# a wheel and uploaded it without ever loading the extension. A wheel that
|
|
9
|
+
# links a DLL the target machine lacks fails here rather than on a user's
|
|
10
|
+
# machine after publishing.
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
# The directory this script was staged into is the one holding the wheels.
|
|
14
|
+
cd "$(dirname "$0")"
|
|
15
|
+
|
|
16
|
+
# --only-binary: the sdist would also satisfy `gitoxide-py`, and building it
|
|
17
|
+
# would need a Rust toolchain this image does not have -- a silent fallback
|
|
18
|
+
# would mean testing something other than the wheel under test.
|
|
19
|
+
# --no-index: everything needed is in that directory, and the container may
|
|
20
|
+
# have no route to PyPI.
|
|
21
|
+
xvfb-run --auto-servernum wine C:/Python312/python.exe -m pip install \
|
|
22
|
+
--no-index --only-binary :all: --find-links . gitoxide-py
|
|
23
|
+
|
|
24
|
+
xvfb-run --auto-servernum wine C:/Python312/python.exe - <<'PY'
|
|
25
|
+
import pathlib
|
|
26
|
+
|
|
27
|
+
import gitoxide_py
|
|
28
|
+
|
|
29
|
+
pkg = pathlib.Path(gitoxide_py.__file__).parent
|
|
30
|
+
assert (pkg / "py.typed").is_file(), "py.typed missing from the wheel"
|
|
31
|
+
assert (pkg / "__init__.pyi").is_file(), "stub missing from the wheel"
|
|
32
|
+
|
|
33
|
+
# Calls into the extension, so a wheel that imports but cannot execute fails
|
|
34
|
+
# here too.
|
|
35
|
+
print("windows wheel ok:", gitoxide_py.gix_version())
|
|
36
|
+
PY
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.event.pull_request.number || github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
name: Test
|
|
19
|
+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
|
20
|
+
runs-on: [self-hosted, feguitv]
|
|
21
|
+
timeout-minutes: 20
|
|
22
|
+
# git, uv, and a pinned Rust toolchain (with rustfmt/clippy) all ship in
|
|
23
|
+
# this image already (infra's roles/python_ci), so no per-run setup is
|
|
24
|
+
# needed for any of them.
|
|
25
|
+
container:
|
|
26
|
+
image: localhost:5000/python-ci:latest
|
|
27
|
+
defaults:
|
|
28
|
+
run:
|
|
29
|
+
shell: bash
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
32
|
+
with:
|
|
33
|
+
fetch-depth: 0 # full history, so `git describe` can see tags
|
|
34
|
+
|
|
35
|
+
- name: Configure git identity for test fixtures
|
|
36
|
+
run: |
|
|
37
|
+
git config --global user.name "CI"
|
|
38
|
+
git config --global user.email "ci@localhost"
|
|
39
|
+
git config --global --add safe.directory '*'
|
|
40
|
+
|
|
41
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
|
42
|
+
|
|
43
|
+
# dunamai derives a version from git state: the exact tag on a tagged
|
|
44
|
+
# commit (a release build on main), otherwise the last tag plus a
|
|
45
|
+
# commit-count/short-SHA suffix so a dev build is traceable to the
|
|
46
|
+
# exact commit it came from. `uv tool run` (the `uvx` shorthand isn't
|
|
47
|
+
# installed in this image) runs it without adding it to the project's
|
|
48
|
+
# own dependencies. `--style semver` is required here (not pep440):
|
|
49
|
+
# Cargo.toml needs strict SemVer, and maturin itself converts a
|
|
50
|
+
# SemVer prerelease/build version into the equivalent PEP 440 wheel
|
|
51
|
+
# version at build time.
|
|
52
|
+
- name: Set package version from git
|
|
53
|
+
run: |
|
|
54
|
+
version="$(uv tool run dunamai from git --style semver)"
|
|
55
|
+
echo "Package version: $version"
|
|
56
|
+
sed -i "0,/^version = \".*\"/s//version = \"$version\"/" Cargo.toml
|
|
57
|
+
|
|
58
|
+
# --locked fails instead of silently updating uv.lock if it's out of
|
|
59
|
+
# sync with pyproject.toml.
|
|
60
|
+
- name: uv sync
|
|
61
|
+
run: uv sync --locked
|
|
62
|
+
|
|
63
|
+
- name: cargo fmt --check
|
|
64
|
+
run: cargo fmt --check
|
|
65
|
+
|
|
66
|
+
- name: cargo clippy
|
|
67
|
+
run: cargo clippy --all-targets -- -D warnings
|
|
68
|
+
|
|
69
|
+
- name: Build extension
|
|
70
|
+
run: uv run maturin develop --release --generate-stubs
|
|
71
|
+
|
|
72
|
+
# `--no-sync` for the same reason the coverage step below needs it: a
|
|
73
|
+
# plain `uv run pytest` re-syncs the project first and swaps the `.so`
|
|
74
|
+
# maturin just installed for whatever build its cache holds for this
|
|
75
|
+
# version, so pytest would import an extension that predates the step
|
|
76
|
+
# above. `--no-sync` runs pytest against the environment as it stands.
|
|
77
|
+
- name: pytest
|
|
78
|
+
run: uv run --no-sync pytest -v
|
|
79
|
+
|
|
80
|
+
# `cargo llvm-cov show-env` prints the RUSTC_WRAPPER/LLVM_PROFILE_FILE/etc
|
|
81
|
+
# that make a `cargo build` instrumented and its profile data land where
|
|
82
|
+
# `cargo llvm-cov report` expects it. The Rust code here is only ever
|
|
83
|
+
# exercised through the Python extension module (there's no `cargo
|
|
84
|
+
# test` harness), so rather than the usual `cargo llvm-cov test`
|
|
85
|
+
# one-liner, the env is exported once, maturin builds the instrumented
|
|
86
|
+
# `.so` under it, pytest drives that `.so` to produce the profile data
|
|
87
|
+
# (the LLVM profiling runtime flushes it when the Python process exits,
|
|
88
|
+
# same as it would for a native Rust test binary), and only then is the
|
|
89
|
+
# report generated from the accumulated data.
|
|
90
|
+
- name: Set up coverage environment
|
|
91
|
+
run: |
|
|
92
|
+
cargo llvm-cov show-env --sh | sed -E "s/^export //; s/'//g" >> "$GITHUB_ENV"
|
|
93
|
+
cargo llvm-cov clean --workspace
|
|
94
|
+
|
|
95
|
+
# A debug build, not the `test` job's `--release`: the release profile's
|
|
96
|
+
# `lto = "thin"` + `codegen-units = 1` (Cargo.toml) inlines and merges
|
|
97
|
+
# code across unit boundaries aggressively enough to make LLVM's
|
|
98
|
+
# per-line coverage counters unreliable.
|
|
99
|
+
- name: Build extension (instrumented)
|
|
100
|
+
run: uv run maturin develop
|
|
101
|
+
|
|
102
|
+
# `--no-sync`: a plain `uv run pytest` re-checks the project against
|
|
103
|
+
# uv's own build/dependency cache first and, finding the local
|
|
104
|
+
# gitoxide-py package "out of sync" with what it last built, silently
|
|
105
|
+
# rebuilds and reinstalls it -- outside the coverage env set up above,
|
|
106
|
+
# so it clobbers the instrumented `.so` maturin just installed with an
|
|
107
|
+
# uninstrumented one before pytest ever imports it. `--no-sync` runs
|
|
108
|
+
# pytest against the environment as it stands.
|
|
109
|
+
- name: pytest (coverage)
|
|
110
|
+
run: uv run --no-sync pytest -v
|
|
111
|
+
|
|
112
|
+
- name: Generate coverage report
|
|
113
|
+
run: |
|
|
114
|
+
echo "## Rust Coverage Summary" >> "$GITHUB_STEP_SUMMARY"
|
|
115
|
+
echo '```' >> "$GITHUB_STEP_SUMMARY"
|
|
116
|
+
cargo llvm-cov report --summary-only >> "$GITHUB_STEP_SUMMARY"
|
|
117
|
+
echo '```' >> "$GITHUB_STEP_SUMMARY"
|
|
118
|
+
|
|
119
|
+
# PyO3's `#[pymethods]` macro emits a zero-width coverage region per
|
|
120
|
+
# generated method trampoline, collapsed onto the attribute's own
|
|
121
|
+
# line, that never registers a hit regardless of whether the method
|
|
122
|
+
# actually ran (e.g. Repository.head_commit's shows 0 despite being
|
|
123
|
+
# called throughout the test suite) -- a coverage-instrumentation
|
|
124
|
+
# artifact of the macro expansion, not a gap in testing. That caps
|
|
125
|
+
# this file's realistic ceiling below literal 100%; 97 leaves
|
|
126
|
+
# headroom for that artifact while still catching real regressions.
|
|
127
|
+
- name: Enforce coverage threshold
|
|
128
|
+
run: cargo llvm-cov report --fail-under-lines 97
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Distributions are built for every push to main so a broken build surfaces
|
|
4
|
+
# then, not at tag time. Publishing happens only for a tag, which is also
|
|
5
|
+
# where the version comes from.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
inputs:
|
|
12
|
+
testpypi:
|
|
13
|
+
description: >-
|
|
14
|
+
Upload to TestPyPI. Exercises the publish path -- the OIDC exchange
|
|
15
|
+
and the upload itself -- which a tag would otherwise be the first
|
|
16
|
+
thing to try.
|
|
17
|
+
type: boolean
|
|
18
|
+
default: false
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
# Every job below mounts the same host directory at /dist, so the wheels move
|
|
24
|
+
# between images without going through actions/upload-artifact -- a billed,
|
|
25
|
+
# org-wide quota a release has no reason to depend on. This works because all
|
|
26
|
+
# three jobs land on the same machine: `feguitv` is a single host, and its
|
|
27
|
+
# runner requires a job container, which is what lets the Windows check simply
|
|
28
|
+
# *be* the Wine image rather than start one by hand. The run id keeps
|
|
29
|
+
# concurrent releases out of each other's directory.
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
# macOS has no self-hosted equivalent here and cannot be cross-compiled to
|
|
33
|
+
# from Linux without an Apple SDK, so it runs on GitHub-hosted runners:
|
|
34
|
+
# minutes, and artifact storage to carry the wheel back. Set the repository
|
|
35
|
+
# variable HOSTED_RUNNERS to `true` to include them; with it unset a release
|
|
36
|
+
# ships the Linux, Windows and source distributions, and macOS builds from
|
|
37
|
+
# the sdist, which needs a Rust toolchain on the installing machine.
|
|
38
|
+
wheels-hosted:
|
|
39
|
+
name: ${{ matrix.target }}
|
|
40
|
+
if: vars.HOSTED_RUNNERS == 'true'
|
|
41
|
+
runs-on: ${{ matrix.runner }}
|
|
42
|
+
strategy:
|
|
43
|
+
fail-fast: false
|
|
44
|
+
matrix:
|
|
45
|
+
include:
|
|
46
|
+
- { runner: ubuntu-latest, target: aarch64-unknown-linux-gnu, args: "--zig" }
|
|
47
|
+
- { runner: macos-latest, target: x86_64-apple-darwin, args: "" }
|
|
48
|
+
- { runner: macos-latest, target: aarch64-apple-darwin, args: "" }
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
51
|
+
with:
|
|
52
|
+
fetch-depth: 0
|
|
53
|
+
|
|
54
|
+
- uses: actions/setup-python@v6
|
|
55
|
+
with:
|
|
56
|
+
python-version: "3.13"
|
|
57
|
+
|
|
58
|
+
# `gix` is pure Rust with no C dependencies (see Cargo.toml), so a
|
|
59
|
+
# cross-compile needs no target toolchain beyond what --zig provides.
|
|
60
|
+
- name: Set package version from git
|
|
61
|
+
shell: bash
|
|
62
|
+
run: |
|
|
63
|
+
pipx install dunamai
|
|
64
|
+
version="$(dunamai from git --style semver)"
|
|
65
|
+
echo "Package version: $version"
|
|
66
|
+
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
|
|
67
|
+
python scripts/check_version_tracks_gix.py "$version"
|
|
68
|
+
fi
|
|
69
|
+
python - "$version" <<'PY'
|
|
70
|
+
import re, sys
|
|
71
|
+
from pathlib import Path
|
|
72
|
+
path = Path("Cargo.toml")
|
|
73
|
+
text = path.read_text()
|
|
74
|
+
path.write_text(re.sub(r'^version = ".*"', f'version = "{sys.argv[1]}"', text, count=1, flags=re.M))
|
|
75
|
+
PY
|
|
76
|
+
|
|
77
|
+
- name: Build wheel
|
|
78
|
+
uses: PyO3/maturin-action@v1
|
|
79
|
+
with:
|
|
80
|
+
target: ${{ matrix.target }}
|
|
81
|
+
args: --release --out dist --generate-stubs ${{ matrix.args }}
|
|
82
|
+
manylinux: auto
|
|
83
|
+
|
|
84
|
+
- uses: actions/upload-artifact@v4
|
|
85
|
+
with:
|
|
86
|
+
name: wheels-${{ matrix.target }}
|
|
87
|
+
path: dist
|
|
88
|
+
|
|
89
|
+
# Everything buildable on Linux: the native wheel, the Windows wheel
|
|
90
|
+
# (cross-compiled with the mingw-w64 toolchain the image carries, see
|
|
91
|
+
# infra's roles/python_ci) and the sdist.
|
|
92
|
+
build:
|
|
93
|
+
name: Build
|
|
94
|
+
runs-on: [self-hosted, feguitv]
|
|
95
|
+
timeout-minutes: 40
|
|
96
|
+
container:
|
|
97
|
+
image: localhost:5000/python-ci:latest
|
|
98
|
+
volumes:
|
|
99
|
+
- /var/tmp/gitoxide-py-release/${{ github.run_id }}:/dist
|
|
100
|
+
defaults:
|
|
101
|
+
run:
|
|
102
|
+
shell: bash
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
105
|
+
with:
|
|
106
|
+
fetch-depth: 0 # full history, so the version can be derived from tags
|
|
107
|
+
|
|
108
|
+
- run: git config --global --add safe.directory '*'
|
|
109
|
+
|
|
110
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
|
111
|
+
|
|
112
|
+
- name: Set package version from git
|
|
113
|
+
env:
|
|
114
|
+
TESTPYPI: ${{ inputs.testpypi }}
|
|
115
|
+
run: |
|
|
116
|
+
# An untagged build's version carries a local segment (`+<sha>`),
|
|
117
|
+
# which PyPI and TestPyPI both reject on upload, so drop it when the
|
|
118
|
+
# run is going to upload. A tagged build has no such segment.
|
|
119
|
+
style=(--style semver)
|
|
120
|
+
if [ "$TESTPYPI" = "true" ]; then style+=(--no-metadata); fi
|
|
121
|
+
version="$(uv tool run dunamai from git "${style[@]}")"
|
|
122
|
+
echo "Package version: $version"
|
|
123
|
+
# A tagged build is a release: its version has to track the gix it
|
|
124
|
+
# links, or the number stops meaning what RELEASING.md says it does.
|
|
125
|
+
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
|
|
126
|
+
uv run --no-project python scripts/check_version_tracks_gix.py "$version"
|
|
127
|
+
fi
|
|
128
|
+
sed -i "0,/^version = \".*\"/s//version = \"$version\"/" Cargo.toml
|
|
129
|
+
|
|
130
|
+
- name: uv sync
|
|
131
|
+
run: uv sync --locked
|
|
132
|
+
|
|
133
|
+
# --generate-stubs bundles the .pyi and py.typed into the wheel; without
|
|
134
|
+
# it the package ships untyped and the Typing :: Typed classifier lies.
|
|
135
|
+
# maturin extracts the stub from the built binary rather than importing
|
|
136
|
+
# it, which is why it works for the cross-compiled target too.
|
|
137
|
+
- name: Build Linux wheel
|
|
138
|
+
run: uv run maturin build --release --out /dist --generate-stubs
|
|
139
|
+
|
|
140
|
+
- name: Build Windows wheel
|
|
141
|
+
run: |
|
|
142
|
+
uv run maturin build --release --target x86_64-pc-windows-gnu \
|
|
143
|
+
--out /dist --generate-stubs
|
|
144
|
+
|
|
145
|
+
- name: Build sdist
|
|
146
|
+
run: uv tool run maturin sdist --out /dist
|
|
147
|
+
|
|
148
|
+
- name: Check the Linux wheel is installable and typed
|
|
149
|
+
run: |
|
|
150
|
+
uv venv /tmp/wheel-check
|
|
151
|
+
uv pip install --python /tmp/wheel-check/bin/python \
|
|
152
|
+
--no-index --find-links /dist gitoxide-py
|
|
153
|
+
/tmp/wheel-check/bin/python -c "
|
|
154
|
+
import gitoxide_py, pathlib
|
|
155
|
+
pkg = pathlib.Path(gitoxide_py.__file__).parent
|
|
156
|
+
assert (pkg / 'py.typed').is_file(), 'py.typed missing from the wheel'
|
|
157
|
+
assert (pkg / '__init__.pyi').is_file(), 'stub missing from the wheel'
|
|
158
|
+
print('wheel ok:', gitoxide_py.open('.').head_id)
|
|
159
|
+
"
|
|
160
|
+
|
|
161
|
+
# Travels with the wheels so the Wine job needs no checkout of its own:
|
|
162
|
+
# that image has no git, and the only thing it needs from the repository
|
|
163
|
+
# is this script.
|
|
164
|
+
- name: Stage the Windows check
|
|
165
|
+
run: cp .github/windows-check/verify-wheel.sh /dist/
|
|
166
|
+
|
|
167
|
+
# A real Windows CPython, under Wine, importing the wheel that will be
|
|
168
|
+
# published -- the check a GitHub-hosted windows runner never performed,
|
|
169
|
+
# since it uploaded a wheel it had never loaded.
|
|
170
|
+
windows-check:
|
|
171
|
+
name: Windows wheel under Wine
|
|
172
|
+
needs: build
|
|
173
|
+
runs-on: [self-hosted, feguitv]
|
|
174
|
+
timeout-minutes: 20
|
|
175
|
+
container:
|
|
176
|
+
image: localhost:5000/windows-ci:latest
|
|
177
|
+
volumes:
|
|
178
|
+
- /var/tmp/gitoxide-py-release/${{ github.run_id }}:/dist
|
|
179
|
+
defaults:
|
|
180
|
+
run:
|
|
181
|
+
shell: bash
|
|
182
|
+
steps:
|
|
183
|
+
- name: Import the Windows wheel
|
|
184
|
+
run: bash /dist/verify-wheel.sh
|
|
185
|
+
|
|
186
|
+
# The same upload the real release performs, against TestPyPI, so the parts
|
|
187
|
+
# a tag would otherwise try for the first time -- the OIDC exchange and the
|
|
188
|
+
# upload -- have been exercised before one is pushed. Needs its own trusted
|
|
189
|
+
# publisher registered on test.pypi.org, with environment `testpypi`.
|
|
190
|
+
publish-testpypi:
|
|
191
|
+
name: Publish to TestPyPI
|
|
192
|
+
needs: [build, windows-check]
|
|
193
|
+
if: inputs.testpypi
|
|
194
|
+
runs-on: [self-hosted, feguitv]
|
|
195
|
+
timeout-minutes: 20
|
|
196
|
+
container:
|
|
197
|
+
image: localhost:5000/python-ci:latest
|
|
198
|
+
volumes:
|
|
199
|
+
- /var/tmp/gitoxide-py-release/${{ github.run_id }}:/dist
|
|
200
|
+
defaults:
|
|
201
|
+
run:
|
|
202
|
+
shell: bash
|
|
203
|
+
environment: testpypi
|
|
204
|
+
permissions:
|
|
205
|
+
# A job-level block replaces the workflow-level one rather than adding to
|
|
206
|
+
# it, so `contents: read` has to be repeated or the checkout fails.
|
|
207
|
+
contents: read
|
|
208
|
+
id-token: write
|
|
209
|
+
steps:
|
|
210
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
211
|
+
|
|
212
|
+
- name: Upload
|
|
213
|
+
run: uv run --no-project python scripts/publish.py https://test.pypi.org /dist
|
|
214
|
+
|
|
215
|
+
publish:
|
|
216
|
+
name: Publish to PyPI
|
|
217
|
+
needs: [build, windows-check, wheels-hosted]
|
|
218
|
+
# `wheels-hosted` is skipped unless HOSTED_RUNNERS is set, and a skipped
|
|
219
|
+
# job would otherwise skip this one too; a *failed* one still blocks.
|
|
220
|
+
if: |
|
|
221
|
+
always() && !cancelled()
|
|
222
|
+
&& startsWith(github.ref, 'refs/tags/v')
|
|
223
|
+
&& needs.build.result == 'success'
|
|
224
|
+
&& needs.windows-check.result == 'success'
|
|
225
|
+
&& needs.wheels-hosted.result != 'failure'
|
|
226
|
+
runs-on: [self-hosted, feguitv]
|
|
227
|
+
timeout-minutes: 20
|
|
228
|
+
container:
|
|
229
|
+
image: localhost:5000/python-ci:latest
|
|
230
|
+
volumes:
|
|
231
|
+
- /var/tmp/gitoxide-py-release/${{ github.run_id }}:/dist
|
|
232
|
+
defaults:
|
|
233
|
+
run:
|
|
234
|
+
shell: bash
|
|
235
|
+
environment: pypi
|
|
236
|
+
permissions:
|
|
237
|
+
contents: read
|
|
238
|
+
# Trusted Publishing: PyPI verifies this workflow's OIDC identity, so
|
|
239
|
+
# there is no API token to store or rotate. Configure the publisher at
|
|
240
|
+
# https://pypi.org/manage/project/gitoxide-py/settings/publishing/
|
|
241
|
+
id-token: write
|
|
242
|
+
steps:
|
|
243
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
244
|
+
|
|
245
|
+
- name: Collect the hosted wheels
|
|
246
|
+
if: vars.HOSTED_RUNNERS == 'true'
|
|
247
|
+
uses: actions/download-artifact@v4
|
|
248
|
+
with:
|
|
249
|
+
pattern: wheels-*
|
|
250
|
+
merge-multiple: true
|
|
251
|
+
path: /dist
|
|
252
|
+
|
|
253
|
+
- name: Upload
|
|
254
|
+
run: uv run --no-project python scripts/publish.py https://pypi.org /dist
|
|
255
|
+
|
|
256
|
+
# The shared directory lives on the runner, outside any workspace, so
|
|
257
|
+
# nothing else reclaims it.
|
|
258
|
+
cleanup:
|
|
259
|
+
name: Clean up
|
|
260
|
+
needs: [build, windows-check, publish, publish-testpypi]
|
|
261
|
+
if: always()
|
|
262
|
+
runs-on: [self-hosted, feguitv]
|
|
263
|
+
timeout-minutes: 10
|
|
264
|
+
container:
|
|
265
|
+
image: localhost:5000/python-ci:latest
|
|
266
|
+
volumes:
|
|
267
|
+
- /var/tmp/gitoxide-py-release/${{ github.run_id }}:/dist
|
|
268
|
+
defaults:
|
|
269
|
+
run:
|
|
270
|
+
shell: bash
|
|
271
|
+
steps:
|
|
272
|
+
- name: Empty the shared directory
|
|
273
|
+
run: rm -rf /dist/* /dist/.[!.]* 2>/dev/null || true
|