Skip to content

Commit 797078e

Browse files
sharpninjaCopilot
andcommitted
Add MSIX/deb packaging, shared logo, and multi-platform CI
- Add MsixTools as git submodule for Windows MSIX package generation - Create msix.yml config for RequestTracker Desktop MSIX builds - Create scripts/build-msix.ps1 wrapper for local MSIX builds - Create scripts/build-deb.sh for Linux .deb package generation - Generate shared square logo (SVG source + PNG at all sizes + ICO) - Add Android mipmap icons at all density buckets - Add F-Droid repo icon and update landing page with logo - Update CI workflow to build Android APK, Windows MSIX, and Linux deb in parallel, then create a single release with all packages - Add global.json to pin .NET 9 SDK - Fix Android csproj: add OutputType=Exe, disable trimming and AOT Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9d9996c commit 797078e

32 files changed

Lines changed: 447 additions & 106 deletions

File tree

.github/scripts/generate_fdroid_repo.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def generate_index_v1(args):
3030
"repo": {
3131
"name": "RequestTracker F-Droid Repo",
3232
"description": "F-Droid repository for the RequestTracker Android application.",
33-
"icon": "",
33+
"icon": "icons/com.requesttracker.android.128.png",
3434
"address": args.repo_url,
3535
"timestamp": timestamp,
3636
"version": 21,
@@ -51,6 +51,7 @@ def generate_index_v1(args):
5151
"sourceCode": "https://github.com/sharpninja/RequestTracker",
5252
"issueTracker": "https://github.com/sharpninja/RequestTracker/issues",
5353
"categories": ["Development", "System"],
54+
"icon": "icons/com.requesttracker.android.128.png",
5455
"suggestedVersionName": args.version,
5556
"suggestedVersionCode": args.version_code,
5657
}
Lines changed: 165 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build Android APK & Publish F-Droid Repo
1+
name: Build & Release
22

33
on:
44
push:
@@ -15,67 +15,63 @@ concurrency:
1515
cancel-in-progress: false
1616

1717
jobs:
18-
build-and-release:
18+
# ── Shared version detection ──────────────────────────────────────────────
19+
version:
1920
runs-on: ubuntu-latest
2021
outputs:
2122
semver: ${{ steps.gitversion.outputs.semVer }}
23+
major: ${{ steps.gitversion.outputs.major }}
24+
minor: ${{ steps.gitversion.outputs.minor }}
25+
patch: ${{ steps.gitversion.outputs.patch }}
26+
commits: ${{ steps.gitversion.outputs.commitsSinceVersionSource }}
2227
version-code: ${{ steps.version-code.outputs.code }}
23-
apk-url: ${{ steps.upload-release.outputs.apk-url }}
24-
apk-hash: ${{ steps.apk-hash.outputs.sha256 }}
25-
apk-size: ${{ steps.apk-hash.outputs.size }}
2628
steps:
27-
- name: Checkout
28-
uses: actions/checkout@v4
29-
with:
30-
fetch-depth: 0 # Full history for GitVersion
31-
32-
- name: Setup .NET
33-
uses: actions/setup-dotnet@v4
29+
- uses: actions/checkout@v4
3430
with:
35-
dotnet-version: '9.0.x'
36-
37-
- name: Install Android workload
38-
run: dotnet workload install android
31+
fetch-depth: 0
3932

40-
- name: Install GitVersion
41-
uses: gittools/actions/gitversion/setup@v3.2.1
33+
- uses: gittools/actions/gitversion/setup@v3.2.1
4234
with:
4335
versionSpec: '6.x'
4436

45-
- name: Determine version
46-
id: gitversion
37+
- id: gitversion
4738
uses: gittools/actions/gitversion/execute@v3.2.1
4839
with:
4940
useConfigFile: false
5041

