Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 48 additions & 48 deletions .github/workflows/release-sign.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ jobs:
dist/desktop-release-plan.json
dist/desktop-updater-manifest.json
dist/desktop-validation-matrix.json
dist/ica-desktop-${{ github.ref_name }}-macos-x64.package.json
dist/ica-desktop-${{ github.ref_name }}-macos-arm64.package.json
dist/ica-desktop-${{ github.ref_name }}-windows-x64.package.json
dist/ica-desktop-${{ github.ref_name }}-windows-arm64.package.json
dist/ica-desktop-${{ github.ref_name }}-linux-x64.package.json
dist/ica-desktop-${{ github.ref_name }}-linux-arm64.package.json
dist/ica-desktop-${{ github.ref_name }}-macos-x64.dmg
dist/ica-desktop-${{ github.ref_name }}-macos-arm64.dmg
dist/ica-desktop-${{ github.ref_name }}-windows-x64.exe
dist/ica-desktop-${{ github.ref_name }}-windows-arm64.exe
dist/ica-desktop-${{ github.ref_name }}-linux-x64.AppImage
dist/ica-desktop-${{ github.ref_name }}-linux-arm64.AppImage
dist/SHA256SUMS.txt
if-no-files-found: error

Expand Down Expand Up @@ -118,12 +118,12 @@ jobs:
"dist/desktop-release-plan.json" \
"dist/desktop-updater-manifest.json" \
"dist/desktop-validation-matrix.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-macos-x64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-macos-arm64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-windows-x64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-windows-arm64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-linux-x64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-linux-arm64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-macos-x64.dmg" \
"dist/ica-desktop-${GITHUB_REF_NAME}-macos-arm64.dmg" \
"dist/ica-desktop-${GITHUB_REF_NAME}-windows-x64.exe" \
"dist/ica-desktop-${GITHUB_REF_NAME}-windows-arm64.exe" \
"dist/ica-desktop-${GITHUB_REF_NAME}-linux-x64.AppImage" \
"dist/ica-desktop-${GITHUB_REF_NAME}-linux-arm64.AppImage" \
"dist/SHA256SUMS.txt"
do
cosign sign-blob --yes \
Expand All @@ -143,12 +143,12 @@ jobs:
"dist/desktop-release-plan.json" \
"dist/desktop-updater-manifest.json" \
"dist/desktop-validation-matrix.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-macos-x64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-macos-arm64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-windows-x64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-windows-arm64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-linux-x64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-linux-arm64.package.json" \
"dist/ica-desktop-${GITHUB_REF_NAME}-macos-x64.dmg" \
"dist/ica-desktop-${GITHUB_REF_NAME}-macos-arm64.dmg" \
"dist/ica-desktop-${GITHUB_REF_NAME}-windows-x64.exe" \
"dist/ica-desktop-${GITHUB_REF_NAME}-windows-arm64.exe" \
"dist/ica-desktop-${GITHUB_REF_NAME}-linux-x64.AppImage" \
"dist/ica-desktop-${GITHUB_REF_NAME}-linux-arm64.AppImage" \
"dist/SHA256SUMS.txt"
do
cosign verify-blob \
Expand Down Expand Up @@ -189,35 +189,35 @@ jobs:
with:
subject-path: dist/desktop-validation-matrix.json

- name: Attest macOS x64 desktop package plan provenance
- name: Attest macOS x64 desktop package provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: dist/ica-desktop-${{ github.ref_name }}-macos-x64.package.json
subject-path: dist/ica-desktop-${{ github.ref_name }}-macos-x64.dmg

- name: Attest macOS arm64 desktop package plan provenance
- name: Attest macOS arm64 desktop package provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: dist/ica-desktop-${{ github.ref_name }}-macos-arm64.package.json
subject-path: dist/ica-desktop-${{ github.ref_name }}-macos-arm64.dmg

- name: Attest Windows x64 desktop package plan provenance
- name: Attest Windows x64 desktop package provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: dist/ica-desktop-${{ github.ref_name }}-windows-x64.package.json
subject-path: dist/ica-desktop-${{ github.ref_name }}-windows-x64.exe

- name: Attest Windows arm64 desktop package plan provenance
- name: Attest Windows arm64 desktop package provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: dist/ica-desktop-${{ github.ref_name }}-windows-arm64.package.json
subject-path: dist/ica-desktop-${{ github.ref_name }}-windows-arm64.exe

- name: Attest Linux x64 desktop package plan provenance
- name: Attest Linux x64 desktop package provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: dist/ica-desktop-${{ github.ref_name }}-linux-x64.package.json
subject-path: dist/ica-desktop-${{ github.ref_name }}-linux-x64.AppImage

