11name : Release and Publish
22
3+ # Publishing is driven by publishing a GitHub release, not by pushing to a
4+ # branch. Create the release and its tag when you intend to ship; until then
5+ # nothing reaches crates.io, so merging to any branch is always safe.
36on :
4- push :
5- branches : [ main, master ]
6- paths-ignore :
7- - ' **.md'
8- - ' docs/**'
9- - ' .github/**'
10- - ' !.github/workflows/release.yml'
7+ release :
8+ types : [published]
119
1210env :
1311 CARGO_TERM_COLOR : always
1412
1513jobs :
16- # First ensure all tests pass
14+ # Derive the version from the release tag and hold it against Cargo.toml, so a
15+ # mistyped tag fails here rather than shipping under the wrong version.
16+ version :
17+ name : Resolve Version
18+ runs-on : ubuntu-latest
19+ outputs :
20+ version : ${{ steps.version.outputs.version }}
21+
22+ steps :
23+ - name : Checkout code
24+ uses : actions/checkout@v7
25+ with :
26+ ref : ${{ github.event.release.tag_name }}
27+
28+ - name : Resolve and verify version
29+ id : version
30+ run : |
31+ TAG="${{ github.event.release.tag_name }}"
32+ VERSION="${TAG#v}"
33+ FAILED=0
34+ for MANIFEST in dotscope/Cargo.toml dotscope-cli/Cargo.toml; do
35+ DECLARED=$(grep '^version = ' "$MANIFEST" | head -1 | cut -d '"' -f 2)
36+ if [ "$VERSION" != "$DECLARED" ]; then
37+ echo "::error::Release tag ${TAG} implies version ${VERSION}, but ${MANIFEST} declares ${DECLARED}"
38+ FAILED=1
39+ fi
40+ done
41+ if [ "$FAILED" -ne 0 ]; then
42+ exit 1
43+ fi
44+ echo "version=$VERSION" >> $GITHUB_OUTPUT
45+ echo "Releasing v${VERSION}"
46+
47+ # Then ensure all tests pass
1748 test :
1849 name : Pre-Release Tests
1950 runs-on : ubuntu-latest
51+ needs : version
2052
2153 steps :
2254 - name : Checkout code
2355 uses : actions/checkout@v7
56+ with :
57+ ref : ${{ github.event.release.tag_name }}
2458
2559 - name : Install Rust toolchain
2660 uses : dtolnay/rust-toolchain@stable
@@ -57,111 +91,11 @@ jobs:
5791 - name : Check documentation
5892 run : cargo doc --no-deps
5993
60- # Create GitHub release
61- create-release :
62- name : Create Release
63- runs-on : ubuntu-latest
64- needs : test
65- outputs :
66- version : ${{ steps.version.outputs.version }}
67- release_created : ${{ steps.check_release.outputs.created }}
68-
69- steps :
70- - name : Checkout code
71- uses : actions/checkout@v7
72- with :
73- fetch-depth : 0
74-
75- - name : Get version from Cargo.toml
76- id : version
77- run : |
78- VERSION=$(grep '^version = ' dotscope/Cargo.toml | head -1 | cut -d '"' -f 2)
79- echo "version=$VERSION" >> $GITHUB_OUTPUT
80- echo "Version: $VERSION"
81-
82- - name : Check if release exists
83- id : check_release
84- run : |
85- if gh release view "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
86- echo "created=false" >> $GITHUB_OUTPUT
87- echo "Release v${{ steps.version.outputs.version }} already exists, skipping"
88- else
89- echo "created=true" >> $GITHUB_OUTPUT
90- fi
91- env :
92- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
93-
94- - name : Generate changelog
95- id : changelog
96- if : steps.check_release.outputs.created == 'true'
97- run : |
98- LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
99-
100- if [ -z "$LATEST_TAG" ]; then
101- CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges)
102- else
103- CHANGELOG=$(git log ${LATEST_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges)
104- fi
105-
106- if [ -z "$CHANGELOG" ]; then
107- CHANGELOG="- Initial release"
108- fi
109-
110- echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
111- echo "$CHANGELOG" >> $GITHUB_OUTPUT
112- echo "EOF" >> $GITHUB_OUTPUT
113-
114- - name : Create Release
115- if : steps.check_release.outputs.created == 'true'
116- run : |
117- VERSION="v${{ steps.version.outputs.version }}"
118- cat <<BODY > release_body.md
119- ## Changes in ${VERSION}
120-
121- ${{ steps.changelog.outputs.CHANGELOG }}
122-
123- ## Installation
124-
125- ### Library
126-
127- Add this to your \`Cargo.toml\`:
128-
129- \`\`\`toml
130- [dependencies]
131- dotscope = "${{ steps.version.outputs.version }}"
132- \`\`\`
133-
134- Or install via cargo:
135-
136- \`\`\`bash
137- cargo add dotscope
138- \`\`\`
139-
140- ### CLI Tool
141-
142- Download the pre-built binary for your platform from the assets below and extract it.
143-
144- | Platform | Asset |
145- |----------|-------|
146- | Linux (x86_64) | \`dotscope-${VERSION}-x86_64-unknown-linux-gnu.zip\` |
147- | macOS (Apple Silicon) | \`dotscope-${VERSION}-aarch64-apple-darwin.zip\` |
148- | Windows (x86_64) | \`dotscope-${VERSION}-x86_64-pc-windows-msvc.zip\` |
149-
150- > **Note:** Z3 is an optional compile-time dependency used only for the \`z3\` feature (CFF reconstruction). The pre-built CLI binaries do **not** require Z3 at runtime.
151- BODY
152-
153- gh release create "$VERSION" \
154- --title "Release ${VERSION}" \
155- --notes-file release_body.md
156- env :
157- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
158-
159- # Build CLI binaries for all platforms
94+ # Build CLI binaries for all platforms and attach them to the release
16095 build-cli :
16196 name : Build CLI (${{ matrix.name }})
16297 runs-on : ${{ matrix.os }}
163- needs : create-release
164- if : needs.create-release.outputs.release_created == 'true'
98+ needs : [version, test]
16599 strategy :
166100 fail-fast : false
167101 matrix :
@@ -182,6 +116,8 @@ jobs:
182116 steps :
183117 - name : Checkout code
184118 uses : actions/checkout@v7
119+ with :
120+ ref : ${{ github.event.release.tag_name }}
185121
186122 - name : Install Rust toolchain
187123 uses : dtolnay/rust-toolchain@stable
@@ -200,31 +136,32 @@ jobs:
200136 if : runner.os != 'Windows'
201137 run : |
202138 cd target/${{ matrix.target }}/release
203- zip dotscope-v${{ needs.create-release .outputs.version }}-${{ matrix.target }}.zip ${{ matrix.artifact }}
204- mv dotscope-v${{ needs.create-release .outputs.version }}-${{ matrix.target }}.zip ${{ github.workspace }}/
139+ zip dotscope-v${{ needs.version .outputs.version }}-${{ matrix.target }}.zip ${{ matrix.artifact }}
140+ mv dotscope-v${{ needs.version .outputs.version }}-${{ matrix.target }}.zip ${{ github.workspace }}/
205141
206142 - name : Package binary (Windows)
207143 if : runner.os == 'Windows'
208144 run : |
209145 cd target/${{ matrix.target }}/release
210- Compress-Archive -Path ${{ matrix.artifact }} -DestinationPath "${{ github.workspace }}\dotscope-v${{ needs.create-release .outputs.version }}-${{ matrix.target }}.zip"
146+ Compress-Archive -Path ${{ matrix.artifact }} -DestinationPath "${{ github.workspace }}\dotscope-v${{ needs.version .outputs.version }}-${{ matrix.target }}.zip"
211147 shell : pwsh
212148
213149 - name : Upload release asset
214- run : gh release upload "v ${{ needs.create- release.outputs.version }}" "dotscope-v${{ needs.create-release .outputs.version }}-${{ matrix.target }}.zip" --clobber
150+ run : gh release upload "${{ github.event. release.tag_name }}" "dotscope-v${{ needs.version .outputs.version }}-${{ matrix.target }}.zip" --clobber
215151 env :
216152 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
217153
218154 # Publish to crates.io
219155 publish :
220156 name : Publish to crates.io
221157 runs-on : ubuntu-latest
222- needs : [test, create-release]
223- if : needs.create-release.outputs.release_created == 'true'
158+ needs : [version, test]
224159
225160 steps :
226161 - name : Checkout code
227162 uses : actions/checkout@v7
163+ with :
164+ ref : ${{ github.event.release.tag_name }}
228165
229166 - name : Install Rust toolchain
230167 uses : dtolnay/rust-toolchain@stable
@@ -244,12 +181,13 @@ jobs:
244181 verify-docs :
245182 name : Verify Documentation
246183 runs-on : ubuntu-latest
247- needs : [test, create-release]
248- if : needs.create-release.outputs.release_created == 'true'
184+ needs : [version, test]
249185
250186 steps :
251187 - name : Checkout code
252188 uses : actions/checkout@v7
189+ with :
190+ ref : ${{ github.event.release.tag_name }}
253191
254192 - name : Install Rust toolchain
255193 uses : dtolnay/rust-toolchain@stable
0 commit comments