Skip to content

ci: unify native packaging and harden releases#2012

Open
yyhhyyyyyy wants to merge 11 commits into
devfrom
ci/release-packaging-contract
Open

ci: unify native packaging and harden releases#2012
yyhhyyyyyy wants to merge 11 commits into
devfrom
ci/release-packaging-contract

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Refactor DeepChat's native packaging pipeline around three OS-level reusable workflows and replace permissive release collection with a fail-closed package contract.

This keeps electron-builder and electron-updater while removing the duplicated Windows, Linux, and macOS implementations previously maintained independently in Build and Release.

What changed

  • Added reusable native packaging workflows for Windows, Linux, and macOS.
  • Rewired Build, Release, and package regression to use the same OS workflows.
  • Added immutable source-SHA, target manifest, file-role, digest, provenance, and smoke-test contracts.
  • Added fail-closed release preflight and assembly.
  • Restricted releases to exactly six targets and nineteen public assets.
  • Added local and GitHub API verification of draft release assets.
  • Replaced per-target historical baseline rebuilds with a committed package-size baseline.
  • Added PR package-impact classification and conditional six-target package regression.
  • Split PR checks into explicit static, main, renderer, native-memory, and build jobs.
  • Updated Light OCR, Linux ARM64, plugin packaging, and release-flow documentation.

Packaging architecture

build.yml ───────────────┐
release.yml ─────────────┼── _package-windows.yml
package-regression.yml ──┼── _package-linux.yml
                         └── _package-macos.yml

Each reusable workflow owns its native runner mapping, runtime installation, plugin packaging, Electron packaging, packaged smoke tests, component budgets, manifest generation, and artifact staging.

Callers select only:

  • immutable source SHA
  • architecture
  • distribution or verification purpose
  • installer-size enforcement

Release safety

Release now fails if:

  • any of the six targets is missing
  • a required installer, updater payload, blockmap, or metadata file is missing
  • an unexpected or duplicate public filename is present
  • a manifest contains unsafe paths or symlinks
  • file size or SHA-256 does not match the manifest
  • updater SHA-512 or size does not match the referenced payload
  • updater metadata references a missing release file
  • a verification artifact is supplied to Release
  • macOS distribution evidence is incomplete
  • artifacts from different workflow run attempts are mixed
  • the final draft does not contain exactly nineteen expected assets

Release preflight also requires the tag to exist, resolve to an immutable commit reachable from origin/main, match package.json, and have non-empty CHANGELOG notes before native packaging starts.

Signing and permissions

  • macOS Build and Release packages must be signed, notarized, stapled, and independently verified.
  • Signing state is derived from actual verification commands and cannot be supplied as a caller boolean.
  • PR and scheduled package regression use unsigned, non-distributable verification packages and receive no Apple signing secrets.
  • Windows remains unsigned and does not declare a signing state.
  • Secrets are passed explicitly; secrets: inherit is not used.
  • Package jobs use contents: read; only draft publication receives contents: write.

Package-size regression

The previous implementation rebuilt the historical baseline once for every target.

This PR replaces that with:

  • a committed six-target baseline from Build Application run 29978292769
  • SHA-256 and provenance validation
  • separate component budgets for OCR, Node, and other runtimes
  • upper and lower installer delta limits
  • six-target regression for packaging/runtime-related PR changes
  • scheduled and manually dispatchable package regression

Manual Build keeps lightweight component budgets but skips installer-delta comparison. Release and package regression enforce the complete installer policy.

Summary by CodeRabbit

  • New Features
    • Added reusable packaging workflows for Windows, Linux (x64/arm64), and macOS, plus automated package regression checks.
    • Introduced fail-closed release preflight, assembly, and draft publishing with strict asset/digest verification.
    • Added installer size baselines and growth/shrink policy enforcement.
  • Refactor
    • Refactored the main build flow to delegate OS packaging to the new reusable workflows.
  • Documentation
    • Updated CI/release packaging guidance, specifications, and Light OCR/Linux ARM64 plugin packaging docs.
  • Chores
    • Updated the dimcode registry entry to version 0.2.36.
    • Removed the prior Light OCR package-size script.

Document reusable workflow ownership, release verification order, and package-size provenance.

