1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
name: Release
# Triggered only by pushing a tag like v1.0.0, v1.2.3, etc.
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
# ── 1. Quality checks (same as the CI workflow) ────────────────────────────
ci:
name: CI checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ci-cargo-${{ hashFiles('**/Cargo.lock') }}
- run: cargo test --verbose
- run: cargo clippy -- -D warnings
- run: cargo fmt --check
# ── 2. Build release binaries ──────────────────────────────────────────────
#
# The four platforms are built in parallel and independently.
# Each job uploads its archive as a GitHub Actions artifact,
# which the final "publish" job then attaches to the Release.
#
# runner → target mapping:
# ubuntu-22.04 → x86_64-unknown-linux-gnu (older glibc for wider compat)
# ubuntu-22.04 → aarch64-unknown-linux-musl (static, cross-compiled; runs on Termux)
# macos-latest → x86_64-apple-darwin (ARM runner, cross-compiled)
# macos-latest → aarch64-apple-darwin (Apple Silicon runner, native)
# windows-latest → x86_64-pc-windows-msvc (native)
build:
name: Build · ${{ matrix.target }}
needs: ci
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
# Built on Ubuntu 22.04 (glibc 2.35) so the binary also runs on
# 22.04 hosts — ubuntu-latest moved to 24.04 (glibc 2.39).
- target: x86_64-unknown-linux-gnu
runner: ubuntu-22.04
binary: omny
archive: omny-x86_64-unknown-linux-gnu.tar.gz
# Statically linked aarch64 Linux build. The musl runtime is bundled
# into the binary so it runs anywhere with a Linux kernel — including
# Termux on Android, which does not ship glibc.
- target: aarch64-unknown-linux-musl
runner: ubuntu-22.04
binary: omny
archive: omny-aarch64-unknown-linux-musl.tar.gz
- target: x86_64-apple-darwin
runner: macos-latest
binary: omny
archive: omny-x86_64-apple-darwin.tar.gz
- target: aarch64-apple-darwin
runner: macos-latest
binary: omny
archive: omny-aarch64-apple-darwin.tar.gz
- target: x86_64-pc-windows-msvc
runner: windows-latest
binary: omny.exe
archive: omny-x86_64-pc-windows-msvc.zip
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ matrix.target }}-cargo-
# Cross-linker for the aarch64-musl target. Rust bundles the musl libc
# itself; gcc-aarch64-linux-gnu only supplies the ELF linker.
- name: Install aarch64 cross linker
if: matrix.target == 'aarch64-unknown-linux-musl'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
# Build the release binary (optimized, stripped)
- name: Build release binary
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: aarch64-linux-gnu-gcc
CC_aarch64_unknown_linux_musl: aarch64-linux-gnu-gcc
run: cargo build --release --locked --target ${{ matrix.target }}
# Package for Unix (tar.gz)
- name: Package (Unix)
if: runner.os != 'Windows'
run: |
cd target/${{ matrix.target }}/release
tar czf "${{ matrix.archive }}" ${{ matrix.binary }}
mv "${{ matrix.archive }}" "${{ github.workspace }}/"
# Package for Windows (zip)
- name: Package (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
cd target/${{ matrix.target }}/release
Compress-Archive -Path ${{ matrix.binary }} -DestinationPath "${{ github.workspace }}/${{ matrix.archive }}"
# Upload the archive as an artifact — the publish job downloads it
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.archive }}
path: ${{ matrix.archive }}
retention-days: 1
if-no-files-found: error
# ── 3. Create the GitHub Release and attach the binaries ───────────────────
publish:
name: Publish GitHub Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write # required to create the Release
steps:
- uses: actions/checkout@v4
# Download all four archives from the previous jobs
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
# Make sure all files are present
- name: List artifacts
run: ls -lh artifacts/
# Generate SHA256SUMS with bare filenames so the in-app updater can
# verify a downloaded archive against its checksum.
- name: Generate checksums
run: |
cd artifacts
sha256sum omny-* > SHA256SUMS
cat SHA256SUMS
# Extract the current release block from CHANGELOG.md: from the
# "## X.Y.Z" heading down to the next "## " heading or "---" rule.
- name: Extract changelog entry
id: changelog
run: |
VERSION="${GITHUB_REF_NAME#v}" # strip the leading "v" from the tag
BODY=$(awk "/^## ${VERSION} /{found=1; next} found && (/^## /||/^---\$/){exit} found{print}" CHANGELOG.md)
# Pass the multi-line text through GITHUB_OUTPUT
{
echo 'body<<CHANGELOG_EOF'
echo "$BODY"
echo 'CHANGELOG_EOF'
} >> "$GITHUB_OUTPUT"
# Create the Release and attach all binaries in one step
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: OmnySSH ${{ github.ref_name }}
body: ${{ steps.changelog.outputs.body }}
draft: false
prerelease: false
fail_on_unmatched_files: true
files: |
artifacts/omny-x86_64-unknown-linux-gnu.tar.gz
artifacts/omny-aarch64-unknown-linux-musl.tar.gz
artifacts/omny-x86_64-apple-darwin.tar.gz
artifacts/omny-aarch64-apple-darwin.tar.gz
artifacts/omny-x86_64-pc-windows-msvc.zip
artifacts/SHA256SUMS