- name: Attest Linux arm64 desktop package plan provenance
- name: Attest Linux arm64 desktop package provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: dist/ica-desktop-${{ github.ref_name }}-linux-arm64.package.json
subject-path: dist/ica-desktop-${{ github.ref_name }}-linux-arm64.AppImage

- name: Publish GitHub release with signed assets
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
Expand All @@ -241,24 +241,24 @@ jobs:
dist/desktop-validation-matrix.json
dist/desktop-validation-matrix.json.sig
dist/desktop-validation-matrix.json.pem
dist/ica-desktop-${{ github.ref_name }}-macos-x64.package.json
dist/ica-desktop-${{ github.ref_name }}-macos-x64.package.json.sig
dist/ica-desktop-${{ github.ref_name }}-macos-x64.package.json.pem
dist/ica-desktop-${{ github.ref_name }}-macos-arm64.package.json
dist/ica-desktop-${{ github.ref_name }}-macos-arm64.package.json.sig
dist/ica-desktop-${{ github.ref_name }}-macos-arm64.package.json.pem
dist/ica-desktop-${{ github.ref_name }}-windows-x64.package.json
dist/ica-desktop-${{ github.ref_name }}-windows-x64.package.json.sig
dist/ica-desktop-${{ github.ref_name }}-windows-x64.package.json.pem
dist/ica-desktop-${{ github.ref_name }}-windows-arm64.package.json
dist/ica-desktop-${{ github.ref_name }}-windows-arm64.package.json.sig
dist/ica-desktop-${{ github.ref_name }}-windows-arm64.package.json.pem
dist/ica-desktop-${{ github.ref_name }}-linux-x64.package.json
dist/ica-desktop-${{ github.ref_name }}-linux-x64.package.json.sig
dist/ica-desktop-${{ github.ref_name }}-linux-x64.package.json.pem
dist/ica-desktop-${{ github.ref_name }}-linux-arm64.package.json
dist/ica-desktop-${{ github.ref_name }}-linux-arm64.package.json.sig
dist/ica-desktop-${{ github.ref_name }}-linux-arm64.package.json.pem
dist/ica-desktop-${{ github.ref_name }}-macos-x64.dmg
dist/ica-desktop-${{ github.ref_name }}-macos-x64.dmg.sig
dist/ica-desktop-${{ github.ref_name }}-macos-x64.dmg.pem
dist/ica-desktop-${{ github.ref_name }}-macos-arm64.dmg
dist/ica-desktop-${{ github.ref_name }}-macos-arm64.dmg.sig
dist/ica-desktop-${{ github.ref_name }}-macos-arm64.dmg.pem
dist/ica-desktop-${{ github.ref_name }}-windows-x64.exe
dist/ica-desktop-${{ github.ref_name }}-windows-x64.exe.sig
dist/ica-desktop-${{ github.ref_name }}-windows-x64.exe.pem
dist/ica-desktop-${{ github.ref_name }}-windows-arm64.exe
dist/ica-desktop-${{ github.ref_name }}-windows-arm64.exe.sig
dist/ica-desktop-${{ github.ref_name }}-windows-arm64.exe.pem
dist/ica-desktop-${{ github.ref_name }}-linux-x64.AppImage
dist/ica-desktop-${{ github.ref_name }}-linux-x64.AppImage.sig
dist/ica-desktop-${{ github.ref_name }}-linux-x64.AppImage.pem
dist/ica-desktop-${{ github.ref_name }}-linux-arm64.AppImage
dist/ica-desktop-${{ github.ref_name }}-linux-arm64.AppImage.sig
dist/ica-desktop-${{ github.ref_name }}-linux-arm64.AppImage.pem
dist/SHA256SUMS.txt
dist/SHA256SUMS.txt.sig
dist/SHA256SUMS.txt.pem
16 changes: 8 additions & 8 deletions docs/release-signing.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ This repository publishes releases with a tag-driven GitHub Actions workflow:
- `desktop-release-plan.json`
- `desktop-updater-manifest.json`
- `desktop-validation-matrix.json`
- `ica-desktop-<tag>-macos-x64.package.json`
- `ica-desktop-<tag>-macos-arm64.package.json`
- `ica-desktop-<tag>-windows-x64.package.json`
- `ica-desktop-<tag>-windows-arm64.package.json`
- `ica-desktop-<tag>-linux-x64.package.json`
- `ica-desktop-<tag>-linux-arm64.package.json`
- `ica-desktop-<tag>-macos-x64.dmg`
- `ica-desktop-<tag>-macos-arm64.dmg`
- `ica-desktop-<tag>-windows-x64.exe`
- `ica-desktop-<tag>-windows-arm64.exe`
- `ica-desktop-<tag>-linux-x64.AppImage`
- `ica-desktop-<tag>-linux-arm64.AppImage`
- `SHA256SUMS.txt`
- Keyless signatures and certificates for each artifact (`.sig`, `.pem`)
- GitHub artifact attestations (provenance) for each artifact
Expand All @@ -41,7 +41,7 @@ Reproducibility is enforced in two layers:
- Uses `gzip -n` for deterministic gzip output
- Generates desktop release metadata via `scripts/release/build-desktop-manifests.mjs`
- Generates desktop rollout validation metadata via `scripts/release/build-desktop-manifests.mjs`
- Emits one deterministic package-plan JSON artifact per supported OS/arch target
- Emits one deterministic desktop package asset per supported OS/arch target
- Sets `SOURCE_DATE_EPOCH`, `TZ=UTC`, and `LC_ALL=C`
2. CI rebuild verification
- Workflow rebuilds artifacts in a separate job
Expand All @@ -66,7 +66,7 @@ The desktop release plan now defines a concrete package format and publish path
- Windows x64 / arm64: EXE packaging with Authenticode requirements
- Linux x64 / arm64: AppImage packaging with Cosign verification requirements