51-
- name: Display version
52-
run: |
53-
echo "SemVer: ${{ steps.gitversion.outputs.semVer }}"
54-
echo "FullSemVer: ${{ steps.gitversion.outputs.fullSemVer }}"
55-
echo "Major: ${{ steps.gitversion.outputs.major }}"
56-
echo "Minor: ${{ steps.gitversion.outputs.minor }}"
57-
echo "Patch: ${{ steps.gitversion.outputs.patch }}"
58-
echo "CommitsSinceVersionSource: ${{ steps.gitversion.outputs.commitsSinceVersionSource }}"
59-
60-
- name: Calculate version code
42+
- name: Calculate Android version code
6143
id: version-code
6244
run: |
63-
MAJOR=${{ steps.gitversion.outputs.major }}
64-
MINOR=${{ steps.gitversion.outputs.minor }}
65-
PATCH=${{ steps.gitversion.outputs.patch }}
66-
COMMITS=${{ steps.gitversion.outputs.commitsSinceVersionSource }}
67-
# version code = major*10000 + minor*1000 + patch*100 + commits
68-
CODE=$(( MAJOR * 10000 + MINOR * 1000 + PATCH * 100 + COMMITS ))
45+
CODE=$(( ${{ steps.gitversion.outputs.major }} * 10000 + ${{ steps.gitversion.outputs.minor }} * 1000 + ${{ steps.gitversion.outputs.patch }} * 100 + ${{ steps.gitversion.outputs.commitsSinceVersionSource }} ))
6946
if [ "$CODE" -lt 1 ]; then CODE=1; fi
7047
echo "code=$CODE" >> "$GITHUB_OUTPUT"
71-
echo "Version code: $CODE"
48+
49+
- run: echo "SemVer=${{ steps.gitversion.outputs.semVer }} VersionCode=${{ steps.version-code.outputs.code }}"
50+
51+
# ── Android APK ───────────────────────────────────────────────────────────
52+
build-android:
53+
needs: version
54+
runs-on: ubuntu-latest
55+
outputs:
56+
apk-hash: ${{ steps.apk-hash.outputs.sha256 }}
57+
apk-size: ${{ steps.apk-hash.outputs.size }}
58+
steps:
59+
- uses: actions/checkout@v4
60+
with:
61+
fetch-depth: 0
62+
submodules: true
63+
64+
- uses: actions/setup-dotnet@v4
65+
with:
66+
dotnet-version: '9.0.x'
67+
68+
- run: dotnet workload install android
7269

7370
- name: Decode keystore
7471
if: env.KEYSTORE_BASE64 != ''
7572
env:
7673
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
77-
run: |
78-
echo "$KEYSTORE_BASE64" | base64 -d > ${{ github.workspace }}/release.keystore
74+
run: echo "$KEYSTORE_BASE64" | base64 -d > ${{ github.workspace }}/release.keystore
7975

