Skip to content

Commit 583a3f5

Browse files
committed
fix(bot): review release distribution consistency
1 parent db8b379 commit 583a3f5

2 files changed

Lines changed: 106 additions & 0 deletions

File tree

.github/prompts/codex-pr-review.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,21 @@ Example finding:
126126
```
127127
````
128128

129+
## Release And Distribution Validation
130+
131+
When a PR touches `.github/workflows/release.yml`, `.github/workflows/packaging-smoke.yml`, `packaging/**`, release notes, or package-manager manifests, review it as a release-path change. Do not stop at YAML syntax or local manifest shape.
132+
133+
Check these paths explicitly when relevant:
134+
135+
- `packaging/update-shas.sh` must derive version, release URLs, checksums, and release dates from the actual published release or current workflow input. It must not depend on `github.ref_name` when the workflow can run from `workflow_dispatch` on `main`.
136+
- Post-release jobs must run both for stable tag pushes and for non-draft manual dispatches that publish or overwrite release assets. If a workflow can overwrite release files, the packaging/Homebrew/Scoop/winget sync jobs must also run on that path.
137+
- `packaging/homebrew/Casks/open-codesign.rb`, `packaging/scoop/bucket/open-codesign.json`, `packaging/winget/**`, and `packaging/flatpak/**` must match the current release assets and `SHA256SUMS.txt`. Hash drift in any public install channel is a **Blocker** because users will get checksum failures or install the wrong artifact.
138+
- Homebrew and Scoop downstream repos are separate public install sources. If the PR changes generated manifests or release assets, check whether the workflow or release process updates `OpenCoworkAI/homebrew-tap` and `OpenCoworkAI/scoop-bucket`; otherwise call out the gap.
139+
- winget manifests should match the schema version used by the published package series and include fields required by winget validation, especially `ReleaseDate` in installer manifests when previous published versions have it. Installer URLs and `InstallerSha256` must match the current GitHub Release assets, not stale assets from an earlier rerun.
140+
- For release PRs, compare `SHA256SUMS.txt`, attached assets, in-repo manifests, external channel manifests, and workflow triggers as one system. A fix that updates only one channel while leaving another channel stale is incomplete.
141+
142+
If the validation context is unavailable in the public checkout, say exactly what could not be verified and treat the risk conservatively. Do not claim a release/distribution PR is safe unless the artifact/checksum/update path is internally consistent.
143+
129144
## Severity And Noise Control
130145

131146
Use severity to reflect merge risk, not personal taste:

.github/scripts/deepseek-pr-review.mjs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,93 @@ function serializeExcerpts(excerpts) {
5757
return excerpts.map((entry) => `## ${entry.path}\n${entry.content}`).join('\n\n');
5858
}
5959

60+
function touchesReleasePath(files) {
61+
return files.some((file) => {
62+
const name = file.filename;
63+
return (
64+
name === '.github/workflows/release.yml' ||
65+
name === '.github/workflows/packaging-smoke.yml' ||
66+
name.startsWith('packaging/') ||
67+
name.startsWith('.github/releases/') ||
68+
name === 'CHANGELOG.md'
69+
);
70+
});
71+
}
72+
73+
function loadReleaseContext(files) {
74+
if (!touchesReleasePath(files)) {
75+
return 'No release or distribution files changed.';
76+
}
77+
78+
const paths = [
79+
'.github/workflows/release.yml',
80+
'.github/workflows/packaging-smoke.yml',
81+
'packaging/update-shas.sh',
82+
'packaging/README.md',
83+
'packaging/homebrew/Casks/open-codesign.rb',
84+
'packaging/scoop/bucket/open-codesign.json',
85+
'packaging/winget/manifests/o/OpenCoworkAI/OpenCoDesign/0.2.0/OpenCoworkAI.OpenCoDesign.yaml',
86+
'packaging/winget/manifests/o/OpenCoworkAI/OpenCoDesign/0.2.0/OpenCoworkAI.OpenCoDesign.installer.yaml',
87+
'packaging/winget/manifests/o/OpenCoworkAI/OpenCoDesign/0.2.0/OpenCoworkAI.OpenCoDesign.locale.en-US.yaml',
88+
'packaging/flatpak/ai.opencowork.codesign.yaml',
89+
];
90+
91+
const localContext = loadRepoDocs(paths, 9000);
92+
let releaseChecksums = '';
93+
try {
94+
releaseChecksums = runGh([
95+
'release',
96+
'download',
97+
'v0.2.0',
98+
'-R',
99+
'OpenCoworkAI/open-codesign',
100+
'--pattern',
101+
'SHA256SUMS.txt',
102+
'--output',
103+
'-',
104+
]);
105+
} catch (error) {
106+
releaseChecksums = `Could not load v0.2.0 SHA256SUMS.txt: ${error.message}`;
107+
}
108+
109+
let homebrew = '';
110+
try {
111+
homebrew = runGh([
112+
'api',
113+
'repos/OpenCoworkAI/homebrew-tap/contents/Casks/open-codesign.rb',
114+
'--jq',
115+
'.content',
116+
]);
117+
homebrew = Buffer.from(homebrew, 'base64').toString('utf8');
118+
} catch (error) {
119+
homebrew = `Could not load live Homebrew tap cask: ${error.message}`;
120+
}
121+
122+
let scoop = '';
123+
try {
124+
scoop = runGh([
125+
'api',
126+
'repos/OpenCoworkAI/scoop-bucket/contents/bucket/open-codesign.json',
127+
'--jq',
128+
'.content',
129+
]);
130+
scoop = Buffer.from(scoop, 'base64').toString('utf8');
131+
} catch (error) {
132+
scoop = `Could not load live Scoop bucket manifest: ${error.message}`;
133+
}
134+
135+
return [
136+
'Release/distribution validation context:',
137+
serializeDocs(localContext),
138+
'## GitHub Release v0.2.0 SHA256SUMS.txt',
139+
releaseChecksums,
140+
'## Live OpenCoworkAI/homebrew-tap Cask',
141+
homebrew,
142+
'## Live OpenCoworkAI/scoop-bucket manifest',
143+
scoop,
144+
].join('\n\n');
145+
}
146+
60147
function loadPullRequestDiff(prNumber, repo, prMeta) {
61148
try {
62149
return runGh(['pr', 'diff', prNumber, '-R', repo]);
@@ -128,6 +215,7 @@ async function main() {
128215
8,
129216
5000,
130217
);
218+
const releaseContext = loadReleaseContext(files);
131219

132220
let followUpContext = 'None.';
133221
if (isFollowUpReview && latestBotReviewId) {
@@ -176,6 +264,9 @@ async function main() {
176264
'PR head file excerpts:',
177265
serializeExcerpts(excerpts),
178266
'',
267+
'Release/distribution context:',
268+
truncate(releaseContext, 60000, 'release distribution context'),
269+
'',
179270
'Unified diff:',
180271
truncate(diff, 140000, 'PR diff'),
181272
'',

0 commit comments

Comments
 (0)