Repository navigation
276 lines (246 loc) · 10.3 KB
/
Copy pathrelease.yml
File metadata and controls
276 lines (246 loc) · 10.3 KB
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
name: release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Existing tag to (re)build, e.g. v0.5.1'
required: true
# Default to read-only. Each job opts in to the writes it actually needs.
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: '0'
CARGO_NET_RETRY: '10'
RUSTFLAGS: '-D warnings'
RUST_BACKTRACE: '1'
jobs:
# Cross-compile sanity check: the protocol layer is meant to stay buildable
# on Linux (stubs for the macOS-only modules). If this breaks, we'd rather
# fail fast before burning macOS runner minutes.
lint:
name: lint (linux)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Harden runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1
with:
toolchain: '1.95.0'
components: rustfmt
- name: Cache cargo registry
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
- name: Format check
run: cargo fmt --all -- --check
# Cross-compile sanity: the protocol layer must keep building on Linux
# via the non-macOS stubs. Clippy/-D warnings runs on macOS where every
# platform-gated item is actually reachable; on Linux the stubs leave
# macOS-only items unused by design, so we just check the build.
- name: Cross-compile check (Linux stubs)
env:
RUSTFLAGS: ''
run: cargo check --locked --all-targets
build:
name: build (${{ matrix.target }})
needs: lint
permissions:
contents: read
id-token: write # for build provenance OIDC
attestations: write # for actions/attest-build-provenance
strategy:
fail-fast: false
matrix:
include:
- runner: macos-14 # Apple Silicon
target: aarch64-apple-darwin
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
steps:
- name: Harden runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit # macOS runners only support audit mode
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
ref: ${{ inputs.tag || github.ref }}
- name: Verify Cargo.lock is committed
shell: bash
run: |
set -euo pipefail
test -f Cargo.lock || { echo "Cargo.lock missing — commit it for reproducible release builds"; exit 1; }
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1
with:
toolchain: '1.95.0'
target: ${{ matrix.target }}
components: clippy
- name: Cache cargo build
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
key: ${{ matrix.target }}
- name: Clippy (macOS — full code paths)
shell: bash
run: |
set -euo pipefail
cargo clippy --locked --all-targets --target "${{ matrix.target }}" -- -D warnings
- name: Build release binary
shell: bash
run: |
set -euo pipefail
cargo build --release --locked --target "${{ matrix.target }}"
- name: Ad-hoc codesign
# Without any signature the OS treats every rebuilt binary as a new
# identity and the user has to re-grant Screen Recording / Accessibility
# on every download. Ad-hoc (-s -) at least gives it a stable hash.
# For production: replace with Developer ID + notarytool.
shell: bash
run: |
set -euo pipefail
BIN="target/${{ matrix.target }}/release/macrdp"
codesign -s - --force --options runtime --timestamp=none "$BIN"
codesign -dv "$BIN"
- name: Stage artifact
id: stage
shell: bash
# VERSION comes from a tag name / dispatch input; pass it via env (never
# inline in the script) so a maliciously-named tag can't inject shell.
env:
VERSION: ${{ inputs.tag || github.ref_name }}
run: |
set -euo pipefail
NAME="macrdp-${VERSION}-${{ matrix.target }}"
mkdir -p "dist/${NAME}"
cp "target/${{ matrix.target }}/release/macrdp" "dist/${NAME}/"
cp LICENSE* README.md "dist/${NAME}/" 2>/dev/null || true
tar -C dist -czf "dist/${NAME}.tar.gz" "${NAME}"
(cd dist && shasum -a 256 "${NAME}.tar.gz" > "${NAME}.tar.gz.sha256")
echo "archive=dist/${NAME}.tar.gz" >> "$GITHUB_OUTPUT"
echo "checksum=dist/${NAME}.tar.gz.sha256" >> "$GITHUB_OUTPUT"
echo "name=${NAME}" >> "$GITHUB_OUTPUT"
# The .app bundle: the bare binary plus the embedded smart-card IFD handler
# (ifd-macrdp.bundle) and its installer — i.e. the only release artifact that
# carries everything --enable-smartcard-redirection needs. Ad-hoc signed (no
# Developer ID / notarization in CI), so end users get a Gatekeeper "can't
# verify developer" prompt and open it via right-click -> Open; a notarized
# build needs a local `make-app.sh` with a Developer ID. See README.
- name: Build smart-card IFD handler (cdylib, for the .app)
shell: bash
run: |
set -euo pipefail
cargo build --release --locked --manifest-path ifd-handler/Cargo.toml
- name: Build app-switcher HUD helper (for the .app)
shell: bash
run: |
set -euo pipefail
( cd gui && swift build -c release --product macrdphud )
- name: Build shield-window helper (for the .app)
shell: bash
run: |
set -euo pipefail
( cd gui && swift build -c release --product macrdpshield )
- name: Assemble macrdp.app (ad-hoc signed)
# SKIP_BUILD reuses the binary already built + signed above (so the app
# ships the identical Mach-O) and the IFD dylib from the step above.
# make-app.sh embeds the IFD bundle + installer and ad-hoc signs
# (CODESIGN_IDENTITY unset). APP_DIR is a throwaway dir so its final
# "install" copy lands somewhere writable; the canonical staged bundle is
# target/macrdp.app, which is what we package.
shell: bash
run: |
set -euo pipefail
mkdir -p target/release
cp "target/${{ matrix.target }}/release/macrdp" target/release/macrdp
SKIP_BUILD=1 APP_DIR="$RUNNER_TEMP/appout" packaging/make-app.sh
test -f "target/macrdp.app/Contents/Resources/ifd-macrdp.bundle/Contents/MacOS/libifd_macrdp.dylib" \
|| { echo "IFD handler not embedded in the app"; exit 1; }
test -f "target/macrdp.app/Contents/Resources/macrdphud" \
|| { echo "app-switcher HUD helper not embedded in the app"; exit 1; }
test -f "target/macrdp.app/Contents/Resources/macrdpshield" \
|| { echo "shield-window helper not embedded in the app"; exit 1; }
codesign --verify --deep --strict target/macrdp.app
- name: Stage app artifact
id: stageapp
shell: bash
# VERSION via env (not inline) — same shell-injection hardening as above.
env:
VERSION: ${{ inputs.tag || github.ref_name }}
run: |
set -euo pipefail
NAME="macrdp-${VERSION}-${{ matrix.target }}-app"
mkdir -p dist
# ditto -k preserves the bundle's signature + symlinks (plain zip won't).
ditto -c -k --keepParent target/macrdp.app "dist/${NAME}.zip"
(cd dist && shasum -a 256 "${NAME}.zip" > "${NAME}.zip.sha256")
echo "archive=dist/${NAME}.zip" >> "$GITHUB_OUTPUT"
echo "checksum=dist/${NAME}.zip.sha256" >> "$GITHUB_OUTPUT"
- name: Attest build provenance
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: |
${{ steps.stage.outputs.archive }}
${{ steps.stageapp.outputs.archive }}
- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ steps.stage.outputs.name }}
path: |
${{ steps.stage.outputs.archive }}
${{ steps.stage.outputs.checksum }}
${{ steps.stageapp.outputs.archive }}
${{ steps.stageapp.outputs.checksum }}
if-no-files-found: error
retention-days: 7
release:
name: publish release
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write # only this job writes to the repo (creates a Release)
steps:
- name: Harden runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Download all artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: artifacts
pattern: macrdp-*
- name: Aggregate checksums
shell: bash
run: |
set -euo pipefail
mkdir -p out
find artifacts -type f -name '*.tar.gz' -exec cp {} out/ \;
find artifacts -type f -name '*.zip' -exec cp {} out/ \;
find artifacts -type f -name '*.sha256' -exec cp {} out/ \;
(cd out && cat *.sha256 > SHA256SUMS && rm *.sha256)
cat out/SHA256SUMS
- name: Create GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
tag_name: ${{ inputs.tag || github.ref_name }}
draft: true # human reviews before publishing
generate_release_notes: true
fail_on_unmatched_files: true
files: |
out/*.tar.gz
out/*.zip
out/SHA256SUMS