-
Notifications
You must be signed in to change notification settings - Fork 7
217 lines (191 loc) · 9.27 KB
/
build.yml
File metadata and controls
217 lines (191 loc) · 9.27 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
# ─────────────────────────────────────────────────────────────
# Moonfin Plugin — CI Build Workflow
# ─────────────────────────────────────────────────────────────
#
# Triggers:
# • Push to master → build + package ZIP artifact
# • PR to master → build + upload result artifact
#
# Security:
# Uses pull_request (not pull_request_target) so untrusted fork
# code never runs with write access. PR comments are posted by
# a separate workflow (comment.yml) triggered via workflow_run.
#
# Build flow:
# 1. Frontend install (npm ci)
# 2. Frontend build (node build.js) — skipped if (1) fails
# 3. Sync web files (cp to backend/Web/) — skipped if (2) fails
# 4. Backend build (dotnet build) — skipped if (2) fails
# 5. Gate step — fails the job if any of (1)-(4) didn't succeed
#
# Error handling:
# Build steps (1), (2), (4) use continue-on-error so the job keeps
# running even on failure. Their output is captured to log files via
# tee. The gate step (5) re-fails the job so the overall status is
# correct. On PRs, build results (outcomes + logs) are uploaded as
# an artifact for the comment.yml workflow to pick up.
#
# Packaging (master only, after successful build):
# Reads version + ABI from the .csproj, creates a release ZIP with
# the DLL + meta.json, and uploads it as a workflow artifact.
# ─────────────────────────────────────────────────────────────
name: Build
on:
push:
branches: [master]
pull_request:
branches: [master]
permissions:
contents: read
jobs:
build:
name: Build Plugin
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
# ── Frontend ──────────────────────────────────────────────
# continue-on-error: lets the job continue so we can upload
# build results even when a step fails.
# set -o pipefail: ensures the pipe returns the command's exit
# code, not tee's (which always succeeds).
# 2>&1 | tee: captures stdout+stderr to a log file for the
# PR comment, while still printing to the Actions console.
- name: Install frontend dependencies
id: frontend_install
continue-on-error: true
working-directory: frontend
run: |
set -o pipefail
npm ci 2>&1 | tee "${{ runner.temp }}/frontend-install.log"
- name: Build frontend
if: steps.frontend_install.outcome == 'success'
id: frontend_build
continue-on-error: true
working-directory: frontend
run: |
set -o pipefail
node build.js 2>&1 | tee "${{ runner.temp }}/frontend-build.log"
# ── Sync web files into backend ───────────────────────────
- name: Sync web files
if: steps.frontend_build.outcome == 'success'
run: |
mkdir -p backend/Web
cp frontend/dist/plugin.js backend/Web/plugin.js
cp frontend/dist/plugin.css backend/Web/plugin.css
# ── Backend ───────────────────────────────────────────────
- name: Build backend
if: steps.frontend_build.outcome == 'success'
id: backend_build
continue-on-error: true
run: |
set -o pipefail
dotnet build backend/Moonfin.Server.csproj -c Release 2>&1 | tee "${{ runner.temp }}/backend-build.log"
# ── Gate ───────────────────────────────────────────────────
# continue-on-error masks failures from the job status. This
# gate step re-fails the job when any build step didn't
# succeed (covers both 'failure' and 'skipped' outcomes).
- name: Fail if build failed
id: build_gate
if: |
steps.frontend_install.outcome != 'success' ||
steps.frontend_build.outcome != 'success' ||
steps.backend_build.outcome != 'success'
run: exit 1
# ── Upload build results for PR comment workflow ──────────
# Always runs on PRs so the comment.yml workflow can post
# results even on failure. Writes step outcomes + PR number
# to a metadata file alongside the log files.
- name: Save build results
if: always() && github.event_name == 'pull_request'
run: |
mkdir -p "${{ runner.temp }}/build-results"
cat > "${{ runner.temp }}/build-results/outcomes.json" <<EOF
{
"pr_number": ${{ github.event.pull_request.number }},
"sha": "${{ github.event.pull_request.head.sha }}",
"frontend_install": "${{ steps.frontend_install.outcome }}",
"frontend_build": "${{ steps.frontend_build.outcome }}",
"backend_build": "${{ steps.backend_build.outcome }}"
}
EOF
# Copy any log files that exist
for f in frontend-install.log frontend-build.log backend-build.log; do
[ -f "${{ runner.temp }}/$f" ] && cp "${{ runner.temp }}/$f" "${{ runner.temp }}/build-results/"
done
- name: Upload build results
if: always() && github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: build-results
path: ${{ runner.temp }}/build-results/
# ── Package (master only) ─────────────────────────────────
- name: Read version from csproj
if: github.event_name == 'push' && steps.backend_build.outcome == 'success'
id: version
run: |
VERSION=$(grep -oPm1 '(?<=<AssemblyVersion>)[^<]+' backend/Moonfin.Server.csproj)
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Read target ABI from csproj
if: github.event_name == 'push' && steps.backend_build.outcome == 'success'
id: abi
run: |
ABI=$(grep 'Jellyfin.Controller' backend/Moonfin.Server.csproj | grep -oP 'Version="\K[^"]+')
echo "abi=${ABI}.0" >> "$GITHUB_OUTPUT"
- name: Package release ZIP
if: github.event_name == 'push' && steps.backend_build.outcome == 'success'
id: package
run: |
VERSION="${{ steps.version.outputs.version }}"
TARGET_ABI="${{ steps.abi.outputs.abi }}"
PLUGIN_GUID="8c5d0e91-4f2a-4b6d-9e3f-1a7c8d9e0f2b"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
mkdir -p release
cp backend/bin/Release/net8.0/Moonfin.Server.dll release/
cat > release/meta.json <<EOF
{
"category": "General",
"changelog": "",
"description": "Moonfin brings a modern TV-style UI to Jellyfin web. Features include: custom navbar, media bar with featured content, Jellyseerr integration, and cross-device settings synchronization.",
"guid": "${PLUGIN_GUID}",
"name": "Moonfin",
"overview": "Custom UI and settings sync for Jellyfin",
"owner": "RadicalMuffinMan",
"targetAbi": "${TARGET_ABI}",
"timestamp": "${TIMESTAMP}",
"version": "${VERSION}",
"status": "Active",
"autoUpdate": true,
"assemblies": ["Moonfin.Server.dll"]
}
EOF
ZIP_NAME="Moonfin.Server-${VERSION}.zip"
cd release && zip -r "../${ZIP_NAME}" . && cd ..
CHECKSUM=$(md5sum "$ZIP_NAME" | awk '{print toupper($1)}')
echo "zip_name=$ZIP_NAME" >> "$GITHUB_OUTPUT"
echo "checksum=$CHECKSUM" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Upload build artifact
if: github.event_name == 'push' && steps.backend_build.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.zip_name }}
path: ${{ steps.package.outputs.zip_name }}
- name: Build summary (master)
if: github.event_name == 'push' && steps.backend_build.outcome == 'success'
run: |
echo "## ✅ Build Successful" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|---|---|" >> $GITHUB_STEP_SUMMARY
echo "| **Version** | \`${{ steps.version.outputs.version }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Checksum (MD5)** | \`${{ steps.package.outputs.checksum }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Artifact** | \`${{ steps.package.outputs.zip_name }}\` |" >> $GITHUB_STEP_SUMMARY