@@ -14,114 +14,186 @@ concurrency:
1414 cancel-in-progress : false
1515
1616jobs :
17- draft-release :
18- runs-on : windows-latest
19-
17+ metadata :
18+ runs-on : ubuntu-latest
19+ outputs :
20+ version : ${{ steps.version.outputs.version }}
21+ tag : ${{ steps.version.outputs.tag }}
22+ mode : ${{ steps.tag_state.outputs.mode }}
2023 steps :
2124 - name : Checkout
2225 uses : actions/checkout@v4
2326 with :
2427 fetch-depth : 0
2528
29+ - name : Read package version
30+ id : version
31+ shell : bash
32+ run : |
33+ version="$(python -c "import pathlib,re; text=pathlib.Path('Cargo.toml').read_text(); print(re.search(r'(?s)\[package\].*?version\s*=\s*\"([^\"]+)\"', text).group(1))")"
34+ echo "version=$version" >> "$GITHUB_OUTPUT"
35+ echo "tag=v$version" >> "$GITHUB_OUTPUT"
36+
37+ - name : Check release tag state
38+ id : tag_state
39+ shell : bash
40+ run : |
41+ tag="${{ steps.version.outputs.tag }}"
42+ if ! tag_sha="$(git rev-list -n 1 "$tag" 2>/dev/null)"; then
43+ echo "mode=new" >> "$GITHUB_OUTPUT"
44+ elif [ "$tag_sha" = "$GITHUB_SHA" ]; then
45+ echo "mode=rerun" >> "$GITHUB_OUTPUT"
46+ else
47+ echo "mode=skip" >> "$GITHUB_OUTPUT"
48+ echo "Version tag $tag already exists on commit $tag_sha. Skipping release for unchanged Cargo version."
49+ fi
50+
51+ build-windows :
52+ needs : metadata
53+ if : needs.metadata.outputs.mode != 'skip'
54+ runs-on : windows-latest
55+ steps :
56+ - name : Checkout
57+ uses : actions/checkout@v4
58+
2659 - name : Install Rust
2760 uses : dtolnay/rust-toolchain@stable
2861
2962 - name : Install Inno Setup
3063 shell : pwsh
3164 run : choco install innosetup --no-progress --yes
3265
33- - name : Read package version
34- id : version
66+ - name : Test
67+ shell : pwsh
68+ run : cargo test --locked
69+
70+ - name : Build packages
3571 shell : pwsh
72+ run : .\.github\scripts\package-release.ps1 -RepoRoot $pwd.Path
73+
74+ - name : Upload workflow artifacts
75+ uses : actions/upload-artifact@v4
76+ with :
77+ name : windows-release
78+ path : |
79+ target/release/usb_mirror_sync-portable-v${{ needs.metadata.outputs.version }}.zip
80+ target/release/usb_mirror_sync-setup-v${{ needs.metadata.outputs.version }}.exe
81+
82+ build-macos :
83+ needs : metadata
84+ if : needs.metadata.outputs.mode != 'skip'
85+ runs-on : macos-latest
86+ steps :
87+ - name : Checkout
88+ uses : actions/checkout@v4
89+
90+ - name : Install Rust
91+ uses : dtolnay/rust-toolchain@stable
92+
93+ - name : Test
94+ run : cargo test --locked
95+
96+ - name : Build release binary
97+ run : cargo build --release --locked
98+
99+ - name : Package macOS artifact
100+ shell : bash
36101 run : |
37- $manifest = Get-Content -LiteralPath Cargo.toml -Raw
38- if ($manifest -match '(?s)\[package\].*?version\s*=\s*"([^"]+)"') {
39- $version = $matches[1]
40- } else {
41- throw "Unable to read package version from Cargo.toml"
42- }
102+ version="${{ needs.metadata.outputs.version }}"
103+ arch="$(uname -m)"
104+ stage="target/release/package-macos"
105+ artifact="target/release/usb_mirror_sync-macos-${arch}-v${version}.tar.gz"
106+ rm -rf "$stage"
107+ mkdir -p "$stage"
108+ cp target/release/usb_mirror_sync "$stage/"
109+ cp README.md "$stage/"
110+ cp config.example.json "$stage/"
111+ tar -czf "$artifact" -C "$stage" .
112+
113+ - name : Upload workflow artifacts
114+ uses : actions/upload-artifact@v4
115+ with :
116+ name : macos-release
117+ path : target/release/usb_mirror_sync-macos-*-v${{ needs.metadata.outputs.version }}.tar.gz
43118
44- "version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
45- "tag=v$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
119+ build-linux :
120+ needs : metadata
121+ if : needs.metadata.outputs.mode != 'skip'
122+ runs-on : ubuntu-latest
123+ steps :
124+ - name : Checkout
125+ uses : actions/checkout@v4
46126
47- - name : Check release tag state
48- id : tag_state
49- shell : pwsh
127+ - name : Install Rust
128+ uses : dtolnay/rust-toolchain@stable
129+
130+ - name : Install Linux UI dependencies
50131 run : |
51- $tag = "${{ steps.version.outputs.tag }}"
52- $tagSha = & git rev-list -n 1 $tag 2>$null
53- $tagLookupExit = $LASTEXITCODE
54- $global:LASTEXITCODE = 0
55-
56- if ($tagLookupExit -ne 0 -or -not $tagSha) {
57- "mode=new" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
58- } elseif ($tagSha -eq $env:GITHUB_SHA) {
59- "mode=rerun" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
60- } else {
61- "mode=skip" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
62- Write-Host "Version tag $tag already exists on commit $tagSha. Skipping release for unchanged Cargo version."
63- }
132+ sudo apt-get update
133+ sudo apt-get install -y libgtk-3-dev libayatana-appindicator3-dev
64134
65135 - name : Test
66- if : steps.tag_state.outputs.mode != 'skip'
67- shell : pwsh
68136 run : cargo test --locked
69137
70- - name : Build packages
71- if : steps.tag_state.outputs.mode != 'skip'
72- shell : pwsh
73- run : .\.github\scripts\package-release.ps1 -RepoRoot $pwd.Path
138+ - name : Build release binary
139+ run : cargo build --release --locked
140+
141+ - name : Package Linux artifact
142+ shell : bash
143+ run : |
144+ version="${{ needs.metadata.outputs.version }}"
145+ arch="$(uname -m)"
146+ stage="target/release/package-linux"
147+ artifact="target/release/usb_mirror_sync-linux-${arch}-v${version}.tar.gz"
148+ rm -rf "$stage"
149+ mkdir -p "$stage"
150+ cp target/release/usb_mirror_sync "$stage/"
151+ cp README.md "$stage/"
152+ cp config.example.json "$stage/"
153+ tar -czf "$artifact" -C "$stage" .
154+
155+ - name : Upload workflow artifacts
156+ uses : actions/upload-artifact@v4
157+ with :
158+ name : linux-release
159+ path : target/release/usb_mirror_sync-linux-*-v${{ needs.metadata.outputs.version }}.tar.gz
160+
161+ release :
162+ needs :
163+ - metadata
164+ - build-windows
165+ - build-macos
166+ - build-linux
167+ if : needs.metadata.outputs.mode != 'skip'
168+ runs-on : ubuntu-latest
169+ env :
170+ GH_TOKEN : ${{ github.token }}
171+ steps :
172+ - name : Download artifacts
173+ uses : actions/download-artifact@v4
174+ with :
175+ path : release-artifacts
74176
75177 - name : Generate release notes
76178 id : notes
77- if : steps.tag_state.outputs.mode != 'skip'
78- shell : pwsh
79- env :
80- GH_TOKEN : ${{ github.token }}
179+ shell : bash
81180 run : |
82- $tag = "v${{ steps.version.outputs.version }}"
83- $notesJson = gh api "repos/$env:GITHUB_REPOSITORY/releases/generate-notes" -X POST -f tag_name="$tag" -f target_commitish="$env:GITHUB_SHA"
84- $notes = ($notesJson | ConvertFrom-Json).body
85- $notesPath = Join-Path $env:RUNNER_TEMP "release-notes.md"
86- Set-Content -LiteralPath $notesPath -Value $notes -NoNewline
87- "path=$notesPath" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
181+ tag="v${{ needs.metadata.outputs.version }}"
182+ gh api "repos/$GITHUB_REPOSITORY/releases/generate-notes" -X POST -f tag_name="$tag" -f target_commitish="$GITHUB_SHA" --jq .body > release-notes.md
183+ echo "path=$PWD/release-notes.md" >> "$GITHUB_OUTPUT"
88184
89185 - name : Create or update draft release
90- if : steps.tag_state.outputs.mode != 'skip'
91- shell : pwsh
92- env :
93- GH_TOKEN : ${{ github.token }}
186+ shell : bash
94187 run : |
95- $tag = "v${{ steps.version.outputs.version }}"
96- $notesPath = "${{ steps.notes.outputs.path }}"
97-
98- & gh release view $tag --json tagName *> $null
99- $releaseExists = $LASTEXITCODE -eq 0
100- $global:LASTEXITCODE = 0
101-
102- if ($releaseExists) {
103- & gh release edit $tag --draft --title $tag --notes-file $notesPath
104- } else {
105- & gh release create $tag --draft --title $tag --target $env:GITHUB_SHA --notes-file $notesPath
106- }
107-
108- if ($LASTEXITCODE -ne 0) {
109- throw "Failed to create or update draft release."
110- }
188+ tag="v${{ needs.metadata.outputs.version }}"
189+ if gh release view "$tag" --json tagName >/dev/null 2>&1; then
190+ gh release edit "$tag" --draft --title "$tag" --notes-file "${{ steps.notes.outputs.path }}"
191+ else
192+ gh release create "$tag" --draft --title "$tag" --target "$GITHUB_SHA" --notes-file "${{ steps.notes.outputs.path }}"
193+ fi
111194
112195 - name : Upload release assets
113- if : steps.tag_state.outputs.mode != 'skip'
114- shell : pwsh
115- env :
116- GH_TOKEN : ${{ github.token }}
196+ shell : bash
117197 run : |
118- $version = "${{ steps.version.outputs.version }}"
119- $tag = "v$version"
120- $releaseDir = Join-Path $env:GITHUB_WORKSPACE "target\release"
121- $portable = Join-Path $releaseDir "usb_mirror_sync-portable-v$version.zip"
122- $installer = Join-Path $releaseDir "usb_mirror_sync-setup-v$version.exe"
123-
124- & gh release upload $tag $portable $installer --clobber
125- if ($LASTEXITCODE -ne 0) {
126- throw "Failed to upload release assets."
127- }
198+ tag="v${{ needs.metadata.outputs.version }}"
199+ find release-artifacts -type f -print0 | xargs -0 gh release upload "$tag" --clobber
0 commit comments