Skip to content

Commit fc7ad8f

Browse files
committed
Add cross-platform runtime foundation
1 parent fbc895c commit fc7ad8f

14 files changed

Lines changed: 2580 additions & 261 deletions

.github/workflows/release.yml

Lines changed: 151 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -14,114 +14,186 @@ concurrency:
1414
cancel-in-progress: false
1515

1616
jobs:
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

Comments
 (0)