Skip to content

Commit 9f8b730

Browse files
committed
add linux release packaging workflow
1 parent 3449e68 commit 9f8b730

8 files changed

Lines changed: 360 additions & 156 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
name: release-linux
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: 'Release version (e.g. 1.2.3)'
11+
required: true
12+
13+
permissions:
14+
contents: write
15+
16+
jobs:
17+
prepare:
18+
runs-on: ubuntu-latest
19+
outputs:
20+
version: ${{ steps.resolve_version.outputs.version }}
21+
steps:
22+
- name: Resolve version
23+
id: resolve_version
24+
shell: bash
25+
run: |
26+
set -euo pipefail
27+
28+
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
29+
version="${{ inputs.version }}"
30+
else
31+
version="${GITHUB_REF_NAME#v}"
32+
fi
33+
34+
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*$ ]]; then
35+
echo "Invalid version: $version" >&2
36+
exit 1
37+
fi
38+
39+
echo "version=$version" >> "$GITHUB_OUTPUT"
40+
41+
package:
42+
runs-on: ubuntu-latest
43+
needs: prepare
44+
strategy:
45+
fail-fast: false
46+
matrix:
47+
arch: [amd64, arm64]
48+
steps:
49+
- uses: actions/checkout@v4
50+
51+
- name: Setup .NET
52+
uses: actions/setup-dotnet@v4
53+
with:
54+
dotnet-version: 8.0.x
55+
56+
- name: Install packaging dependencies
57+
run: |
58+
sudo apt-get update
59+
sudo apt-get install -y ruby ruby-dev build-essential rpm dpkg-dev
60+
sudo gem install --no-document fpm
61+
62+
- name: Build release packages
63+
env:
64+
VERSION: ${{ needs.prepare.outputs.version }}
65+
OUT_DIR: ${{ github.workspace }}/dist
66+
run: |
67+
chmod +x Packaging.Linux/*.sh
68+
./Packaging.Linux/build-release-packages.sh "${{ matrix.arch }}"
69+
70+
- name: Upload package artifacts (${{ matrix.arch }})
71+
uses: actions/upload-artifact@v4
72+
with:
73+
name: linux-packages-${{ matrix.arch }}
74+
path: dist/*
75+
76+
aur-source:
77+
runs-on: ubuntu-latest
78+
needs: prepare
79+
steps:
80+
- uses: actions/checkout@v4
81+
82+
- name: Build AUR source archive
83+
env:
84+
VERSION: ${{ needs.prepare.outputs.version }}
85+
OUT_DIR: ${{ github.workspace }}/dist
86+
run: |
87+
chmod +x Packaging.Linux/*.sh
88+
./Packaging.Linux/build-aur-source.sh
89+
90+
- name: Upload AUR source artifact
91+
uses: actions/upload-artifact@v4
92+
with:
93+
name: linux-packages-aur
94+
path: dist/*
95+
96+
release:
97+
runs-on: ubuntu-latest
98+
needs: [prepare, package, aur-source]
99+
if: startsWith(github.ref, 'refs/tags/')
100+
steps:
101+
- name: Download all artifacts
102+
uses: actions/download-artifact@v4
103+
with:
104+
pattern: linux-packages-*
105+
path: release-assets
106+
merge-multiple: true
107+
108+
- name: Publish GitHub release
109+
uses: softprops/action-gh-release@v2
110+
with:
111+
name: v${{ needs.prepare.outputs.version }}
112+
generate_release_notes: true
113+
files: |
114+
release-assets/*

Packaging.Linux/PKGBUILD

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ package() {
4242
install -d "${pkgdir}/usr/bin"
4343
install -d "${pkgdir}/usr/share/applications"
4444
install -d "${pkgdir}/usr/share/pixmaps"
45-
install -d "${pkgdir}/etc/systemd/system"
45+
install -d "${pkgdir}/usr/lib/systemd/system"
4646
install -d "${pkgdir}/etc/coder-desktop"
4747

4848
cp -a "${srcdir}/build/app/." "${pkgdir}/usr/lib/coder-desktop/app/"
@@ -57,7 +57,7 @@ WRAPPER
5757
ln -sf "/usr/lib/coder-desktop/service/CoderVpnService" "${pkgdir}/usr/bin/coder-vpn-service"
5858

5959
install -m 644 "${srcdir}/coder-desktop.desktop" "${pkgdir}/usr/share/applications/coder-desktop.desktop"
60-
install -m 644 "${srcdir}/coder-desktop.service" "${pkgdir}/etc/systemd/system/coder-desktop.service"
60+
install -m 644 "${srcdir}/coder-desktop.service" "${pkgdir}/usr/lib/systemd/system/coder-desktop.service"
6161
install -m 644 "${repo_root}/App.Avalonia/Assets/coder.ico" "${pkgdir}/usr/share/pixmaps/coder-desktop.ico"
6262

6363
cat > "${pkgdir}/etc/coder-desktop/config.json" <<'CONFIG'

Packaging.Linux/README.md

Lines changed: 36 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,70 @@
11
# Linux Packaging
22

3-
## Debian package (`.deb`)
3+
## Build release artifacts locally
4+
5+
From repo root:
46

57
```bash
6-
# Build .deb for amd64
7-
./build-deb.sh amd64
8+
# Build .deb, .rpm, and .tar.gz for amd64
9+
VERSION=1.0.0 ./Packaging.Linux/build-release-packages.sh amd64
10+
11+
# Build .deb, .rpm, and .tar.gz for arm64
12+
VERSION=1.0.0 ./Packaging.Linux/build-release-packages.sh arm64
813

9-
# Build .deb for arm64
10-
VERSION=1.0.0 ./build-deb.sh arm64
14+
# Build only one format
15+
VERSION=1.0.0 FORMATS=deb ./Packaging.Linux/build-release-packages.sh amd64
1116
```
1217

13-
The output package is written to the repository root as:
18+
Artifacts are written to `./dist/` by default.
19+
20+
Generated formats:
1421

1522
- `coder-desktop_<version>_amd64.deb`
1623
- `coder-desktop_<version>_arm64.deb`
24+
- `coder-desktop-<version>-1.x86_64.rpm`
25+
- `coder-desktop-<version>-1.aarch64.rpm`
26+
- `coder-desktop_<version>_amd64.tar.gz`
27+
- `coder-desktop_<version>_arm64.tar.gz`
1728

18-
## Arch / Manjaro package (`PKGBUILD`)
19-
20-
A sample `PKGBUILD` and `coder-desktop.install` are provided for Arch-based
21-
systems.
29+
## Build AUR source bundle
2230

2331
```bash
24-
cd Packaging.Linux
25-
makepkg -si
32+
VERSION=1.0.0 ./Packaging.Linux/build-aur-source.sh
2633
```
2734

28-
## Local development run helper
35+
Produces:
2936

30-
From repo root, use:
31-
32-
```bash
33-
./scripts/run-linux-dev.sh
34-
```
37+
- `coder-desktop_<version>_aur.tar.gz`
3538

36-
This script:
39+
This archive contains `PKGBUILD`, `coder-desktop.install`, unit/desktop files,
40+
and can be used as a source bundle for AUR packaging.
3741

38-
- builds `Vpn.Service` + `App.Avalonia` (unless `--no-build`)
39-
- starts the service on a user-writable socket
40-
- overrides `Manager:TunnelBinaryPath` to:
41-
- `${XDG_CACHE_HOME:-$HOME/.cache}/coder-desktop-dev/coder-vpn` (default)
42-
- `/tmp/coder-desktop-dev-root/coder-vpn` when `--sudo-service` is used
43-
- sets `Manager:TunnelBinaryAllowVersionMismatch=true` for Linux dev runs because
44-
Windows PE ProductVersion validation does not apply to Linux tunnel binaries
45-
- can run the service via `--sudo-service` (recommended when testing real tunnel setup,
46-
since creating/configuring TUN interfaces typically requires root privileges)
47-
- starts the Avalonia app in visible mode (`--show` by default)
48-
- writes logs under `${XDG_RUNTIME_DIR:-/tmp}/coder-desktop-dev/logs/`
42+
## Debian helper
4943

50-
Useful variants:
44+
`build-deb.sh` is retained as a convenience wrapper:
5145

5246
```bash
53-
./scripts/run-linux-dev.sh --no-build
54-
./scripts/run-linux-dev.sh -- --minimized
55-
./scripts/run-linux-dev.sh --sudo-service
47+
VERSION=1.0.0 ./Packaging.Linux/build-deb.sh amd64
5648
```
5749

58-
The app also writes bootstrap logs to:
50+
## Service behavior (root VPN service)
5951

60-
- `~/.local/state/coder-desktop/app-avalonia.log`
52+
Packages install a systemd unit (`coder-desktop.service`) that runs:
53+
54+
- `User=root`
55+
- `ExecStart=/usr/bin/coder-vpn-service`
56+
57+
Post-install scripts run `systemctl daemon-reload`, `enable`, and `start/restart`
58+
so the VPN service is active as root after installation (when systemd is
59+
available).
6160

6261
## Package contents
6362

6463
- `/usr/lib/coder-desktop/app/` — Avalonia desktop app binaries
6564
- `/usr/lib/coder-desktop/service/` — VPN service binaries
6665
- `/usr/bin/coder-desktop` — Desktop app launcher wrapper
6766
- `/usr/bin/coder-vpn-service` — Symlink to VPN service binary
68-
- `/etc/systemd/system/coder-desktop.service` — systemd unit
67+
- `/usr/lib/systemd/system/coder-desktop.service` — systemd unit
6968
- `/etc/coder-desktop/config.json` — Default configuration
7069
- `/usr/share/applications/coder-desktop.desktop` — Desktop entry
7170
- `/usr/share/pixmaps/coder-desktop.ico` — App icon
72-
73-
## Runtime dependencies
74-
75-
### Debian/Ubuntu package names
76-
77-
- `dotnet-runtime-8.0`
78-
- `libnotify-bin`
79-
- `libsecret-tools`
80-
- `freerdp2-x11` or `remmina` (optional)
81-
82-
### Arch/Manjaro package names
83-
84-
- `dotnet-runtime-8.0`
85-
- `libnotify`
86-
- `libsecret`
87-
- `freerdp` or `remmina` (optional)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Build an AUR source archive containing PKGBUILD + install/unit/desktop assets.
5+
#
6+
# Usage:
7+
# VERSION=1.2.3 ./build-aur-source.sh
8+
9+
VERSION="${VERSION:-0.1.0}"
10+
OUT_DIR="${OUT_DIR:-$(pwd)/dist}"
11+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12+
WORK_DIR="$(mktemp -d)"
13+
AUR_DIR="$WORK_DIR/coder-desktop"
14+
15+
cleanup() {
16+
rm -rf "$WORK_DIR"
17+
}
18+
trap cleanup EXIT
19+
20+
mkdir -p "$OUT_DIR"
21+
mkdir -p "$AUR_DIR"
22+
23+
sed -E "s/^pkgver=.*/pkgver=${VERSION}/" "$SCRIPT_DIR/PKGBUILD" > "$AUR_DIR/PKGBUILD"
24+
cp "$SCRIPT_DIR/coder-desktop.install" "$AUR_DIR/"
25+
cp "$SCRIPT_DIR/coder-desktop.service" "$AUR_DIR/"
26+
cp "$SCRIPT_DIR/coder-desktop.desktop" "$AUR_DIR/"
27+
cp "$SCRIPT_DIR/README.md" "$AUR_DIR/README.md"
28+
29+
ARCHIVE_NAME="coder-desktop_${VERSION}_aur.tar.gz"
30+
tar -C "$WORK_DIR" -czf "$OUT_DIR/$ARCHIVE_NAME" "coder-desktop"
31+
32+
echo "AUR source archive created: $OUT_DIR/$ARCHIVE_NAME"

