-
Notifications
You must be signed in to change notification settings - Fork 0
142 lines (128 loc) · 5.98 KB
/
Copy pathpublish-cargo.yml
File metadata and controls
142 lines (128 loc) · 5.98 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
name: Publish crates.io
run-name: "Publish crates.io ${{ inputs.version }}${{ inputs.distinct-id != '' && format(' [{0}]', inputs.distinct-id) || '' }}"
# Publishes socket-patch-core + socket-patch-cli to crates.io for an existing
# v<version> tag. Dispatched two ways, both as a plain workflow_dispatch run:
# - by release.yml (scripts/dispatch-publish.sh), as one leg of the
# single-dispatch release fan-out — distinct-id carries the release
# run's correlation id into this run's name;
# - manually (Actions → Publish crates.io → Run workflow), to retry just
# this registry after a mid-release failure: fix the cause, enter the
# release version, leave distinct-id blank.
# Every run checks out refs/tags/v<version> and publishes from source, so a
# manual retry publishes exactly what the release run would have.
#
# Idempotent: already-published versions are probed and skipped, so re-runs
# and retries after a partial publish (core landed, cli didn't) are safe.
#
# OIDC trusted publishing: the crates.io trusted publisher for both crates is
# keyed on this repo + THIS file's name (publish-cargo.yml). Because this
# workflow only ever runs as its own top-level workflow_dispatch run (never
# as a called reusable workflow), the OIDC token's workflow_ref and
# job_workflow_ref claims both name this file — one publisher registration
# covers every path, regardless of which claim the registry matches.
on:
workflow_dispatch:
inputs:
version:
description: 'Release version (X.Y.Z; the tag v<version> must exist)'
required: true
type: string
distinct-id:
description: 'Correlation id set by release.yml to track its dispatched run — leave blank for manual runs'
required: false
default: ''
type: string
# Serialize same-version runs: the already-published probes below are
# check-then-act, so two CONCURRENT runs for one version could both pass a
# probe and the loser would hard-fail on the registry. Serialized, a
# duplicate (e.g. re-dispatched by a release-run watcher whose `gh run
# watch` timed out while this run was still going) waits behind the live
# run and then no-ops. NOTE: the group holds at most ONE waiting run — a
# further same-version dispatch displaces (cancels) the waiting duplicate,
# and a watcher following the displaced run reports that as a failure; the
# publish itself is unaffected (the surviving runs no-op or publish).
concurrency:
group: publish-cargo-${{ inputs.version }}
permissions: {}
jobs:
cargo-publish:
runs-on: ubuntu-latest
# Bounds how long a wedged run (hung registry call) can hold this
# workflow's per-version concurrency group before retries can proceed.
timeout-minutes: 45
permissions:
contents: read
id-token: write
steps:
- name: Validate version input
env:
VERSION: ${{ inputs.version }}
run: |
if ! printf '%s' "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::error::'${VERSION}' is not a plain X.Y.Z release version"
exit 1
fi
- name: Checkout release tag
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: refs/tags/v${{ inputs.version }}
persist-credentials: false
- name: Verify tag carries the requested version
env:
VERSION: ${{ inputs.version }}
run: |
CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
if [ "$CARGO_VERSION" != "$VERSION" ]; then
echo "::error::tag v${VERSION} carries workspace version ${CARGO_VERSION} — refusing to publish"
exit 1
fi
- name: Install Rust
# rustup is pre-installed on GitHub-hosted runners. `rustup show`
# reads rust-toolchain.toml in the repo root, then installs the
# pinned channel + listed components if missing.
run: rustup show
- name: Probe crates.io for already-published versions
id: published
env:
VERSION: ${{ inputs.version }}
run: |
# crates.io returns HTTP 200 for a published version and 404
# otherwise; its crawler policy requires a User-Agent identifying
# the caller. Anything but a definite 200 (including transient
# network errors) falls through to publishing, where `cargo publish`
# gives the authoritative error.
probe() {
curl -fsSL -o /dev/null \
-H 'User-Agent: socket-patch-release-workflow (https://github.com/SocketDev/socket-patch)' \
"https://crates.io/api/v1/crates/${1}/${VERSION}"
}
if probe socket-patch-core; then
echo "socket-patch-core ${VERSION} already on crates.io; skipping its publish."
echo "core=true" >> "$GITHUB_OUTPUT"
else
echo "core=false" >> "$GITHUB_OUTPUT"
fi
if probe socket-patch-cli; then
echo "socket-patch-cli ${VERSION} already on crates.io; skipping its publish."
echo "cli=true" >> "$GITHUB_OUTPUT"
else
echo "cli=false" >> "$GITHUB_OUTPUT"
fi
- name: Authenticate with crates.io
id: crates-io-auth
uses: rust-lang/crates-io-auth-action@b7e9a28eded4986ec6b1fa40eeee8f8f165559ec # v1.0.3
- name: Publish socket-patch-core
if: steps.published.outputs.core != 'true'
run: cargo publish -p socket-patch-core
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }}
- name: Wait for crates.io index update
if: steps.published.outputs.core != 'true'
run: sleep 30
- name: Copy README for CLI crate
run: cp README.md crates/socket-patch-cli/README.md
- name: Publish socket-patch-cli
if: steps.published.outputs.cli != 'true'
run: cargo publish -p socket-patch-cli
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token }}