Skip to content

Commit 61d0ac9

Browse files
committed
Feature/app distribution with package manager (#170)
* Add workflow job for updating Brew and Scoop repos with meta data for new Avalonia Desktop app release. * Change desktop apps to expect appsettings.json in same directory as executable. * Embed icon to Avalonia Desktop app * Update dpc
1 parent 217be7a commit 61d0ac9

9 files changed

Lines changed: 290 additions & 30 deletions

File tree

.github/workflows/release-desktop-apps.yml

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ on:
99
# Allow manual triggering of the workflow for testing (artifacts will be uploaded to the workflow run, not a release))
1010
workflow_dispatch:
1111

12-
permissions:
13-
contents: write
14-
1512
jobs:
1613
build-and-upload:
1714
runs-on: ubuntu-latest
15+
permissions:
16+
contents: write
1817

1918
strategy:
2019
fail-fast: false
@@ -84,3 +83,124 @@ jobs:
8483
DotNet6502-Avalonia-${{ matrix.runtime }}.zip
8584
DotNet6502-SadConsole-${{ matrix.runtime }}.zip
8685
checksums-${{ matrix.runtime }}.sha256
86+
87+
update-package-managers:
88+
if: github.event_name == 'release'
89+
needs: build-and-upload
90+
runs-on: ubuntu-latest
91+
steps:
92+
- name: Extract version from tag
93+
id: version
94+
run: |
95+
TAG="${{ github.event.release.tag_name }}"
96+
VERSION="${TAG#v}"
97+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
98+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
99+
100+
- name: Download checksum files from release
101+
env:
102+
GH_TOKEN: ${{ github.token }}
103+
run: |
104+
for runtime in osx-arm64 linux-x64 linux-arm64 win-x64 win-arm64; do
105+
gh release download "${{ github.event.release.tag_name }}" \
106+
--repo ${{ github.repository }} \
107+
--pattern "checksums-${runtime}.sha256"
108+
done
109+
110+
- name: Extract SHA256 hashes
111+
id: hashes
112+
run: |
113+
extract_hash() {
114+
grep "DotNet6502-Avalonia-${1}.zip" "checksums-${1}.sha256" | awk '{print $1}'
115+
}
116+
echo "osx_arm64=$(extract_hash osx-arm64)" >> "$GITHUB_OUTPUT"
117+
echo "linux_x64=$(extract_hash linux-x64)" >> "$GITHUB_OUTPUT"
118+
echo "linux_arm64=$(extract_hash linux-arm64)" >> "$GITHUB_OUTPUT"
119+
echo "win_x64=$(extract_hash win-x64)" >> "$GITHUB_OUTPUT"
120+
echo "win_arm64=$(extract_hash win-arm64)" >> "$GITHUB_OUTPUT"
121+
122+
- name: Update Homebrew tap
123+
env:
124+
GH_TOKEN: ${{ secrets.PACKAGE_MANAGER_TOKEN }}
125+
VERSION: ${{ steps.version.outputs.version }}
126+
OSX_ARM64_HASH: ${{ steps.hashes.outputs.osx_arm64 }}
127+
LINUX_X64_HASH: ${{ steps.hashes.outputs.linux_x64 }}
128+
LINUX_ARM64_HASH: ${{ steps.hashes.outputs.linux_arm64 }}
129+
run: |
130+
git clone "https://x-access-token:${GH_TOKEN}@github.com/highbyte/homebrew-dotnet-6502.git"
131+
cd homebrew-dotnet-6502
132+
133+
# Update Cask (macOS)
134+
sed -i "s/version \".*\"/version \"${VERSION}\"/" Casks/dotnet-6502.rb
135+
sed -i "s/sha256 \".*\"/sha256 \"${OSX_ARM64_HASH}\"/" Casks/dotnet-6502.rb
136+
137+
# Update Formula (Linux) - use Python for multi-hash replacement
138+
python3 <<'PYEOF'
139+
import re, os
140+
141+
version = os.environ["VERSION"]
142+
linux_x64_hash = os.environ["LINUX_X64_HASH"]
143+
linux_arm64_hash = os.environ["LINUX_ARM64_HASH"]
144+
145+
with open("Formula/dotnet-6502.rb", "r") as f:
146+
content = f.read()
147+
148+
content = re.sub(r'version ".*?"', f'version "{version}"', content)
149+
150+
# Replace hashes in order: first occurrence is x64, second is arm64
151+
hashes = [linux_x64_hash, linux_arm64_hash]
152+
idx = [0]
153+
def replace_hash(m):
154+
if idx[0] < len(hashes):
155+
h = hashes[idx[0]]
156+
idx[0] += 1
157+
return f'sha256 "{h}"'
158+
return m.group(0)
159+
content = re.sub(r'sha256 ".*?"', replace_hash, content)
160+
161+
with open("Formula/dotnet-6502.rb", "w") as f:
162+
f.write(content)
163+
PYEOF
164+
165+
git config user.name "github-actions[bot]"
166+
git config user.email "github-actions[bot]@users.noreply.github.com"
167+
git add -A
168+
git diff --cached --quiet || { git commit -m "Update to ${VERSION}" && git push; }
169+
170+
- name: Update Scoop bucket
171+
env:
172+
GH_TOKEN: ${{ secrets.PACKAGE_MANAGER_TOKEN }}
173+
VERSION: ${{ steps.version.outputs.version }}
174+
TAG: ${{ steps.version.outputs.tag }}
175+
WIN_X64_HASH: ${{ steps.hashes.outputs.win_x64 }}
176+
WIN_ARM64_HASH: ${{ steps.hashes.outputs.win_arm64 }}
177+
run: |
178+
git clone "https://x-access-token:${GH_TOKEN}@github.com/highbyte/scoop-dotnet-6502.git"
179+
cd scoop-dotnet-6502
180+
181+
python3 <<'PYEOF'
182+
import json, os
183+
184+
version = os.environ["VERSION"]
185+
tag = os.environ["TAG"]
186+
win_x64_hash = os.environ["WIN_X64_HASH"]
187+
win_arm64_hash = os.environ["WIN_ARM64_HASH"]
188+
189+
with open("bucket/dotnet-6502.json", "r") as f:
190+
manifest = json.load(f)
191+
192+
manifest["version"] = version
193+
manifest["architecture"]["64bit"]["url"] = f"https://github.com/highbyte/dotnet-6502/releases/download/{tag}/DotNet6502-Avalonia-win-x64.zip"
194+
manifest["architecture"]["64bit"]["hash"] = win_x64_hash
195+
manifest["architecture"]["arm64"]["url"] = f"https://github.com/highbyte/dotnet-6502/releases/download/{tag}/DotNet6502-Avalonia-win-arm64.zip"
196+
manifest["architecture"]["arm64"]["hash"] = win_arm64_hash
197+
198+
with open("bucket/dotnet-6502.json", "w") as f:
199+
json.dump(manifest, f, indent=4)
200+
f.write("\n")
201+
PYEOF
202+
203+
git config user.name "github-actions[bot]"
204+
git config user.email "github-actions[bot]@users.noreply.github.com"
205+
git add -A
206+
git diff --cached --quiet || { git commit -m "Update to ${VERSION}" && git push; }

doc/DESKTOP_APPS.md

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,82 @@ The emulator has front-ends written with different technologies, and have somewh
1212
| **SadConsole** | Cross-platform desktop console-style app using SadConsole library. See details [here](APPS_SADCONSOLE.md). |
1313
| **SilkNetNative** | Cross-platform desktop app using Silk.NET + SkiaSharp + shaders for rendering. See details [here](APPS_SILKNET_NATIVE.md). |
1414

15-
## Download
15+
## Install via Package Manager
16+
17+
The **Avalonia** desktop app can be installed via package managers for a simpler experience.
18+
19+
**Prerequisites:** Install [Homebrew](https://brew.sh/) (macOS/Linux) or [Scoop](https://scoop.sh/) (Windows) if you don't have them already.
20+
21+
### macOS (Homebrew)
22+
23+
```bash
24+
brew tap highbyte/dotnet-6502
25+
brew install --cask dotnet-6502
26+
```
27+
28+
### Linux (Homebrew)
29+
30+
```bash
31+
brew tap highbyte/dotnet-6502
32+
brew install --formula dotnet-6502
33+
```
34+
35+
### Windows (Scoop)
36+
37+
```powershell
38+
scoop bucket add dotnet-6502 https://github.com/highbyte/scoop-dotnet-6502
39+
scoop install dotnet-6502
40+
```
41+
42+
### Launching
43+
44+
After installing via a package manager, run the emulator from a terminal:
45+
46+
```sh
47+
dotnet-6502
48+
```
49+
50+
On macOS, the app is also installed to `/Applications` and can be launched from Launchpad, Spotlight, or Finder like any other Mac app.
51+
52+
On Windows (Scoop), a Start Menu shortcut **DotNet6502 Emulator** is also created.
53+
54+
### Updating
55+
56+
```bash
57+
# macOS
58+
brew update && brew upgrade --cask dotnet-6502
59+
60+
# Linux
61+
brew update && brew upgrade --formula dotnet-6502
62+
```
63+
64+
```powershell
65+
# Windows
66+
scoop update
67+
scoop update dotnet-6502
68+
```
69+
70+
### Uninstalling
71+
72+
```bash
73+
# macOS
74+
brew uninstall --cask dotnet-6502
75+
brew untap highbyte/dotnet-6502
76+
77+
# Linux
78+
brew uninstall --formula dotnet-6502
79+
brew untap highbyte/dotnet-6502
80+
```
81+
82+
```powershell
83+
# Windows
84+
scoop uninstall dotnet-6502
85+
scoop bucket rm dotnet-6502
86+
```
87+
88+
---
89+
90+
## Install via manual download
1691

1792
Download the latest release for your platform from the [Releases](https://github.com/highbyte/dotnet-6502/releases) page under Assets.
1893

@@ -24,22 +99,14 @@ Download the latest release for your platform from the [Releases](https://github
2499
| Linux ARM64 | `DotNet6502-*-linux-arm64.zip` |
25100
| macOS ARM64 (Apple Silicon) | `DotNet6502-*-osx-arm64.zip` |
26101

27-
---
28-
### Prerequisites, compatibility, and troubleshooting
29-
[Avalonia desktop app](APPS_AVALONIA_TROUBLESHOOT.md)
102+
### Launching the Application
30103

31-
[Silk.NET desktop app](APPS_SILKNET_NATIVE_TROUBLESHOOT.md)
32-
33-
[SadConsole desktop app](APPS_SADCONSOLE_TROUBLESHOOT.md)
34-
35-
## Launching the Application
36-
37-
### Windows
104+
#### Windows
38105

39106
1. Extract the `.zip` file to a folder
40107
2. Double-click the `.exe` file to run
41108

42-
#### SmartScreen Warning
109+
##### SmartScreen Warning
43110

44111
Since the application is not code-signed, Windows SmartScreen may show a warning:
45112

@@ -53,7 +120,7 @@ This warning only appears the first time you run the application.
53120

54121
---
55122

56-
### Linux
123+
#### Linux
57124

58125
1. Extract the `.zip` file:
59126
```sh
@@ -70,7 +137,7 @@ No security warnings are typically shown on Linux.
70137

71138
---
72139

73-
### macOS
140+
#### macOS
74141

75142
> **Note:** The macOS build is not notarized with Apple. It must be run from Terminal.
76143
@@ -91,32 +158,41 @@ No security warnings are typically shown on Linux.
91158
./Highbyte.DotNet6502.App.Avalonia.Desktop
92159
```
93160

94-
#### Why can't I double-click to run?
161+
##### Why can't I double-click to run?
95162

96163
macOS Gatekeeper blocks unsigned/non-notarized applications from running via Finder. Running from Terminal with the `xattr -cr .` command removes the quarantine flag and allows execution.
97164

98165
---
99166

100-
## Verifying Download Integrity (Optional)
167+
### Verifying Download Integrity (Optional)
101168

102169
Each release includes SHA256 checksum files (`checksums-*.sha256`) to verify your download hasn't been corrupted or tampered with.
103170

104-
### Windows (PowerShell)
171+
#### Windows (PowerShell)
105172

106173
```powershell
107174
(Get-FileHash -Algorithm SHA256 DotNet6502-Avalonia-win-x64.zip).Hash.ToLower()
108175
```
109176

110-
### Linux
177+
#### Linux
111178

112179
```sh
113180
sha256sum DotNet6502-Avalonia-linux-x64.zip
114181
```
115182

116-
### macOS
183+
#### macOS
117184

118185
```sh
119186
shasum -a 256 DotNet6502-Avalonia-osx-arm64.zip
120187
```
121188

122189
Compare the output with the corresponding entry in the `checksums-*.sha256` file.
190+
191+
---
192+
193+
## Prerequisites, compatibility, and troubleshooting
194+
[Avalonia desktop app](APPS_AVALONIA_TROUBLESHOOT.md)
195+
196+
[Silk.NET desktop app](APPS_SILKNET_NATIVE_TROUBLESHOOT.md)
197+
198+
[SadConsole desktop app](APPS_SADCONSOLE_TROUBLESHOOT.md)
Binary file not shown.

src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Desktop/Highbyte.DotNet6502.App.Avalonia.Desktop.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
<PropertyGroup>
1414
<ApplicationManifest>app.manifest</ApplicationManifest>
15+
<ApplicationIcon>..\Highbyte.DotNet6502.App.Avalonia.Core\Assets\favicon.ico</ApplicationIcon>
1516
</PropertyGroup>
1617

1718
<ItemGroup>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleName</key>
6+
<string>DotNet6502</string>
7+
<key>CFBundleDisplayName</key>
8+
<string>DotNet6502 Emulator</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>com.highbyte.dotnet6502</string>
11+
<key>CFBundleVersion</key>
12+
<string>1.0.0</string>
13+
<key>CFBundleShortVersionString</key>
14+
<string>1.0.0</string>
15+
<key>CFBundlePackageType</key>
16+
<string>APPL</string>
17+
<key>CFBundleExecutable</key>
18+
<string>Highbyte.DotNet6502.App.Avalonia.Desktop</string>
19+
<key>CFBundleIconFile</key>
20+
<string>AppIcon</string>
21+
<key>LSMinimumSystemVersion</key>
22+
<string>12.0</string>
23+
<key>NSHighResolutionCapable</key>
24+
<true/>
25+
<key>NSSupportsAutomaticGraphicsSwitching</key>
26+
<true/>
27+
</dict>
28+
</plist>

src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Desktop/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,9 @@ public static int Main(string[] args)
151151
// Get config file
152152
// ----------
153153
WriteBootstrapLog($"Creating configuration object.");
154+
var appDir = AppContext.BaseDirectory;
154155
var builder = new ConfigurationBuilder()
155-
.SetBasePath(Directory.GetCurrentDirectory())
156+
.SetBasePath(appDir)
156157
.AddJsonFile("appsettings.json")
157158
.AddJsonFile("appsettings.Development.json", optional: true);
158159

0 commit comments

Comments
 (0)