8076
- name: Build Android APK
8177
run: |
@@ -84,119 +80,185 @@ jobs:
8480
SIGNING_ARGS="/p:AndroidSigningKeyStore=${{ github.workspace }}/release.keystore /p:AndroidSigningKeyAlias=${{ secrets.ANDROID_KEY_ALIAS }} /p:AndroidSigningStorePass=${{ secrets.ANDROID_KEYSTORE_PASSWORD }} /p:AndroidSigningKeyPass=${{ secrets.ANDROID_KEY_PASSWORD }}"
8581
fi
8682
dotnet publish src/RequestTracker.Android/RequestTracker.Android.csproj \
87-
-c Release \
88-
-f net9.0-android \
89-
/p:ApplicationVersion=${{ steps.version-code.outputs.code }} \
90-
/p:ApplicationDisplayVersion=${{ steps.gitversion.outputs.semVer }} \
83+
-c Release -f net9.0-android \
84+
/p:ApplicationVersion=${{ needs.version.outputs.version-code }} \
85+
/p:ApplicationDisplayVersion=${{ needs.version.outputs.semver }} \
9186
$SIGNING_ARGS
9287
9388
- name: Find and rename APK
9489
id: find-apk
9590
run: |
96-
echo "=== All output files ==="
97-
find src/RequestTracker.Android/bin/Release -type f -name "*.apk" -o -name "*.aab" | sort
98-
echo "=== Searching for APK ==="
99-
# Search for signed APK first, then any APK
10091
APK=$(find src/RequestTracker.Android/bin/Release -name "*-Signed.apk" | head -1)
92+
[ -z "$APK" ] && APK=$(find src/RequestTracker.Android/bin/Release -name "*.apk" | head -1)
10193
if [ -z "$APK" ]; then
102-
APK=$(find src/RequestTracker.Android/bin/Release -name "*.apk" | head -1)
103-
fi
104-
if [ -z "$APK" ]; then
105-
echo "ERROR: No APK found! Full file listing:"
106-
find src/RequestTracker.Android/bin/Release -type f
107-
exit 1
94+
echo "ERROR: No APK found!"; find src/RequestTracker.Android/bin/Release -type f; exit 1
10895
fi
109-
DEST="src/RequestTracker.Android/bin/Release/RequestTracker-${{ steps.gitversion.outputs.semVer }}.apk"
96+
DEST="artifacts/RequestTracker-${{ needs.version.outputs.semver }}.apk"
97+
mkdir -p artifacts
11098
cp "$APK" "$DEST"
11199
echo "apk-path=$DEST" >> "$GITHUB_OUTPUT"
112-
echo "Found APK: $APK -> $DEST"
113100
114-
- name: Compute APK hash and size
101+
- name: Compute APK hash
115102
id: apk-hash
116103
run: |
117-
APK="${{ steps.find-apk.outputs.apk-path }}"
118-
SHA256=$(sha256sum "$APK" | awk '{print $1}')
119-
SIZE=$(stat -c%s "$APK")
104+
SHA256=$(sha256sum "${{ steps.find-apk.outputs.apk-path }}" | awk '{print $1}')
105+
SIZE=$(stat -c%s "${{ steps.find-apk.outputs.apk-path }}")
120106
echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"
121107
echo "size=$SIZE" >> "$GITHUB_OUTPUT"
122-
echo "APK SHA-256: $SHA256"
123-
echo "APK size: $SIZE bytes"
108+
109+
- uses: actions/upload-artifact@v4
110+
with:
111+
name: android-apk
112+
path: artifacts/*.apk
113+
114+
# ── Windows MSIX ──────────────────────────────────────────────────────────
115+
build-windows:
116+
needs: version
117+
runs-on: windows-latest
118+
steps:
119+
- uses: actions/checkout@v4
120+
with:
121+
fetch-depth: 0
122+
submodules: true
123+
124+
- uses: actions/setup-dotnet@v4
125+
with:
126+
dotnet-version: '9.0.x'
127+
128+
- name: Build MSIX package
129+
shell: pwsh
130+
run: |
131+
Import-Module ./scripts/MsixTools/MsixTools.psd1 -Force
132+
New-MsixPackage `
133+
-WorkspaceRoot . `
134+
-ConfigPath msix.yml `
135+
-Version "${{ needs.version.outputs.semver }}" `
136+
-ExcludeService `
137+
-DevCert `
138+
-Force
139+
140+
- name: Rename MSIX
141+
shell: pwsh
142+
run: |
143+
$msix = Get-ChildItem artifacts -Filter '*.msix' | Select-Object -First 1
144+
$dest = "artifacts/RequestTracker-${{ needs.version.outputs.semver }}-win-x64.msix"
145+
if ($msix.FullName -ne (Resolve-Path $dest -ErrorAction SilentlyContinue)) {
146+
Move-Item $msix.FullName $dest -Force
147+
}
148+
Write-Host "MSIX: $dest"
149+
150+
- uses: actions/upload-artifact@v4
151+
with:
152+
name: windows-msix
153+
path: artifacts/*.msix
154+
155+
# ── Linux DEB ─────────────────────────────────────────────────────────────
156+
build-linux:
157+
needs: version
158+
runs-on: ubuntu-latest
159+
steps:
160+
- uses: actions/checkout@v4
161+
with:
162+
fetch-depth: 0
163+
submodules: true
164+
165+
- uses: actions/setup-dotnet@v4
166+
with:
167+
dotnet-version: '9.0.x'
168+
169+
- name: Build DEB package
170+
run: |
171+
chmod +x scripts/build-deb.sh
172+
scripts/build-deb.sh --version "${{ needs.version.outputs.semver }}"
173+
174+
- uses: actions/upload-artifact@v4
175+
with:
176+
name: linux-deb
177+
path: artifacts/*.deb
178+
179+
# ── GitHub Release ────────────────────────────────────────────────────────
180+
release:
181+
needs: [version, build-android, build-windows, build-linux]
182+
runs-on: ubuntu-latest
183+
outputs:
184+
apk-url: ${{ steps.urls.outputs.apk-url }}
185+
steps:
186+
- uses: actions/download-artifact@v4
187+
with:
188+
path: release-assets
189+
merge-multiple: true
190+
191+
- name: List release assets
192+
run: ls -lh release-assets/
124193

125194
- name: Create GitHub Release
126-
id: create-release
127195
uses: softprops/action-gh-release@v2
128196
with:
129-
tag_name: v${{ steps.gitversion.outputs.semVer }}
130-
name: RequestTracker v${{ steps.gitversion.outputs.semVer }}
197+
tag_name: v${{ needs.version.outputs.semver }}
198+
name: RequestTracker v${{ needs.version.outputs.semver }}
131199
body: |
132-
## RequestTracker v${{ steps.gitversion.outputs.semVer }}
200+
## RequestTracker v${{ needs.version.outputs.semver }}
201+
202+
### Downloads
203+
| Platform | File |
204+
|----------|------|
205+
| 🤖 Android | `RequestTracker-${{ needs.version.outputs.semver }}.apk` |
206+
| 🪟 Windows | `RequestTracker-${{ needs.version.outputs.semver }}-win-x64.msix` |
207+
| 🐧 Linux | `requesttracker_${{ needs.version.outputs.semver }}_amd64.deb` |
133208
134209
### Android APK
135-
- **Version**: ${{ steps.gitversion.outputs.semVer }}
136-
- **Version Code**: ${{ steps.version-code.outputs.code }}
137-
- **SHA-256**: `${{ steps.apk-hash.outputs.sha256 }}`
210+
- **Version Code**: ${{ needs.version.outputs.version-code }}
211+
- **SHA-256**: `${{ needs.build-android.outputs.apk-hash }}`
138212
139213
### Install via F-Droid
140-
Add this repo URL to your F-Droid client:
141214
```
142215
https://sharpninja.github.io/RequestTracker/fdroid/repo
143216
```
217+
218+
### Install on Linux
219+
```bash
220+
sudo dpkg -i requesttracker_${{ needs.version.outputs.semver }}_amd64.deb
221+
```
144222
draft: false
145-
prerelease: ${{ contains(steps.gitversion.outputs.semVer, '-') }}
146-
files: ${{ steps.find-apk.outputs.apk-path }}
223+
prerelease: ${{ contains(needs.version.outputs.semver, '-') }}
224+
files: release-assets/*
147225

148-
- name: Get APK release asset URL
149-
id: upload-release
226+
- name: Compute release URLs
227+
id: urls
150228
run: |
151-
# Get the direct download URL for the APK asset from the release
152-
TAG="v${{ steps.gitversion.outputs.semVer }}"
153-
APK_NAME="RequestTracker-${{ steps.gitversion.outputs.semVer }}.apk"
154-
APK_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}/${APK_NAME}"
155-
echo "apk-url=$APK_URL" >> "$GITHUB_OUTPUT"
156-
echo "APK release URL: $APK_URL"
229+
TAG="v${{ needs.version.outputs.semver }}"
230+
echo "apk-url=https://github.com/${{ github.repository }}/releases/download/${TAG}/RequestTracker-${{ needs.version.outputs.semver }}.apk" >> "$GITHUB_OUTPUT"
157231
232+
# ── F-Droid repo deployment ───────────────────────────────────────────────
158233
deploy-fdroid:
159-
needs: build-and-release
234+
needs: [version, build-android, release]
160235
runs-on: ubuntu-latest
161236
environment:
162237
name: github-pages
163238
url: ${{ steps.deployment.outputs.page_url }}
164239
steps:
165-
- name: Checkout
166-
uses: actions/checkout@v4
240+
- uses: actions/checkout@v4
167241

168-
- name: Setup Python
169-
uses: actions/setup-python@v5
242+
- uses: actions/setup-python@v5
170243
with:
171244
python-version: '3.12'
172245

173246
- name: Generate F-Droid repo metadata
174247
run: |
175248
python .github/scripts/generate_fdroid_repo.py \
176-
--version "${{ needs.build-and-release.outputs.semver }}" \
177-
--version-code "${{ needs.build-and-release.outputs.version-code }}" \
178-
--apk-url "${{ needs.build-and-release.outputs.apk-url }}" \
179-
--apk-hash "${{ needs.build-and-release.outputs.apk-hash }}" \
180-
--apk-size "${{ needs.build-and-release.outputs.apk-size }}" \
249+
--version "${{ needs.version.outputs.semver }}" \
250+
--version-code "${{ needs.version.outputs.version-code }}" \
251+
--apk-url "${{ needs.release.outputs.apk-url }}" \
252+
--apk-hash "${{ needs.build-android.outputs.apk-hash }}" \
253+
--apk-size "${{ needs.build-android.outputs.apk-size }}" \
181254
--repo-url "https://sharpninja.github.io/RequestTracker/fdroid/repo" \
182255
--output-dir docs/fdroid/repo
183256
184-
- name: Show generated metadata
185-
run: |
186-
echo "=== index-v1.json ==="
187-
cat docs/fdroid/repo/index-v1.json
188-
echo ""
189-
echo "=== entry.json ==="
190-
cat docs/fdroid/repo/entry.json
191-
192-
- name: Setup Pages
193-
uses: actions/configure-pages@v5
257+
- uses: actions/configure-pages@v5
194258

195-
- name: Upload Pages artifact
196-
uses: actions/upload-pages-artifact@v3
259+
- uses: actions/upload-pages-artifact@v3
197260
with:
198261
path: docs/fdroid
199262

200-
- name: Deploy to GitHub Pages
201-
id: deployment
263+
- id: deployment
202264
uses: actions/deploy-pages@v4

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "lib/Markdown.Avalonia"]
22
path = lib/Markdown.Avalonia
33
url = https://github.com/whistyun/Markdown.Avalonia.git
4+
[submodule "scripts/MsixTools"]
5+
path = scripts/MsixTools
6+
url = https://github.com/sharpninja/MsixTools.git

docs/fdroid/icon.png

8.46 KB
Loading

docs/fdroid/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
</style>
1515
</head>
1616
<body>
17-
<h1>📱 RequestTracker F-Droid Repository</h1>
17+
<h1><img src="icon.png" alt="RequestTracker" width="48" height="48" style="vertical-align:middle; margin-right:12px;">RequestTracker F-Droid Repository</h1>
1818
<p>This is the F-Droid repository for the <a href="https://github.com/sharpninja/RequestTracker">RequestTracker</a> Android application.</p>
1919

2020
<h2>Add this repository to F-Droid</h2>
3.49 KB
Loading

global.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"sdk": {
3+
"version": "9.0.114",
4+
"rollForward": "latestFeature",
5+
"allowPrerelease": false
6+
}
7+
}

0 commit comments

Comments
 (0)