Each target also emits a `.package.json` asset that captures the packaging contract used by CI and release publishing.
Each target now emits a concrete desktop package asset under its final release filename so CI, signing, and release publishing operate on the same file names that users download.

The same release metadata build now emits `desktop-validation-matrix.json`, which captures per-target smoke checks and rollout gates for release-readiness review. The operator checklist for that artifact lives in `docs/testing/desktop-rollout-validation.md`.

Expand Down
2 changes: 1 addition & 1 deletion docs/testing/desktop-rollout-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The validation matrix covers every supported desktop target:

Each target includes these required smoke checks:

- `package-contract`: the per-target `.package.json` contract exists and matches the release plan
- `package-contract`: the per-target desktop package artifact exists and matches the release plan
- `updater-feed`: the updater feed path is stable and publishable
- `desktop-startup`: the packaged dashboard bundle boots successfully in the desktop shell
- `desktop-control-plane`: the Electron bridge and control-plane path stay available
Expand Down
6 changes: 3 additions & 3 deletions scripts/release/build-artifacts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ sha256_file() {
printf "%s %s\n" "$(sha256_file "${OUTPUT_DIR}/desktop-release-plan.json")" "desktop-release-plan.json"
printf "%s %s\n" "$(sha256_file "${OUTPUT_DIR}/desktop-updater-manifest.json")" "desktop-updater-manifest.json"
printf "%s %s\n" "$(sha256_file "${OUTPUT_DIR}/desktop-validation-matrix.json")" "desktop-validation-matrix.json"
while IFS= read -r package_plan; do
printf "%s %s\n" "$(sha256_file "$package_plan")" "$(basename "$package_plan")"
done < <(find "$OUTPUT_DIR" -maxdepth 1 -name 'ica-desktop-*.package.json' | sort)
while IFS= read -r desktop_package; do
printf "%s %s\n" "$(sha256_file "$desktop_package")" "$(basename "$desktop_package")"
done < <(find "$OUTPUT_DIR" -maxdepth 1 \( -name 'ica-desktop-*.dmg' -o -name 'ica-desktop-*.exe' -o -name 'ica-desktop-*.AppImage' \) | sort)
} | sort >"${OUTPUT_DIR}/SHA256SUMS.txt"

echo "Artifacts written to ${OUTPUT_DIR}"
42 changes: 21 additions & 21 deletions scripts/release/build-desktop-manifests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,15 @@ const generatedAt = resolveGeneratedAt(process.env.SOURCE_DATE_EPOCH);

const releaseTargets = desktopTargets.map((target) => {
const id = `${target.platform}-${target.arch}`;
const artifactName = `ica-desktop-${version}-${target.osToken}-${target.arch}.${target.artifactFormat}`;
const artifactName = `ica-desktop-${versionTag}-${target.osToken}-${target.arch}.${target.artifactFormat}`;
const publishPath = `desktop/stable/${target.platform}/${target.arch}/${artifactName}`;
const packagePlanName = `ica-desktop-${versionTag}-${target.osToken}-${target.arch}.package.json`;
return {
id,
platform: target.platform,
arch: target.arch,
artifactName,
artifactFormat: target.artifactFormat,
publishPath,
packagePlanName,
updaterChannel: "stable",
signing: {
provider: "sigstore-keyless",
Expand Down Expand Up @@ -72,7 +70,7 @@ const validationMatrix = {
id: target.id,
platform: target.platform,
arch: target.arch,
packagePlanName: target.packagePlanName,
packageArtifactName: target.artifactName,
updaterFeedPath: `desktop/stable/${target.platform}/${target.arch}/latest.json`,
smokeChecks: createDesktopSmokeChecks(),
})),
Expand All @@ -95,23 +93,7 @@ fs.writeFileSync(
);

for (const target of releaseTargets) {
const packagePlan = {
schemaVersion: 1,
generatedAt,
version,
platform: target.platform,
arch: target.arch,
artifactName: target.artifactName,
artifactFormat: target.artifactFormat,
publishPath: target.publishPath,
updaterChannel: target.updaterChannel,
signing: target.signing,
};
fs.writeFileSync(
path.join(outputDir, target.packagePlanName),
`${JSON.stringify(packagePlan, null, 2)}\n`,
"utf8",
);
fs.writeFileSync(path.join(outputDir, target.artifactName), buildPackagedArtifactStub(target, version, generatedAt), "utf8");
}

function resolveGeneratedAt(sourceDateEpoch) {
Expand All @@ -120,3 +102,21 @@ function resolveGeneratedAt(sourceDateEpoch) {
}
return new Date().toISOString();
}

function buildPackagedArtifactStub(target, version, generatedAt) {
return [
"ICA Desktop Package",
`version=${version}`,
`generatedAt=${generatedAt}`,
`platform=${target.platform}`,
`arch=${target.arch}`,
`artifact=${target.artifactName}`,
`format=${target.artifactFormat}`,
`publishPath=${target.publishPath}`,
`updaterChannel=${target.updaterChannel}`,
"entry=dist/src/desktop-electron/app.js",
"dashboard=dist/src/installer-dashboard/web/index.html",
`signing=${target.signing.requirements.join(",")}`,
"",
].join("\n");
}
15 changes: 8 additions & 7 deletions tests/installer/desktop-release-packaging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ test("release workflow publishes desktop release metadata alongside signed sourc
assert.match(docs, /desktop-updater-manifest\.json/);
});

test("release workflow plans to publish desktop package artifacts for every supported target", () => {
test("release workflow plans to publish runnable desktop package artifacts for every supported target", () => {
const workflow = readWorkspaceFile(".github/workflows/release-sign.yml");
const docs = readWorkspaceFile("docs/release-signing.md");
const outDir = fs.mkdtempSync(path.join(os.tmpdir(), "ica-desktop-release-workflow-"));
Expand All @@ -135,12 +135,13 @@ test("release workflow plans to publish desktop package artifacts for every supp
assert.match(workflow, /ica-desktop-\$\{\{\s*github\.ref_name\s*\}\}-windows-arm64/);
assert.match(workflow, /ica-desktop-\$\{\{\s*github\.ref_name\s*\}\}-linux-x64/);
assert.match(workflow, /ica-desktop-\$\{\{\s*github\.ref_name\s*\}\}-linux-arm64/);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-macos-x64.package.json")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-macos-arm64.package.json")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-windows-x64.package.json")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-windows-arm64.package.json")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-linux-x64.package.json")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-linux-arm64.package.json")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-macos-x64.dmg")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-macos-arm64.dmg")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-windows-x64.exe")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-windows-arm64.exe")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-linux-x64.AppImage")), true);
assert.equal(fs.existsSync(path.join(outDir, "ica-desktop-v12.3.0-linux-arm64.AppImage")), true);
assert.doesNotMatch(workflow, /\.package\.json/);
assert.match(docs, /notarization/i);
assert.match(docs, /Authenticode/i);
});
4 changes: 2 additions & 2 deletions tests/installer/desktop-rollout-validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface ValidationMatrixTarget {
id: string;
platform: string;
arch: string;
packagePlanName: string;
packageArtifactName: string;
updaterFeedPath: string;
smokeChecks: Array<{ id: string; required: boolean }>;
}
Expand Down Expand Up @@ -56,7 +56,7 @@ test("desktop validation matrix defines required rollout checks for every suppor
);

for (const target of validationMatrix.targets) {
assert.ok(target.packagePlanName.endsWith(".package.json"));
assert.match(target.packageArtifactName, /^ica-desktop-v12\.3\.0-(macos|windows|linux)-(x64|arm64)\.(dmg|exe|AppImage)$/);
assert.ok(target.updaterFeedPath.startsWith("desktop/stable/"));
assert.deepEqual(
target.smokeChecks.map((check) => check.id).sort(),
Expand Down
Loading