chore(release): refresh cross-platform FFmpeg payloads

This commit is contained in:
NimBold
2026-07-15 00:11:04 +03:30
parent e8487ee71b
commit 0eee47b97f
5 changed files with 61 additions and 32 deletions
+8 -7
View File
@@ -11,7 +11,7 @@ Targets:
## Distribution policy ## Distribution policy
Firelink does not use an Apple Developer account. macOS releases are unsigned and not notarized. Users must explicitly approve the downloaded app through Finder or macOS Privacy & Security. Release copy must never describe these builds as signed, notarized, or Gatekeeper-approved. Firelink does not use an Apple Developer account. macOS releases are ad-hoc signed but not notarized or Gatekeeper-approved. Users may still need to explicitly approve the downloaded app through Finder or macOS Privacy & Security. Release copy must not describe these builds as Developer ID signed, notarized, or Gatekeeper-approved.
Windows releases are currently unsigned. SmartScreen may warn until code signing is added. Windows releases are currently unsigned. SmartScreen may warn until code signing is added.
@@ -58,10 +58,12 @@ node scripts/verify-binaries.js --search-root "$APP" --target aarch64-apple-darw
node scripts/smoke-packaged-app.js --executable "$APP/Contents/MacOS/firelink" node scripts/smoke-packaged-app.js --executable "$APP/Contents/MacOS/firelink"
``` ```
GitHub release publication is intentionally manual. Tag pushes build and upload GitHub release publication follows `.github/workflows/release.yml`. A `v*` tag
artifacts, but the `publish` job only runs from a `workflow_dispatch` on a `v*` push builds, verifies, and publishes the GitHub release after the platform jobs
tag when both release-certification inputs are checked after Windows, Linux, pass. A `workflow_dispatch` on a `v*` tag also publishes when its
and macOS clean-machine QA. `publish_release` input is enabled. The current workflow has no separate
release-certification inputs; clean-machine QA remains a release-owner gate
before pushing the tag.
## Automated release builds ## Automated release builds
@@ -74,7 +76,6 @@ git push origin v<version>
GitHub Actions builds all targets on native runners, verifies engines inside GitHub Actions builds all targets on native runners, verifies engines inside
final package contents, performs packaged launch smoke where supported, and final package contents, performs packaged launch smoke where supported, and
uploads artifacts. Publish the GitHub Release with a manual `workflow_dispatch` publishes the GitHub Release after the build matrix passes.
run on the same tag after clean-machine QA is complete.
No target may silently skip missing engines, failed extraction, checksum mismatch, or missing package output. No target may silently skip missing engines, failed extraction, checksum mismatch, or missing package output.
+4 -4
View File
@@ -14,8 +14,8 @@
}, },
"ffmpeg": { "ffmpeg": {
"version": "8.1.2-22-g94138f6973", "version": "8.1.2-22-g94138f6973",
"url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-07-06-14-19/ffmpeg-n8.1.2-22-g94138f6973-win64-gpl-8.1.zip", "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-07-14-13-19/ffmpeg-n8.1.2-22-g94138f6973-win64-gpl-8.1.zip",
"sha256": "a758e2836a8f33c5f21fc44270cb00392acc6d0085dd0ba14fe14ae75935813d" "sha256": "fb627ebbf9b16a3142fa48b8acd27cd9196d57aee1f719e1233416e95da7e877"
}, },
"aria2c": { "aria2c": {
"version": "1.37.0", "version": "1.37.0",
@@ -36,8 +36,8 @@
}, },
"ffmpeg": { "ffmpeg": {
"version": "8.1.2-22-g94138f6973", "version": "8.1.2-22-g94138f6973",
"url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-07-06-14-19/ffmpeg-n8.1.2-22-g94138f6973-linux64-gpl-8.1.tar.xz", "url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-07-14-13-19/ffmpeg-n8.1.2-22-g94138f6973-linux64-gpl-8.1.tar.xz",
"sha256": "df99ffb3803ee56dc68954f43f950ea9f33685a3595a5da8a3e73ef4bef37e3c" "sha256": "96cf2585a4b2874044320cbc5d4078af57e266f0c86cca9888507c5faab3aee8"
}, },
"aria2c": { "aria2c": {
"version": "1.37.0", "version": "1.37.0",
+3 -2
View File
@@ -16,10 +16,11 @@
"sha256": "111b2f5ed760f1e1a2ec06117c4e8094fcde336ba16122dda1c5e7209bf1862d" "sha256": "111b2f5ed760f1e1a2ec06117c4e8094fcde336ba16122dda1c5e7209bf1862d"
}, },
"ffmpeg": { "ffmpeg": {
"version": "N-125450-gfad2e0bc50", "version": "N-125610-g312c830916",
"source": "https://ffmpeg.org/", "source": "https://ffmpeg.org/",
"build": "GPLv3 build identified by binary as https://www.martin-riedl.de", "build": "GPLv3 build identified by binary as https://www.martin-riedl.de",
"sha256": "be2c39e5c9ef923f60da6cb62f5a209ed98b4da8a732d9f06de4355d5ea99e58" "url": "https://ffmpeg.martin-riedl.de/download/macos/arm64/1784052810_N-125610-g312c830916/ffmpeg.zip",
"sha256": "f65b4ba782b25e9f4374765d421793c6613f692d8e50b2888079657793619034"
}, },
"deno": { "deno": {
"version": "2.9.2", "version": "2.9.2",
+46 -19
View File
@@ -83,22 +83,38 @@ async function latestMartinRiedlMacArm64Release() {
async function latestMartinRiedlMacArm64Snapshot() { async function latestMartinRiedlMacArm64Snapshot() {
const html = await fetchText('https://ffmpeg.martin-riedl.de/'); const html = await fetchText('https://ffmpeg.martin-riedl.de/');
const snapshotSection = html.split('Download Snapshot Build')[1]?.split('Download Release Build')[0] || ''; const snapshotSection = html.split('Download Snapshot Build')[1]?.split('Download Release Build')[0] || '';
const card = snapshotSection.match(/<h3>macOS \(Apple Silicon\/arm64\)<\/h3>[\s\S]*?<\/div>/)?.[0] || '';
const match = const match =
snapshotSection.match(/macOS \(Apple Silicon\/arm64\)[\s\S]*?<b>Release:\s*<\/b>\s*([A-Za-z0-9.-]+)/) || card.match(/<b>Release:\s*<\/b>\s*([A-Za-z0-9.-]+)/) ||
snapshotSection.match(/macOS \(Apple Silicon\/arm64\)[\s\S]*?Release:\s*([A-Za-z0-9.-]+)/); card.match(/Release:\s*([A-Za-z0-9.-]+)/);
return match?.[1]; const url = card.match(/href="([^"]+\/ffmpeg\.zip)"/)?.[1];
return match?.[1]
? { version: match[1], url: url ? new URL(url, 'https://ffmpeg.martin-riedl.de').href : undefined }
: undefined;
} }
async function latestBtbnFfmpegN81Build() { async function latestBtbnFfmpegN81Build() {
const releases = await fetchJson('https://api.github.com/repos/BtbN/FFmpeg-Builds/releases?per_page=10'); const releases = await fetchJson('https://api.github.com/repos/BtbN/FFmpeg-Builds/releases?per_page=10');
for (const release of releases) { for (const release of releases) {
if (release.tag_name === 'latest') continue; if (release.tag_name === 'latest') continue;
const versions = (release.assets || []) const assets = (release.assets || [])
.map(asset => asset.name.match(/^ffmpeg-n(8\.1\.\d+-\d+-g[0-9a-f]+)-(?:win64-gpl-8\.1\.zip|linux64-gpl-8\.1\.tar\.xz)$/)?.[1]) .map(asset => {
const match = asset.name.match(/^ffmpeg-n(8\.1\.\d+-\d+-g[0-9a-f]+)-(win64|linux64)-gpl-8\.1\.(?:zip|tar\.xz)$/);
if (!match) return undefined;
return {
target: match[2] === 'win64' ? 'windows' : 'linux',
version: match[1],
url: asset.browser_download_url,
};
})
.filter(Boolean); .filter(Boolean);
const unique = [...new Set(versions)]; const unique = [...new Set(assets.map(asset => asset.version))];
if (unique.length === 1 && versions.length >= 2) { const byTarget = Object.fromEntries(assets.map(asset => [asset.target, asset]));
return unique[0]; if (unique.length === 1 && byTarget.windows && byTarget.linux) {
return {
version: unique[0],
urls: { windows: byTarget.windows.url, linux: byTarget.linux.url },
};
} }
} }
return undefined; return undefined;
@@ -131,22 +147,26 @@ function packagedEngineVersions(engineLock) {
const rows = []; const rows = [];
for (const [target, targetLock] of Object.entries(engineLock.targets || {})) { for (const [target, targetLock] of Object.entries(engineLock.targets || {})) {
for (const [engine, meta] of Object.entries(targetLock.engines || {})) { for (const [engine, meta] of Object.entries(targetLock.engines || {})) {
rows.push({ target, engine, version: meta.version }); rows.push({ target, engine, version: meta.version, url: meta.url });
} }
} }
return rows; return rows;
} }
function checkRows(rows, latestByEngine, latestByTargetEngine = {}) { function checkRows(rows, latestByEngine, latestByTargetEngine = {}, latestUrlsByTargetEngine = {}) {
let outdated = 0; let outdated = 0;
for (const row of rows) { for (const row of rows) {
const latest = latestByTargetEngine[`${row.target}:${row.engine}`] || latestByEngine[row.engine]; const latest = latestByTargetEngine[`${row.target}:${row.engine}`] || latestByEngine[row.engine];
if (!latest) continue; if (!latest) continue;
const current = normalizeVersion(row.version); const current = normalizeVersion(row.version);
const wanted = normalizeVersion(latest); const wanted = normalizeVersion(latest);
const status = compareVersions(current, wanted) < 0 ? 'outdated' : 'current'; const latestUrl = latestUrlsByTargetEngine[`${row.target}:${row.engine}`];
if (status === 'outdated') outdated += 1; const versionOutdated = compareVersions(current, wanted) < 0;
const sourceOutdated = Boolean(latestUrl && row.url && row.url !== latestUrl);
const status = versionOutdated ? 'outdated' : sourceOutdated ? 'source-outdated' : 'current';
if (status !== 'current') outdated += 1;
console.log(` ${row.target} ${row.engine}: ${current} -> ${wanted} ${status}`); console.log(` ${row.target} ${row.engine}: ${current} -> ${wanted} ${status}`);
if (sourceOutdated) console.log(` source: ${row.url} -> ${latestUrl}`);
} }
return outdated; return outdated;
} }
@@ -184,9 +204,14 @@ async function main() {
ffmpeg, ffmpeg,
}; };
const latestByTargetEngine = { const latestByTargetEngine = {
'x86_64-pc-windows-msvc:ffmpeg': btbnFfmpegN81Build || ffmpeg, 'x86_64-pc-windows-msvc:ffmpeg': btbnFfmpegN81Build?.version || ffmpeg,
'x86_64-unknown-linux-gnu:ffmpeg': btbnFfmpegN81Build || ffmpeg, 'x86_64-unknown-linux-gnu:ffmpeg': btbnFfmpegN81Build?.version || ffmpeg,
'aarch64-apple-darwin:ffmpeg': martinRiedlMacArm64Snapshot || martinRiedlMacArm64Ffmpeg, 'aarch64-apple-darwin:ffmpeg': martinRiedlMacArm64Snapshot?.version || martinRiedlMacArm64Ffmpeg,
};
const latestUrlsByTargetEngine = {
'x86_64-pc-windows-msvc:ffmpeg': btbnFfmpegN81Build?.urls.windows,
'x86_64-unknown-linux-gnu:ffmpeg': btbnFfmpegN81Build?.urls.linux,
'aarch64-apple-darwin:ffmpeg': martinRiedlMacArm64Snapshot?.url,
}; };
console.log('\nlatest engines:'); console.log('\nlatest engines:');
@@ -194,21 +219,23 @@ async function main() {
console.log(` ${engine}: ${normalizeVersion(version)}`); console.log(` ${engine}: ${normalizeVersion(version)}`);
} }
console.log('\nlatest engine provider builds:'); console.log('\nlatest engine provider builds:');
console.log(` BtbN FFmpeg n8.1 Windows/Linux: ${normalizeVersion(btbnFfmpegN81Build || ffmpeg)}`); console.log(` BtbN FFmpeg n8.1 Windows/Linux: ${normalizeVersion(btbnFfmpegN81Build?.version || ffmpeg)}`);
console.log(` Martin Riedl FFmpeg macOS arm64 snapshot: ${normalizeVersion(martinRiedlMacArm64Snapshot || martinRiedlMacArm64Ffmpeg)}`); console.log(` Martin Riedl FFmpeg macOS arm64 snapshot: ${normalizeVersion(martinRiedlMacArm64Snapshot?.version || martinRiedlMacArm64Ffmpeg)}`);
console.log('\nengine source lock:'); console.log('\nengine source lock:');
outdatedCount += checkRows( outdatedCount += checkRows(
sourceEngineVersions(parseJsonFile('engine-sources.lock.json')), sourceEngineVersions(parseJsonFile('engine-sources.lock.json')),
latestByEngine, latestByEngine,
latestByTargetEngine latestByTargetEngine,
latestUrlsByTargetEngine
); );
console.log('\npackaged engine lock:'); console.log('\npackaged engine lock:');
outdatedCount += checkRows( outdatedCount += checkRows(
packagedEngineVersions(parseJsonFile('engines.lock.json')), packagedEngineVersions(parseJsonFile('engines.lock.json')),
latestByEngine, latestByEngine,
latestByTargetEngine latestByTargetEngine,
latestUrlsByTargetEngine
); );
if (outdatedCount > 0) { if (outdatedCount > 0) {
Binary file not shown.