Packaging.Linux/build-deb.sh

Lines changed: 3 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,11 @@
1-
#!/bin/bash
1+
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
# Build a .deb package for Coder Desktop
4+
# Backwards-compatible wrapper around the multi-format release packaging script.
55
# Usage: ./build-deb.sh [amd64|arm64]
66

77
ARCH="${1:-amd64}"
8-
VERSION="${VERSION:-0.1.0}"
98
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
109
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
11-
BUILD_DIR="$(mktemp -d)"
12-
PKG_DIR="$BUILD_DIR/coder-desktop_${VERSION}_${ARCH}"
1310

14-
echo "Building Coder Desktop .deb package v${VERSION} for ${ARCH}..."
15-
16-
# Map architecture names
17-
case "$ARCH" in
18-
amd64) RID="linux-x64" ;;
19-
arm64) RID="linux-arm64" ;;
20-
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
21-
esac
22-
23-
# Build the desktop app
24-
# Framework-dependent publish keeps package size reasonable.
25-
echo "Publishing App.Avalonia..."
26-
dotnet publish "$ROOT_DIR/App.Avalonia" -r "$RID" -c Release -o "$PKG_DIR/usr/lib/coder-desktop/app"
27-
28-
# Build the VPN service
29-
echo "Publishing Vpn.Service..."
30-
dotnet publish "$ROOT_DIR/Vpn.Service" -r "$RID" -c Release -o "$PKG_DIR/usr/lib/coder-desktop/service"
31-
32-
# Create directory structure
33-
mkdir -p "$PKG_DIR/usr/bin"
34-
mkdir -p "$PKG_DIR/usr/share/applications"
35-
mkdir -p "$PKG_DIR/usr/share/pixmaps"
36-
mkdir -p "$PKG_DIR/etc/systemd/system"
37-
mkdir -p "$PKG_DIR/etc/coder-desktop"
38-
mkdir -p "$PKG_DIR/DEBIAN"
39-
40-
# Launchers/wrappers
41-
cat > "$PKG_DIR/usr/bin/coder-desktop" <<'WRAPPER'
42-
#!/bin/bash
43-
exec "/usr/lib/coder-desktop/app/Coder Desktop" "$@"
44-
WRAPPER
45-
chmod 755 "$PKG_DIR/usr/bin/coder-desktop"
46-
ln -sf "/usr/lib/coder-desktop/service/CoderVpnService" "$PKG_DIR/usr/bin/coder-vpn-service"
47-
48-
# Copy packaging files
49-
cp "$SCRIPT_DIR/coder-desktop.service" "$PKG_DIR/etc/systemd/system/"
50-
cp "$SCRIPT_DIR/coder-desktop.desktop" "$PKG_DIR/usr/share/applications/"
51-
cp "$ROOT_DIR/App.Avalonia/Assets/coder.ico" "$PKG_DIR/usr/share/pixmaps/coder-desktop.ico"
52-
53-
# Default config
54-
cat > "$PKG_DIR/etc/coder-desktop/config.json" <<'CONFIG'
55-
{
56-
"Manager": {
57-
"ServiceRpcSocketPath": "/run/coder-desktop/vpn.sock",
58-
"TunnelBinaryPath": "/usr/lib/coder-desktop/coder-vpn",
59-
"TunnelBinarySignatureSigner": "",
60-
"TunnelBinaryAllowVersionMismatch": false
61-
}
62-
}
63-
CONFIG
64-
65-
# Create DEBIAN control file
66-
cat > "$PKG_DIR/DEBIAN/control" <<CONTROL
67-
Package: coder-desktop
68-
Version: ${VERSION}
69-
Architecture: ${ARCH}
70-
Maintainer: Coder Technologies Inc. <support@coder.com>
71-
Description: Coder Desktop - Connect to Coder workspaces
72-
Provides a VPN tunnel service and Linux desktop tray application
73-
for connecting to Coder workspaces.
74-
Depends: dotnet-runtime-8.0, libnotify-bin, libsecret-tools
75-
Recommends: freerdp2-x11 | remmina
76-
Section: net
77-
Priority: optional
78-
CONTROL
79-
80-
# Install scripts
81-
cp "$SCRIPT_DIR/postinst.sh" "$PKG_DIR/DEBIAN/postinst"
82-
cp "$SCRIPT_DIR/prerm.sh" "$PKG_DIR/DEBIAN/prerm"
83-
chmod 755 "$PKG_DIR/DEBIAN/postinst" "$PKG_DIR/DEBIAN/prerm"
84-
85-
# Build .deb
86-
dpkg-deb --build "$PKG_DIR"
87-
mv "$PKG_DIR.deb" "$ROOT_DIR/coder-desktop_${VERSION}_${ARCH}.deb"
88-
89-
echo "Package built: coder-desktop_${VERSION}_${ARCH}.deb"
90-
91-
# Cleanup
92-
rm -rf "$BUILD_DIR"
11+
FORMATS=deb OUT_DIR="$ROOT_DIR" "$SCRIPT_DIR/build-release-packages.sh" "$ARCH"

0 commit comments

Comments
 (0)