Record the remote-validation boundary and refresh generated ACP registry data.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This change replaces duplicated platform packaging jobs with reusable Windows, Linux, and macOS workflows; adds package contracts, manifests, size policies, regression gating, and fail-closed release assembly; updates related tests and CI documentation; and removes the former Light OCR package-size action and script.

Changes

CI Packaging and Release

Layer / File(s) Summary
Package contracts and validation
scripts/ci/package-contract.mjs, scripts/ci/package-files.mjs, scripts/ci/package-manifest.mjs, scripts/ci/check-package-size.mjs, resources/package-size-*.json
Defines target roles, file integrity checks, package manifests, installer-size comparison, and component budget validation across six targets.
Reusable platform packaging
.github/workflows/_package-*.yml
Adds architecture-aware reusable workflows for native setup, packaging, smoke tests, plugin checks, manifests, and distribution or verification artifacts.
Caller orchestration and regression gating
.github/workflows/build.yml, .github/workflows/package-regression.yml, .github/workflows/package-check.yml, .github/workflows/prcheck.yml, scripts/ci/classify-package-impact.mjs
Routes builds and regressions through reusable workflows, classifies package impact, conditionally runs platform checks, and removes the main-release guard path.
Release assembly and publication
.github/workflows/release.yml, scripts/ci/release-preflight.mjs, scripts/ci/assemble-release.mjs, scripts/ci/verify-release-assets.mjs
Adds preflight validation, six-target artifact assembly, updater metadata generation, release indexing, and local/remote draft-release verification.
Tests and documentation
test/main/scripts/*, test/main/build/*, test/main/plugin/*, docs/architecture/ci-release-packaging/*, docs/release-flow.md, docs/features/*, docs/guides/plugin-packaging.md
Adds contract coverage and updates packaging, release, Light OCR, Linux ARM64, plugin, updater, and performance documentation/tests.
Removed legacy size flow
.github/actions/light-ocr-package-size/action.yml, package.json, scripts/compare-light-ocr-package-size.mjs, resources/light-ocr-size-budgets.json
Removes the obsolete Light OCR installer-size action and command, expands component budgets, and separates installer baseline and policy resources.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: consolidating native packaging workflows and tightening release validation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/release-packaging-contract

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
.github/workflows/package-check.yml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

.github/workflows/prcheck.yml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/architecture/ci-release-packaging/spec.md (1)

147-148: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify what “complete verification artifact contract” means.

AC-2 and AC-6 prohibit publishing complete unsigned installers, so this wording could be misread as requiring them. Specify that completeness applies to manifests, reports, and verification metadata—not unsigned installer publication.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture/ci-release-packaging/spec.md` around lines 147 - 148,
Clarify the “complete verification artifact contract” statement in the PR
artifact guidance to explicitly cover manifests, reports, and verification
metadata. State that it does not require publishing complete unsigned
installers, while preserving the prohibition on unsigned installer publication
and avoiding a reduced updater-only artifact set.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/architecture/ci-release-packaging/spec.md`:
- Around line 121-123: The classifier-loading path must require the PR base
revision’s classifier and never fall back to the candidate classifier. Update
the classifier-only impact flow around loading
scripts/ci/classify-package-impact.mjs to fail before producing impact outputs
when the base classifier, base commit, merge-base, or package.json validation is
unavailable, preserving the existing fail-closed behavior for all of these
checks.

---

Nitpick comments:
In `@docs/architecture/ci-release-packaging/spec.md`:
- Around line 147-148: Clarify the “complete verification artifact contract”
statement in the PR artifact guidance to explicitly cover manifests, reports,
and verification metadata. State that it does not require publishing complete
unsigned installers, while preserving the prohibition on unsigned installer
publication and avoiding a reduced updater-only artifact set.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c739884b-9597-40d9-b4d6-a1677c2d0ec3

📥 Commits

Reviewing files that changed from the base of the PR and between a2f8a4d and 376b6ec.

📒 Files selected for processing (11)
  • .github/workflows/package-check.yml
  • .github/workflows/prcheck.yml
  • docs/architecture/ci-release-packaging/plan.md
  • docs/architecture/ci-release-packaging/spec.md
  • docs/architecture/ci-release-packaging/tasks.md
  • scripts/ci/classify-package-impact.mjs
  • test/main/memory/memoryRetrieval.eval.test.ts
  • test/main/scripts/electronUpdaterCompatibility.test.ts
  • test/main/scripts/packageCheckWorkflow.test.ts
  • test/main/scripts/packageContract.test.ts
  • test/main/scripts/prcheckWorkflow.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/architecture/ci-release-packaging/tasks.md
  • docs/architecture/ci-release-packaging/plan.md
  • test/main/scripts/packageContract.test.ts

Comment on lines +121 to +123
- The classifier is loaded from the PR base revision when available, so a classifier-only change
cannot use its candidate rules to skip its own package validation. Workflow changes remain
protected by contract tests and normal review policy.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file availability =="
git ls-files | grep -F 'docs/architecture/ci-release-packaging/spec.md' || true

echo "== surrounding spec sections =="
sed -n '90,160p' docs/architecture/ci-release-packaging/spec.md

echo "== search classifier/base revision/package-required mentions =="
rg -n "classifier|base revision|package-required|skip.*validation|candidate rules|checkout|unavailable" docs/architecture/ci-release-packaging/spec.md .github -S || true

Repository: ThinkInAIXYZ/deepchat

Length of output: 8086


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package-check classifier setup and package-required =="
sed -n '40,80p' .github/workflows/package-check.yml
echo "== package-check required status =="
sed -n '110,130p' .github/workflows/package-check.yml 2>/dev/null || true

echo "== classifier outputs =="
rg -n "console\.|console\.error|process\.exit|package-required|package_required|affected|^  os|affected OS|skip|false|true" scripts/ci/classify-package-impact.mjs .github/workflows/package-check.yml .github/workflows/_package*.yml -S

echo "== classifier file size/hash =="
wc -l scripts/ci/classify-package-impact.mjs && git rev-parse --show-toplevel
sed -n '1,220p' scripts/ci/classify-package-impact.mjs

Repository: ThinkInAIXYZ/deepchat

Length of output: 14511


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== remaining classifier file and workflow package-required =="
sed -n '220,285p' scripts/ci/classify-package-impact.mjs
sed -n '280,370p' scripts/ci/classify-package-impact.mjs
sed -n '117,175p' .github/workflows/package-check.yml

echo "== behavioral probe: classifier path influence on output =="
node - <<'JS'
const specPath = '.github/workflows/package-check.yml';
const baseRules = [];
const headRules = [{ id: 'classifier-only', platforms: ['windows', 'linux'], matches: (path) => path === specPath }];
class ImpactClassifier {
  constructor(rules) { this.rules = rules; }
  findChangedPathRules(changedPath) {
    return this.rules.filter((rule) => rule.matches(changedPath));
  }
  classify(paths) {
    const impact = { windows: false, linux: false, macos: false };
    for (const changedPath of paths) {
      for (const rule of this.findChangedPathRules(changedPath)) {
        for (const platform of rule.platforms) impact[platform] = true;
      }
    }
    return impact;
  }
}
console.log(JSON.stringify({
  classifierOnlyPathToBaseRules: new ImpactClassifier(baseRules).classify([specPath]),
  classifierOnlyPathToHeadRules: new ImpactClassifier(headRules).classify([specPath]),
}));
JS

Repository: ThinkInAIXYZ/deepchat

Length of output: 7252


Require the base classifier instead of falling back to the candidate classifier.

The classifier-only path matches against the rules in scripts/ci/classify-package-impact.mjs, so falling back to the PR’s own classifier can make package-required run a classifier-only PR. If the base classifier cannot be loaded, fail before producing impact outputs; keep base commit, merge-base, and package.json validation in this same fail-closed path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture/ci-release-packaging/spec.md` around lines 121 - 123, The
classifier-loading path must require the PR base revision’s classifier and never
fall back to the candidate classifier. Update the classifier-only impact flow
around loading scripts/ci/classify-package-impact.mjs to fail before producing
impact outputs when the base classifier, base commit, merge-base, or
package.json validation is unavailable, preserving the existing fail-closed
behavior for all of these checks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant