-
Notifications
You must be signed in to change notification settings - Fork 0
375 lines (337 loc) · 12.3 KB
/
Copy pathci.yml
File metadata and controls
375 lines (337 loc) · 12.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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
# Shared cache prefix for better cache reuse across jobs
CACHE_PREFIX: ci-v1
jobs:
# ============================================================
# Fast checks - run first, fail fast on basic issues
# ============================================================
fmt:
name: Formatting
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Check formatting
run: cargo fmt -- --check
lockfile:
name: Cargo.lock Check
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Check Cargo.lock is up to date
run: cargo check --locked
unused-deps:
name: Unused Dependencies
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install cargo-machete
uses: taiki-e/install-action@v2
with:
tool: cargo-machete
- name: Check for unused dependencies
run: cargo machete
# ============================================================
# Code quality warnings - informational only
# ============================================================
code-quality:
name: Code Quality Warnings
runs-on: ubuntu-latest
timeout-minutes: 5
# This job is informational only, doesn't block CI
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Check for TODO/FIXME comments in production code
run: |
echo "Scanning for TODO/FIXME comments in src/..."
# Find TODO/FIXME comments (informational, doesn't fail CI)
findings=$(grep -rn --include="*.rs" "TODO\|FIXME" src/ || true)
if [ -n "$findings" ]; then
echo "::warning::Found TODO/FIXME comments in production code:"
echo "$findings"
count=$(echo "$findings" | wc -l)
echo "::notice::Total: $count TODO/FIXME comments found"
else
echo "✅ No TODO/FIXME comments found in production code"
fi
- name: Check for large files
run: |
echo "Checking for large source files (>500 lines)..."
# Find files with more than 500 lines
large_files=$(find src/ -name "*.rs" -exec wc -l {} + | awk '$1 > 500 && !/total$/ {print}' || true)
if [ -n "$large_files" ]; then
echo "::warning::Found large source files (>500 lines):"
echo "$large_files"
else
echo "✅ All source files are under 500 lines"
fi
# ============================================================
# Security checks - important for every PR
# ============================================================
security-audit:
name: Security Audit
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Run security audit
uses: rustsec/audit-check@v2
with:
token: ${{ github.token }}
deny:
name: Dependency & License Check
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check all
arguments: --all-features
# ============================================================
# Linting - requires build artifacts, shares cache
# ============================================================
clippy:
name: Clippy
runs-on: ubuntu-latest
timeout-minutes: 15
# Wait for fast checks to pass before spending time on compilation
needs: [fmt, lockfile]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
shared-key: ${{ env.CACHE_PREFIX }}-clippy
- name: Clippy (default features)
run: cargo clippy -- -D warnings
- name: Clippy (all features)
run: cargo clippy --all-features -- -D warnings
- name: Clippy (no features)
run: cargo clippy --no-default-features -- -D warnings
# ============================================================
# Build verification
# ============================================================
build:
name: Build (${{ matrix.build-type }})
runs-on: ubuntu-latest
timeout-minutes: 20
needs: [fmt, lockfile]
strategy:
fail-fast: true # Fail fast on build errors
matrix:
build-type: [debug, release]
include:
- build-type: debug
cargo-flags: ""
- build-type: release
cargo-flags: "--release"
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
shared-key: ${{ env.CACHE_PREFIX }}-${{ matrix.build-type }}
- name: Build (default features)
run: cargo build ${{ matrix.cargo-flags }}
- name: Build (all features)
run: cargo build --all-features ${{ matrix.cargo-flags }}
- name: Build (no features)
run: cargo build --no-default-features ${{ matrix.cargo-flags }}
# ============================================================
# Windows build verification
# ============================================================
build-windows:
name: Build (Windows)
runs-on: windows-latest
timeout-minutes: 25
needs: [fmt, lockfile]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-msvc
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
shared-key: ${{ env.CACHE_PREFIX }}-windows
- name: Build (default features)
run: cargo build --target x86_64-pc-windows-msvc
- name: Build (all features)
run: cargo build --all-features --target x86_64-pc-windows-msvc
- name: Build (no features)
run: cargo build --no-default-features --target x86_64-pc-windows-msvc
- name: Check for warnings
run: cargo check --target x86_64-pc-windows-msvc 2>&1
# ============================================================
# Test execution
# ============================================================
test:
name: Tests
runs-on: ubuntu-latest
timeout-minutes: 20
needs: [fmt, lockfile]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
shared-key: ${{ env.CACHE_PREFIX }}-test
- name: Run tests (all features)
run: cargo test --all-features
- name: Run tests (no features)
run: cargo test --no-default-features
# Note: Doc tests require a library target, which this binary-only crate doesn't have
# ============================================================
# Documentation
# ============================================================
docs:
name: Documentation
runs-on: ubuntu-latest
timeout-minutes: 15
needs: [fmt, lockfile]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
shared-key: ${{ env.CACHE_PREFIX }}-docs
- name: Build documentation
run: cargo doc --all-features --no-deps
env:
RUSTDOCFLAGS: -D warnings
# ============================================================
# Windows tests - run tests on Windows
# ============================================================
test-windows:
name: Tests (Windows)
runs-on: windows-latest
timeout-minutes: 25
needs: [fmt, lockfile]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-msvc
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
shared-key: ${{ env.CACHE_PREFIX }}-windows-test
- name: Run tests (all features)
run: cargo test --all-features --target x86_64-pc-windows-msvc
# ============================================================
# Install script tests - verify installers work
# ============================================================
test-install-script:
name: Install Script (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
needs: [fmt, lockfile]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
shell: bash
- os: windows-latest
shell: pwsh
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.os == 'windows-latest' && 'x86_64-pc-windows-msvc' || 'x86_64-unknown-linux-gnu' }}
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
shared-key: ${{ env.CACHE_PREFIX }}-install-${{ matrix.os }}
- name: Build binary
shell: bash
run: cargo build --release
- name: Test one-liner install (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
export PAPERBOAT_BINARY_PATH="${PWD}/target/release/paperboat"
export PAPERBOAT_INSTALL_DIR="${PWD}/test-install"
export PAPERBOAT_VERSION="0.0.0-test"
export PAPERBOAT_NO_HOMEBREW=1
mkdir -p "$PAPERBOAT_INSTALL_DIR"
# Test the actual one-liner syntax (using local file instead of curl)
# This verifies: exec trick works, sh runs, and iwr part is never reached
exec sh -c 'sh scripts/install.sh';iwr -useb https://example.com|iex
# Note: if exec works correctly, we never reach here in this shell
# The install script runs and exits, then this shell is replaced
- name: Verify Unix installation
if: runner.os != 'Windows'
shell: bash
run: |
# Verify installation worked (run in new shell since exec replaced the previous one)
./test-install/paperboat --help
echo "✅ Unix install test passed!"
- name: Test one-liner install (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$env:PAPERBOAT_BINARY_PATH = "$PWD\target\release\paperboat.exe"
$env:PAPERBOAT_INSTALL_DIR = "$PWD\test-install"
$env:PAPERBOAT_VERSION = "0.0.0-test"
New-Item -ItemType Directory -Path $env:PAPERBOAT_INSTALL_DIR -Force | Out-Null
# Test the one-liner in a subprocess to simulate interactive shell behavior
pwsh -NoProfile -Command "exec sh -c 'curl -fsSL https://example.com|sh';& '$PWD\scripts\install.ps1'"
# Verify installation
& "$env:PAPERBOAT_INSTALL_DIR\paperboat.exe" --help
Write-Host "✅ Windows install test passed!"
# ============================================================
# Final status check - required for branch protection
# ============================================================
ci-success:
name: CI Success
runs-on: ubuntu-latest
needs: [fmt, lockfile, unused-deps, security-audit, deny, clippy, build, build-windows, test, test-windows, test-install-script, docs]
if: always()
steps:
- name: Check all jobs passed
run: |
results=(
"${{ needs.fmt.result }}"
"${{ needs.lockfile.result }}"
"${{ needs.unused-deps.result }}"
"${{ needs.security-audit.result }}"
"${{ needs.deny.result }}"
"${{ needs.clippy.result }}"
"${{ needs.build.result }}"
"${{ needs.build-windows.result }}"
"${{ needs.test.result }}"
"${{ needs.test-windows.result }}"
"${{ needs.test-install-script.result }}"
"${{ needs.docs.result }}"
)
for result in "${results[@]}"; do
if [[ "$result" != "success" ]]; then
echo "❌ One or more jobs failed or were cancelled"
exit 1
fi
done
echo "✅ All CI jobs passed!"