-
Notifications
You must be signed in to change notification settings - Fork 5
274 lines (253 loc) · 12.9 KB
/
Copy pathrelease.yml
File metadata and controls
274 lines (253 loc) · 12.9 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
name: Build and Release
on:
push:
tags:
# GitHub uses glob matching here (NOT regex).
# Match both prefixed (v1.2.3) and un-prefixed (1.2.3) tags,
# including prerelease suffixes (e.g. -rc.1, -beta.2).
- 'v[0-9]*.[0-9]*.[0-9]*'
- 'v[0-9]*.[0-9]*.[0-9]*-*'
- '[0-9]*.[0-9]*.[0-9]*'
- '[0-9]*.[0-9]*.[0-9]*-*'
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., 1.0.0)'
required: true
default: '1.0.0'
jobs:
# ----------------------------------------------------------------
# Build all three platforms via a matrix.
#
# Determinism note: `apps/roku-dev-studio` and `roku-dev-studio-mcp` both
# have CI-guarded `prepare` scripts (`if (process.env.CI) process.exit(0)`).
# Without the guard their prepares race against `roku-dev-studio-api`'s
# prepare during `npm ci` (npm doesn't topologically order workspace
# prepare scripts), and the desktop app's preload bundle / mcp's bundle
# both consume `roku-dev-studio-api/dist/lib/*.js`. We therefore build
# mcp explicitly here and let the desktop app's `build:mac|win|linux`
# chain `build:bundle` itself. (`api` and `remote-server` prepares run
# normally — they're self-contained and don't race.)
# ----------------------------------------------------------------
build:
name: Build (${{ matrix.label }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- label: macOS
os: macos-latest
build-cmd: build:mac
artifact-name: mac-builds
artifact-paths: |
apps/roku-dev-studio/dist/*.dmg
apps/roku-dev-studio/dist/*.zip
apps/roku-dev-studio/dist/latest-mac.yml
apps/roku-dev-studio/dist/*.blockmap
apps/roku-dev-studio/dist/mac/**/*.dmg
apps/roku-dev-studio/dist/mac/**/*.zip
apps/roku-dev-studio/dist/mac/**/latest-mac.yml
apps/roku-dev-studio/dist/mac/**/*.blockmap
- label: Windows
os: windows-latest
build-cmd: build:win
artifact-name: windows-builds
# The afterAllArtifactBuild hook
# (apps/roku-dev-studio/scripts/build-hooks.ts) reorganizes Windows
# artifacts into dist/windows/x64/. We deliberately do NOT use
# dist/windows/**/*.exe because dist/windows/unpacked/ contains
# the Electron app's unpacked tree with `Roku Dev Studio.exe` and
# dozens of node_modules .exe files we don't want in the release.
# `latest.yml` (+ the NSIS `.exe.blockmap`) is what lets electron-updater on Windows see a
# release at all — without it every Windows install falls back to the manual-download banner.
# The build hook files them under dist/windows/ (yml) and dist/windows/x64/ (blockmap); the
# flat dist/ paths are the fallback if the hook didn't run.
artifact-paths: |
apps/roku-dev-studio/dist/windows/x64/*.exe
apps/roku-dev-studio/dist/windows/x64/*.blockmap
apps/roku-dev-studio/dist/*.blockmap
apps/roku-dev-studio/dist/latest.yml
apps/roku-dev-studio/dist/windows/**/latest.yml
- label: Linux
os: ubuntu-latest
build-cmd: build:linux
artifact-name: linux-builds
# `latest-linux.yml` / `latest-linux-arm64.yml` are electron-updater's channel files for the
# AppImage builds — same story as Windows above.
artifact-paths: |
apps/roku-dev-studio/dist/*.deb
apps/roku-dev-studio/dist/*.AppImage
apps/roku-dev-studio/dist/latest-linux*.yml
apps/roku-dev-studio/dist/*.blockmap
apps/roku-dev-studio/dist/linux/**/*.deb
apps/roku-dev-studio/dist/linux/**/*.AppImage
apps/roku-dev-studio/dist/linux/**/latest-linux*.yml
apps/roku-dev-studio/dist/linux/**/*.blockmap
env:
# macOS code signing + notarization — electron-builder reads these natively
# (`build.mac.notarize: true` in apps/roku-dev-studio/package.json). CSC_* are scoped to
# the mac leg because electron-builder also treats CSC_LINK as the Windows Authenticode
# fallback and would try to sign the .exe with the Apple certificate.
CSC_LINK: ${{ matrix.build-cmd == 'build:mac' && secrets.CSC_LINK || '' }}
CSC_KEY_PASSWORD: ${{ matrix.build-cmd == 'build:mac' && secrets.CSC_KEY_PASSWORD || '' }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
steps:
- name: Checkout code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '24.17.0'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build mcp (its CI-guarded prepare did not run)
run: npm run build -w roku-dev-studio-mcp
- name: Require macOS signing + notarization secrets
# Without these electron-builder silently emits an ad-hoc-signed, un-notarized app while
# the release body below promises a notarized one. See RELEASE_SETUP.md → Code Signing.
if: runner.os == 'macOS'
run: |
for v in CSC_LINK CSC_KEY_PASSWORD APPLE_ID APPLE_APP_SPECIFIC_PASSWORD APPLE_TEAM_ID; do
[ -n "${!v}" ] || { echo "::error::Missing repository secret: $v"; exit 1; }
done
- name: Build desktop app
# `build:mac|win|linux` chains build:bundle (transpile
# main/preload/renderer) -> clean:dist -> electron-builder. Each of those scripts passes
# `--publish never`, so electron-builder still emits the update-metadata YAML files
# (`app-update.yml`, `latest-mac.yml`, ...) from `apps/roku-dev-studio/package.json`'s
# `build.publish` config but never uploads them itself — `softprops/action-gh-release`
# in the `release` job below is the sole uploader. (Nulling out `build.publish` instead
# of using `--publish never` also suppresses those YAML files entirely, breaking
# electron-updater's runtime checkForUpdates() with an ENOENT — don't do that.)
run: npm run ${{ matrix.build-cmd }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.artifact-name }}
path: ${{ matrix.artifact-paths }}
if-no-files-found: error
retention-days: 5
# ----------------------------------------------------------------
# Aggregate all artifacts into a single GitHub Release.
# ----------------------------------------------------------------
release:
needs: [build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: ./artifacts
- name: Resolve version + tag from event
id: get_version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
# Manual dispatch always materializes as a v-prefixed tag.
TAG_NAME="v${VERSION#v}"
else
TAG_NAME="${GITHUB_REF#refs/tags/}"
# Strip an optional leading 'v' so VERSION is bare semver
# (it appears in artifact filenames in the release body).
VERSION="${TAG_NAME#v}"
fi
# Guardrails: only release valid semver (optionally prefixed with v,
# optionally with prerelease/build metadata after patch).
if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$'; then
echo "Invalid version: '$VERSION'. Expected semver like 1.2.0 or 1.2.0-rc.1"
exit 1
fi
if printf '%s' "$VERSION" | grep -q '-'; then
IS_PRERELEASE=true
else
IS_PRERELEASE=false
fi
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
echo "TAG_NAME=$TAG_NAME" >> "$GITHUB_OUTPUT"
echo "IS_PRERELEASE=$IS_PRERELEASE" >> "$GITHUB_OUTPUT"
- name: Compose release body from the uploaded artifacts
# The Downloads table is generated from the files actually attached to this release, so it
# can never drift from electron-builder's `build.artifactName` templates in
# apps/roku-dev-studio/package.json — the single source of truth for artifact names
# (`npm run verify:artifact-names` prints them and guards against collisions).
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
# shellcheck disable=SC2016 # the backticks in printf's format are literal markdown, not expansion
{
echo "## Roku Dev Studio v${VERSION}"
echo
echo "### Downloads"
echo
echo "| Platform | File |"
echo "|----------|------|"
while IFS= read -r f; do
case "$f" in
./artifacts/mac-builds/*) p="macOS" ;;
./artifacts/windows-builds/*) p="Windows" ;;
./artifacts/linux-builds/*) p="Linux" ;;
*) p="Other" ;;
esac
printf '| **%s** | `%s` |\n' "$p" "$(basename "$f")"
done < <(for d in mac-builds windows-builds linux-builds; do
[ -d "./artifacts/$d" ] && find "./artifacts/$d" -type f \( -name '*.dmg' -o -name '*.zip' -o -name '*.exe' -o -name '*.deb' -o -name '*.AppImage' \) | sort
done)
echo
cat <<'EOF'
Which file: the `.dmg` is for Apple Silicon Macs (Intel builds are not produced by default; run `npm run build:mac:intel` locally if needed). On Windows, the **Setup** `.exe` is the installer and the **Portable** `.exe` runs without installing. Linux `.deb` / `.AppImage` files come in `amd64` / `x86_64` (Intel/AMD) and `arm64` flavours. The `.zip` and `.blockmap` files are used by the in-app updater.
### What's New
See [CHANGELOG.md](https://github.com/paramount-engineering/roku-dev-studio/blob/main/CHANGELOG.md) for a curated summary, or "What's Changed" below for the full commit list.
### Installation
**macOS**
1. Download the `.dmg` file
2. Open the disk image
3. Drag the app to your Applications folder
4. Open the app — it is signed with a Developer ID certificate and notarized by Apple, so Gatekeeper opens it without any override.
**Windows**
1. Download the **Setup** `.exe` file
2. Run the installer
3. If you see "Windows protected your PC":
- Click "More info"
- Click "Run anyway"
4. Follow the installation wizard
**Note for Windows:** The app is not code-signed. Windows SmartScreen will show a warning initially.
**Linux**
- **Debian/Ubuntu:** Download the `.deb` file and install with `sudo dpkg -i filename.deb`
- **Other distros:** Download the `.AppImage` file, make it executable (`chmod +x filename.AppImage`), and run it
EOF
} > release-body.md
cat release-body.md
- name: Create GitHub Release
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
with:
tag_name: ${{ steps.get_version.outputs.TAG_NAME }}
name: Roku Dev Studio v${{ steps.get_version.outputs.VERSION }}
# Appends GitHub's own auto-compiled "What's Changed" (commits/PRs since the last
# tag) after the body below — supplements the curated CHANGELOG.md entry rather than
# replacing the templated downloads/install instructions.
generate_release_notes: true
body_path: release-body.md
draft: false
prerelease: ${{ steps.get_version.outputs.IS_PRERELEASE == 'true' }}
files: |
./artifacts/mac-builds/**/*.dmg
./artifacts/mac-builds/**/*.zip
./artifacts/mac-builds/**/latest-mac.yml
./artifacts/mac-builds/**/*.blockmap
./artifacts/windows-builds/**/*.exe
./artifacts/windows-builds/**/*.blockmap
./artifacts/windows-builds/**/latest.yml
./artifacts/linux-builds/**/*.deb
./artifacts/linux-builds/**/*.AppImage
./artifacts/linux-builds/**/*.blockmap
./artifacts/linux-builds/**/latest-linux*.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}