Skip to content

Commit 3c2e610

Browse files
Flash0verclaude
andcommitted
feat(dotnet): add --agnostic flag and CI NuGet pack job
Redesigns pack.ts mode semantics so CI can assemble all 7 packages across three runners without duplicating the root/any packages: - `--agnostic`: packs only root (pointer) + any packages; no binary needed - `--single`: packs current platform only (no root/any) - `--target <t>`: packs one specific target only (no root/any) - no args: packs current platform + root + any (replaces "all platforms") - `--no-clean`: skips dist-pkg cleanup, allowing accumulation across calls Adds a new `build-nuget` CI job (linux/macos/windows matrix) that downloads the pre-built native binaries, calls pack.ts with --no-clean for each target + agnostic (linux only), and uploads a per-runner nuget-* artifact. Together the three runners produce all 7 packages. Updates package.json: `pack` drops --single, `pack:all` → `pack:agnostic`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 21f7974 commit 3c2e610

File tree

3 files changed

+128
-40
lines changed

3 files changed

+128
-40
lines changed

.github/workflows/ci.yml

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,67 @@ jobs:
208208
name: sentry-${{ matrix.target }}
209209
path: dist-bin/sentry-*
210210

211+
build-nuget:
212+
name: Pack NuGet (${{ matrix.name }})
213+
needs: [changes, lint-dotnet, build-binary]
214+
if: needs.changes.outputs.code == 'true'
215+
runs-on: ${{ matrix.os }}
216+
strategy:
217+
fail-fast: false
218+
matrix:
219+
include:
220+
- name: linux
221+
os: ubuntu-latest
222+
artifact-pattern: sentry-linux-*
223+
pack-targets: linux-x64 linux-arm64
224+
pack-agnostic: true
225+
- name: macos
226+
os: macos-latest
227+
artifact-pattern: sentry-darwin-*
228+
pack-targets: darwin-arm64 darwin-x64
229+
pack-agnostic: false
230+
- name: windows
231+
os: windows-latest
232+
artifact-pattern: sentry-windows-*
233+
pack-targets: windows-x64
234+
pack-agnostic: false
235+
steps:
236+
- uses: actions/checkout@v4
237+
- uses: oven-sh/setup-bun@v2
238+
- uses: actions/cache@v4
239+
id: cache
240+
with:
241+
path: node_modules
242+
key: node-modules-${{ matrix.os }}-${{ hashFiles('bun.lock', 'patches/**') }}
243+
- name: Install dependencies
244+
if: steps.cache.outputs.cache-hit != 'true'
245+
shell: bash
246+
run: bun install --frozen-lockfile
247+
- uses: actions/setup-dotnet@v4
248+
- name: Download binaries
249+
uses: actions/download-artifact@v4
250+
with:
251+
pattern: ${{ matrix.artifact-pattern }}
252+
path: dist-bin
253+
merge-multiple: true
254+
- name: Make binaries executable
255+
if: runner.os != 'Windows'
256+
run: chmod +x dist-bin/sentry-*
257+
- name: Pack NuGet packages
258+
shell: bash
259+
run: |
260+
for target in ${{ matrix.pack-targets }}; do
261+
bun run script/pack.ts --no-clean --target $target
262+
done
263+
if [[ "${{ matrix.pack-agnostic }}" == "true" ]]; then
264+
bun run script/pack.ts --no-clean --agnostic
265+
fi
266+
- name: Upload artifact
267+
uses: actions/upload-artifact@v4
268+
with:
269+
name: nuget-${{ matrix.name }}
270+
path: dist-pkg/*.nupkg
271+
211272
test-e2e:
212273
name: E2E Tests
213274
needs: [build-binary]
@@ -325,14 +386,14 @@ jobs:
325386
ci-status:
326387
name: CI Status
327388
if: always()
328-
needs: [changes, check-skill, build-binary, build-npm, build-docs, test-e2e, test-dotnet]
389+
needs: [changes, check-skill, build-binary, build-npm, build-docs, test-e2e, test-dotnet, build-nuget]
329390
runs-on: ubuntu-latest
330391
permissions: {}
331392
steps:
332393
- name: Check CI status
333394
run: |
334395
# Check for explicit failures or cancellations in all jobs
335-
results="${{ needs.check-skill.result }} ${{ needs.build-binary.result }} ${{ needs.build-npm.result }} ${{ needs.build-docs.result }} ${{ needs.test-e2e.result }} ${{ needs.test-dotnet.result }}"
396+
results="${{ needs.check-skill.result }} ${{ needs.build-binary.result }} ${{ needs.build-npm.result }} ${{ needs.build-docs.result }} ${{ needs.test-e2e.result }} ${{ needs.test-dotnet.result }} ${{ needs.build-nuget.result }}"
336397
for result in $results; do
337398
if [[ "$result" == "failure" || "$result" == "cancelled" ]]; then
338399
echo "::error::CI failed"
@@ -350,6 +411,10 @@ jobs:
350411
echo "::error::CI failed - upstream job failed causing test-dotnet to be skipped"
351412
exit 1
352413
fi
414+
if [[ "${{ needs.changes.outputs.code }}" == "true" && "${{ needs.build-nuget.result }}" == "skipped" ]]; then
415+
echo "::error::CI failed - upstream job failed causing build-nuget to be skipped"
416+
exit 1
417+
fi
353418
if [[ "${{ needs.changes.outputs.skill }}" == "true" && "${{ needs.check-skill.result }}" == "skipped" ]]; then
354419
echo "::error::CI failed - upstream job failed causing check-skill to be skipped"
355420
exit 1

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
"build": "bun run script/build.ts --single",
1515
"build:all": "bun run script/build.ts",
1616
"bundle": "bun run script/bundle.ts",
17-
"pack": "bun run script/pack.ts --single",
18-
"pack:all": "bun run script/pack.ts",
17+
"pack": "bun run script/pack.ts",
18+
"pack:agnostic": "bun run script/pack.ts --agnostic",
1919
"typecheck": "tsc --noEmit",
2020
"lint": "bunx ultracite check",
2121
"lint:fix": "bunx ultracite fix",

script/pack.ts

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55
*
66
* Creates platform-specific NuGet packages with embedded native binaries,
77
* as well as the required top-level pointer package and the RID-agnostic package as fallback.
8-
* The .NET equivalent of build.ts — same target model, same CLI flags, but dependent on it's output at "dist-bin/".
8+
* The .NET equivalent of build.ts — same target model, same CLI flags, but dependent on its output at "dist-bin/".
99
*
1010
* Usage:
11-
* bun run script/pack.ts # Pack for all platforms
12-
* bun run script/pack.ts --single # Pack for current platform only
13-
* bun run script/pack.ts --target darwin-x64 # Pack for specific target (cross-compile)
11+
* bun run script/pack.ts # Pack for current platform + root + any packages
12+
* bun run script/pack.ts --agnostic # Pack only root + any packages (no platform-specific)
13+
* bun run script/pack.ts --single # Pack for current platform only (no root/any)
14+
* bun run script/pack.ts --target darwin-x64 # Pack for a specific target only (no root/any)
15+
*
16+
* Flags:
17+
* --no-clean Skip cleaning the dist-pkg directory before packing
1418
*
1519
* Output:
1620
* dist-pkg/
@@ -140,8 +144,20 @@ async function packRoot(version: string): Promise<boolean> {
140144
}
141145
}
142146

143-
/** Resolve pack targets from CLI args, printing a status line and exiting on error */
144-
function resolveTargets(args: string[]): PackTarget[] {
147+
type PackMode = {
148+
/** Platform-specific targets to pack */
149+
targets: PackTarget[];
150+
/** Whether to also pack the root (pointer) and any (agnostic) packages */
151+
includeAgnostic: boolean;
152+
};
153+
154+
/** Resolve pack mode from CLI args, printing a status line and exiting on error */
155+
function resolveMode(args: string[]): PackMode {
156+
if (args.includes("--agnostic")) {
157+
console.log("\nPacking agnostic packages (root + any)");
158+
return { targets: [], includeAgnostic: true };
159+
}
160+
145161
const targetIndex = args.indexOf("--target");
146162
const targetArg = targetIndex !== -1 ? args[targetIndex + 1] : null;
147163

@@ -155,27 +171,29 @@ function resolveTargets(args: string[]): PackTarget[] {
155171
process.exit(1);
156172
}
157173
console.log(`\nPacking for target: ${getPackageName(target)}`);
158-
return [target];
174+
return { targets: [target], includeAgnostic: false };
175+
}
176+
177+
const currentTarget = ALL_TARGETS.find(
178+
(t) => t.os === process.platform && t.arch === process.arch
179+
);
180+
if (!currentTarget) {
181+
console.error(`Unsupported platform: ${process.platform}-${process.arch}`);
182+
process.exit(1);
159183
}
160184

161185
if (args.includes("--single")) {
162-
const currentTarget = ALL_TARGETS.find(
163-
(t) => t.os === process.platform && t.arch === process.arch
164-
);
165-
if (!currentTarget) {
166-
console.error(
167-
`Unsupported platform: ${process.platform}-${process.arch}`
168-
);
169-
process.exit(1);
170-
}
171186
console.log(
172187
`\nPacking for current platform: ${getPackageName(currentTarget)}`
173188
);
174-
return [currentTarget];
189+
return { targets: [currentTarget], includeAgnostic: false };
175190
}
176191

177-
console.log(`\nPacking for ${ALL_TARGETS.length} targets`);
178-
return ALL_TARGETS;
192+
// Default: current platform + agnostic packages
193+
console.log(
194+
`\nPacking for current platform + agnostic packages: ${getPackageName(currentTarget)}`
195+
);
196+
return { targets: [currentTarget], includeAgnostic: true };
179197
}
180198

