Skip to content

Commit 0c68d9d

Browse files
authored
Merge pull request #149 from beNative/codex/fix-publish-release-workflow-failure
Fix auto-update metadata lookup for renamed Windows installers
2 parents f1c89f8 + 958de6b commit 0c68d9d

2 files changed

Lines changed: 90 additions & 4 deletions

File tree

scripts/__tests__/release-workflow.test.mjs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,47 @@ test('local verification can repair mismatched metadata when requested', async (
310310
assert.equal(updated.files[0].size, installerBuffer.length);
311311
});
312312

313+
test('local verification resolves renamed Windows installers when metadata lacks architecture suffix', async (t) => {
314+
const workspace = await createTemporaryWorkspace(t);
315+
const version = '0.0.4';
316+
const releaseDir = path.join(workspace, 'release-artifacts', 'docforge-windows-ia32');
317+
await fs.mkdir(releaseDir, { recursive: true });
318+
319+
const installerName = `DocForge-Setup-${version}-ia32.exe`;
320+
const installerPath = path.join(releaseDir, installerName);
321+
const binary = crypto.randomBytes(4096);
322+
await fs.writeFile(installerPath, binary);
323+
324+
const metadataPath = path.join(releaseDir, 'latest.yml');
325+
const metadata = {
326+
version,
327+
path: `DocForge-Setup-${version}.exe`,
328+
sha512: 'mismatched',
329+
files: [
330+
{
331+
url: `DocForge-Setup-${version}.exe`,
332+
sha512: 'mismatched',
333+
size: 123,
334+
},
335+
],
336+
};
337+
await fs.writeFile(metadataPath, YAML.stringify(metadata), 'utf8');
338+
339+
await runLocalVerification(releaseDir, ['--fix-metadata']);
340+
341+
const expectedSha = computeSha512Base64(binary);
342+
const updated = YAML.parse(await fs.readFile(metadataPath, 'utf8'));
343+
assert.equal(updated.path, installerName);
344+
assert.equal(updated.sha512, expectedSha);
345+
if (Object.prototype.hasOwnProperty.call(updated, 'size')) {
346+
assert.equal(updated.size, binary.length);
347+
}
348+
assert(Array.isArray(updated.files) && updated.files.length === 1);
349+
assert.equal(updated.files[0].url, installerName);
350+
assert.equal(updated.files[0].sha512, expectedSha);
351+
assert.equal(updated.files[0].size, binary.length);
352+
});
353+
313354
test('metadata updates remain isolated across artifact directories with identical installer names', async (t) => {
314355
const workspace = await createTemporaryWorkspace(t);
315356
const version = '0.0.3';

scripts/test-auto-update.mjs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,17 @@ async function resolveLocalAsset(metadataDir, key, digestCache) {
139139
addCandidate(normalised);
140140
}
141141

142-
for (const candidate of candidates) {
142+
const tryResolveCandidate = async (candidate) => {
143143
const candidatePath = path.resolve(metadataDir, candidate);
144144
const relative = path.relative(metadataDir, candidatePath);
145145
if (relative.startsWith('..') || path.isAbsolute(relative)) {
146-
continue;
146+
return null;
147147
}
148148

149149
try {
150150
const stats = await fs.stat(candidatePath);
151151
if (!stats.isFile()) {
152-
continue;
152+
return null;
153153
}
154154

155155
let digest = digestCache.get(candidatePath);
@@ -165,7 +165,52 @@ async function resolveLocalAsset(metadataDir, key, digestCache) {
165165
size: digest.size,
166166
};
167167
} catch {
168-
// Ignore missing assets at this stage; verification will surface the issue.
168+
return null;
169+
}
170+
};
171+
172+
let index = 0;
173+
while (index < candidates.length) {
174+
const candidate = candidates[index];
175+
index += 1;
176+
const asset = await tryResolveCandidate(candidate);
177+
if (asset) {
178+
return asset;
179+
}
180+
}
181+
182+
const extension = normalised ? path.extname(normalised) : path.extname(key ?? '');
183+
const extensionLower = extension.toLowerCase();
184+
const canonicalBase = extension ? (normalised || key).slice(0, -extension.length) : null;
185+
186+
if (canonicalBase && extensionLower === '.exe') {
187+
let entries = [];
188+
try {
189+
entries = await fs.readdir(metadataDir);
190+
} catch {
191+
entries = [];
192+
}
193+
194+
for (const entry of entries) {
195+
if (seen.has(entry)) {
196+
continue;
197+
}
198+
if (!entry.toLowerCase().endsWith(extensionLower)) {
199+
continue;
200+
}
201+
const baseName = entry.slice(0, -extension.length);
202+
if (baseName === canonicalBase || baseName.startsWith(`${canonicalBase}-`)) {
203+
addCandidate(entry);
204+
}
205+
}
206+
207+
while (index < candidates.length) {
208+
const candidate = candidates[index];
209+
index += 1;
210+
const asset = await tryResolveCandidate(candidate);
211+
if (asset) {
212+
return asset;
213+
}
169214
}
170215
}
171216

0 commit comments

Comments
 (0)