181199
/** Verify that native binaries exist for all targets, exiting on missing files */
@@ -201,39 +219,44 @@ async function verifyBinaries(targets: PackTarget[]): Promise<void> {
201219
/** Main pack function */
202220
async function pack(): Promise<void> {
203221
const args = process.argv.slice(2);
222+
const noClean = args.includes("--no-clean");
204223

205224
console.log(`\nSentry CLI NuGet Pack v${pkg.version}`);
206225
console.log("=".repeat(40));
207226

208-
const targets = resolveTargets(args);
227+
const mode = resolveMode(args);
209228

210-
await verifyBinaries(targets);
229+
if (mode.targets.length > 0) {
230+
await verifyBinaries(mode.targets);
231+
}
211232

212-
// Clean output directory
213-
await $`rm -rf ${DIST_PKG_DIR}`.quiet();
233+
// Clean output directory (unless --no-clean is specified)
234+
if (!noClean) {
235+
await $`rm -rf ${DIST_PKG_DIR}`.quiet();
236+
}
214237

215238
console.log("");
216239

217-
// Build all packages
218240
let successCount = 0;
219241
let failCount = 0;
220242

221-
// Root package (no RID) — always included, even for --single / --target
222-
if (await packRoot(pkg.version)) {
223-
successCount += 1;
224-
} else {
225-
failCount += 1;
226-
}
243+
// Root package (no RID) and any package — only when includeAgnostic is set
244+
if (mode.includeAgnostic) {
245+
if (await packRoot(pkg.version)) {
246+
successCount += 1;
247+
} else {
248+
failCount += 1;
249+
}
227250

228-
// "any" package (framework-dependent fallback) — always included, even for --single / --target
229-
if (await packAny(pkg.version)) {
230-
successCount += 1;
231-
} else {
232-
failCount += 1;
251+
if (await packAny(pkg.version)) {
252+
successCount += 1;
253+
} else {
254+
failCount += 1;
255+
}
233256
}
234257

235258
// Platform-specific packages
236-
for (const target of targets) {
259+
for (const target of mode.targets) {
237260
if (await packTarget(target, pkg.version)) {
238261
successCount += 1;
239262
} else {

0 commit comments

Comments
 (0)