mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-24 05:16:19 -07:00
chore(release): remove package size reports
- Drop the afterAllArtifactBuild size reporting hook and the previous-release baseline downloads in CI - Stop uploading, checksumming, and publishing package-size-*.json - Keep the afterPack package content audit, now without size tracking - Update RELEASING.md and the verification docs, and add a changelog fragment
This commit is contained in:
@@ -59,17 +59,6 @@ jobs:
|
|||||||
bun install --frozen-lockfile
|
bun install --frozen-lockfile
|
||||||
bun run build
|
bun run build
|
||||||
|
|
||||||
- name: Download previous package size reports
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
mkdir -p .tmp/package-baseline
|
|
||||||
previous=$(gh api "repos/$GITHUB_REPOSITORY/releases" --jq '[.[] | select(.draft == false and .tag_name != env.GITHUB_REF_NAME)] | sort_by(.published_at) | last | .tag_name // empty')
|
|
||||||
if [ -n "$previous" ]; then
|
|
||||||
gh release download "$previous" --pattern 'package-size-*.json' --dir .tmp/package-baseline || echo 'Previous release has no package size reports; size comparison will be skipped.'
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
run: bun run build:appimage
|
run: bun run build:appimage
|
||||||
env:
|
env:
|
||||||
@@ -98,7 +87,6 @@ jobs:
|
|||||||
release/*.AppImage
|
release/*.AppImage
|
||||||
release/latest*.yml
|
release/latest*.yml
|
||||||
release/*.blockmap
|
release/*.blockmap
|
||||||
release/package-size-*.json
|
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
@@ -159,17 +147,6 @@ jobs:
|
|||||||
bun install --frozen-lockfile
|
bun install --frozen-lockfile
|
||||||
bun run build
|
bun run build
|
||||||
|
|
||||||
- name: Download previous package size reports
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
mkdir -p .tmp/package-baseline
|
|
||||||
previous=$(gh api "repos/$GITHUB_REPOSITORY/releases" --jq '[.[] | select(.draft == false and .tag_name != env.GITHUB_REF_NAME)] | sort_by(.published_at) | last | .tag_name // empty')
|
|
||||||
if [ -n "$previous" ]; then
|
|
||||||
gh release download "$previous" --pattern 'package-size-*.json' --dir .tmp/package-baseline || echo 'Previous release has no package size reports; size comparison will be skipped.'
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build signed + notarized macOS artifacts
|
- name: Build signed + notarized macOS artifacts
|
||||||
run: bun run build:mac
|
run: bun run build:mac
|
||||||
env:
|
env:
|
||||||
@@ -193,7 +170,6 @@ jobs:
|
|||||||
release/*.zip
|
release/*.zip
|
||||||
release/latest*.yml
|
release/latest*.yml
|
||||||
release/*.blockmap
|
release/*.blockmap
|
||||||
release/package-size-*.json
|
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
@@ -235,17 +211,6 @@ jobs:
|
|||||||
bun install --frozen-lockfile
|
bun install --frozen-lockfile
|
||||||
bun run build
|
bun run build
|
||||||
|
|
||||||
- name: Download previous package size reports
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
mkdir -p .tmp/package-baseline
|
|
||||||
previous=$(gh api "repos/$GITHUB_REPOSITORY/releases" --jq '[.[] | select(.draft == false and .tag_name != env.GITHUB_REF_NAME)] | sort_by(.published_at) | last | .tag_name // empty')
|
|
||||||
if [ -n "$previous" ]; then
|
|
||||||
gh release download "$previous" --pattern 'package-size-*.json' --dir .tmp/package-baseline || echo 'Previous release has no package size reports; size comparison will be skipped.'
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Verify managed Windows launcher
|
- name: Verify managed Windows launcher
|
||||||
run: bun test src/main/runtime/managed-launcher.test.ts
|
run: bun test src/main/runtime/managed-launcher.test.ts
|
||||||
|
|
||||||
@@ -270,5 +235,4 @@ jobs:
|
|||||||
release/*.zip
|
release/*.zip
|
||||||
release/latest*.yml
|
release/latest*.yml
|
||||||
release/*.blockmap
|
release/*.blockmap
|
||||||
release/package-size-*.json
|
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd)
|
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd)
|
||||||
files+=(release/package-size-*.json)
|
|
||||||
if [ "${#files[@]}" -eq 0 ]; then
|
if [ "${#files[@]}" -eq 0 ]; then
|
||||||
echo "No release artifacts found for checksum generation."
|
echo "No release artifacts found for checksum generation."
|
||||||
exit 1
|
exit 1
|
||||||
@@ -152,7 +151,6 @@ jobs:
|
|||||||
release/latest*.yml
|
release/latest*.yml
|
||||||
release/*.blockmap
|
release/*.blockmap
|
||||||
release/SHA256SUMS.txt
|
release/SHA256SUMS.txt
|
||||||
release/package-size-*.json
|
|
||||||
dist/launcher/subminer
|
dist/launcher/subminer
|
||||||
dist/launcher/subminer.cmd
|
dist/launcher/subminer.cmd
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -106,7 +106,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd)
|
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz release/latest*.yml release/*.blockmap dist/launcher/subminer dist/launcher/subminer.cmd)
|
||||||
files+=(release/package-size-*.json)
|
|
||||||
if [ "${#files[@]}" -eq 0 ]; then
|
if [ "${#files[@]}" -eq 0 ]; then
|
||||||
echo "No release artifacts found for checksum generation."
|
echo "No release artifacts found for checksum generation."
|
||||||
exit 1
|
exit 1
|
||||||
@@ -171,7 +170,6 @@ jobs:
|
|||||||
release/latest*.yml
|
release/latest*.yml
|
||||||
release/*.blockmap
|
release/*.blockmap
|
||||||
release/SHA256SUMS.txt
|
release/SHA256SUMS.txt
|
||||||
release/package-size-*.json
|
|
||||||
dist/launcher/subminer
|
dist/launcher/subminer
|
||||||
dist/launcher/subminer.cmd
|
dist/launcher/subminer.cmd
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type: changed
|
||||||
|
area: release
|
||||||
|
|
||||||
|
- Removed package-size JSON reports from future releases and their CI size comparisons. Package-content validation remains enabled.
|
||||||
+4
-14
@@ -11,7 +11,7 @@
|
|||||||
`ANTHROPIC_API_KEY` works. Install from <https://claude.com/claude-code> if
|
`ANTHROPIC_API_KEY` works. Install from <https://claude.com/claude-code> if
|
||||||
you don't already have it.
|
you don't already have it.
|
||||||
|
|
||||||
## Package contents and size checks
|
## Package contents checks
|
||||||
|
|
||||||
Stable and prerelease workflows share `.github/workflows/package-release.yml`.
|
Stable and prerelease workflows share `.github/workflows/package-release.yml`.
|
||||||
Both callers explicitly pass the five required macOS signing/notarization
|
Both callers explicitly pass the five required macOS signing/notarization
|
||||||
@@ -19,11 +19,9 @@ secrets plus the optional `SUBMINER_TMDB_API_KEY` (the project TMDB key that
|
|||||||
`scripts/prepare-build-assets.mjs` stages into `dist/bundled-integration-keys.json`;
|
`scripts/prepare-build-assets.mjs` stages into `dist/bundled-integration-keys.json`;
|
||||||
artifacts built without it simply require users to set `tmdb.apiKey`).
|
artifacts built without it simply require users to set `tmdb.apiKey`).
|
||||||
`GITHUB_TOKEN` remains automatically available to the reusable workflow.
|
`GITHUB_TOKEN` remains automatically available to the reusable workflow.
|
||||||
Each platform verifies its ASAR and external resources before signing, then
|
Each platform verifies its ASAR and external resources before signing. Missing
|
||||||
measures the signed app and installers before upload. Missing runtime assets,
|
runtime assets, foreign SQLite/Koffi binaries, duplicate UI fonts, demo media,
|
||||||
foreign SQLite/Koffi binaries, duplicate UI fonts, demo media, source maps,
|
source maps, TypeScript files, and nested test or fixture directories fail the build.
|
||||||
TypeScript files, and nested test or fixture directories
|
|
||||||
fail the build. Size measurements are informational and do not block releases.
|
|
||||||
Current targets are Linux x64, macOS arm64, and Windows x64.
|
Current targets are Linux x64, macOS arm64, and Windows x64.
|
||||||
|
|
||||||
The runtime allowlist includes `dist/`, `stats/dist/`, and
|
The runtime allowlist includes `dist/`, `stats/dist/`, and
|
||||||
@@ -34,14 +32,6 @@ exclusion-only platform list as a separate include-all matcher. Windows keeps
|
|||||||
only its target Koffi binary; other platforms omit Koffi. Desktop UIs share the
|
only its target Koffi binary; other platforms omit Koffi. Desktop UIs share the
|
||||||
original M PLUS 1 TTF in `dist/fonts/`.
|
original M PLUS 1 TTF in `dist/fonts/`.
|
||||||
|
|
||||||
`release/package-size-<platform>-<arch>.json` reports unpacked bytes, largest
|
|
||||||
files inside and outside ASAR, native binaries, and compressed artifact sizes.
|
|
||||||
Framework symlinks are not counted twice. Reports are checksummed and published.
|
|
||||||
CI downloads the preceding release's reports for comparison; older releases
|
|
||||||
without reports skip comparison. Review the inventory and reason for growth
|
|
||||||
when comparing releases. An AppImage normally
|
|
||||||
runs compressed; its extracted size is a separate measurement.
|
|
||||||
|
|
||||||
The shared workflow runs `bun run test:package <resources-directory>` with the
|
The shared workflow runs `bun run test:package <resources-directory>` with the
|
||||||
pinned Electron runtime and temporary user data. On headless Linux, prefix it
|
pinned Electron runtime and temporary user data. On headless Linux, prefix it
|
||||||
with `xvfb-run -a`. This checks packaged SQLite, Windows FFI loading/polling,
|
with `xvfb-run -a`. This checks packaged SQLite, Windows FFI loading/polling,
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ bun run docs:build
|
|||||||
- Build/release scripts (`scripts/**`): `bun run test:scripts`
|
- Build/release scripts (`scripts/**`): `bun run test:scripts`
|
||||||
- Packaging: build the platform package, then run `bun run test:package <resources-directory>`.
|
- Packaging: build the platform package, then run `bun run test:package <resources-directory>`.
|
||||||
On headless Linux: `xvfb-run -a bun run test:package release/linux-unpacked/resources`.
|
On headless Linux: `xvfb-run -a bun run test:package release/linux-unpacked/resources`.
|
||||||
Content checks and informational size reporting run inside electron-builder hooks. See the
|
Content checks run inside the electron-builder afterPack hook. See the
|
||||||
[release guide](../RELEASING.md#package-contents-and-size-checks) for size reports
|
[release guide](../RELEASING.md#package-contents-checks) for the
|
||||||
and the installed-app verification checklist.
|
installed-app verification checklist.
|
||||||
- Coverage for the maintained source lane: `bun run test:coverage:src`
|
- Coverage for the maintained source lane: `bun run test:coverage:src`
|
||||||
- Deep/local full gate: default handoff gate above
|
- Deep/local full gate: default handoff gate above
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -269,8 +269,7 @@
|
|||||||
"from": "CHANGELOG.md",
|
"from": "CHANGELOG.md",
|
||||||
"to": "CHANGELOG.md"
|
"to": "CHANGELOG.md"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"afterAllArtifactBuild": "scripts/package-audit.cjs"
|
|
||||||
},
|
},
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"@discordjs/rest@2.6.1": "patches/@discordjs%2Frest@2.6.1.patch"
|
"@discordjs/rest@2.6.1": "patches/@discordjs%2Frest@2.6.1.patch"
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ const assert = require('node:assert/strict');
|
|||||||
const asar = require('@electron/asar');
|
const asar = require('@electron/asar');
|
||||||
const { Arch } = require('builder-util');
|
const { Arch } = require('builder-util');
|
||||||
|
|
||||||
const MIB = 1024 * 1024;
|
|
||||||
const currentReports = new Set();
|
|
||||||
const REQUIRED_APP_FILES = [
|
const REQUIRED_APP_FILES = [
|
||||||
'package.json',
|
'package.json',
|
||||||
'LICENSE',
|
'LICENSE',
|
||||||
@@ -41,13 +39,13 @@ const REQUIRED_RESOURCES = [
|
|||||||
'CHANGELOG.md',
|
'CHANGELOG.md',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Do not follow framework symlinks or count ASAR unpacked entries twice.
|
// Skip symlinks when checking resource contents.
|
||||||
function listFiles(root, prefix = '') {
|
function listFiles(root, prefix = '') {
|
||||||
return fs.readdirSync(path.join(root, prefix), { withFileTypes: true }).flatMap((entry) => {
|
return fs.readdirSync(path.join(root, prefix), { withFileTypes: true }).flatMap((entry) => {
|
||||||
const name = prefix ? `${prefix}/${entry.name}` : entry.name;
|
const name = prefix ? `${prefix}/${entry.name}` : entry.name;
|
||||||
if (entry.isSymbolicLink()) return [];
|
if (entry.isSymbolicLink()) return [];
|
||||||
if (entry.isDirectory()) return listFiles(root, name);
|
if (entry.isDirectory()) return listFiles(root, name);
|
||||||
return [{ path: name, bytes: fs.statSync(path.join(root, name)).size }];
|
return [name];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +56,7 @@ function listAppFiles(archive) {
|
|||||||
const native = entry.replace(/^[\\/]/, '');
|
const native = entry.replace(/^[\\/]/, '');
|
||||||
const stat = asar.statFile(archive, native);
|
const stat = asar.statFile(archive, native);
|
||||||
const name = native.replaceAll('\\', '/');
|
const name = native.replaceAll('\\', '/');
|
||||||
return 'size' in stat ? [{ path: name, bytes: stat.size }] : [];
|
return 'size' in stat ? [name] : [];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,13 +97,13 @@ function verifyAppPath(name, platform, arch) {
|
|||||||
|
|
||||||
function verifyContents(archive, resources, platform, arch) {
|
function verifyContents(archive, resources, platform, arch) {
|
||||||
const entries = listAppFiles(archive);
|
const entries = listAppFiles(archive);
|
||||||
const names = new Set(entries.map((entry) => entry.path));
|
const names = new Set(entries);
|
||||||
for (const name of REQUIRED_APP_FILES) assert(names.has(name), `Missing app file: ${name}`);
|
for (const name of REQUIRED_APP_FILES) assert(names.has(name), `Missing app file: ${name}`);
|
||||||
for (const name of REQUIRED_RESOURCES) {
|
for (const name of REQUIRED_RESOURCES) {
|
||||||
assert(fs.statSync(path.join(resources, name)).size > 0, `Empty resource: ${name}`);
|
assert(fs.statSync(path.join(resources, name)).size > 0, `Empty resource: ${name}`);
|
||||||
}
|
}
|
||||||
assert(listFiles(path.join(resources, 'yomitan-jlpt-vocab')).length > 0, 'Missing JLPT data');
|
assert(listFiles(path.join(resources, 'yomitan-jlpt-vocab')).length > 0, 'Missing JLPT data');
|
||||||
for (const { path: name } of entries) verifyAppPath(name, platform, arch);
|
for (const name of entries) verifyAppPath(name, platform, arch);
|
||||||
const libsqlPlatform = {
|
const libsqlPlatform = {
|
||||||
linux: `linux-${arch}-gnu`,
|
linux: `linux-${arch}-gnu`,
|
||||||
darwin: `darwin-${arch}`,
|
darwin: `darwin-${arch}`,
|
||||||
@@ -129,7 +127,7 @@ function verifyContents(archive, resources, platform, arch) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const name of listFiles(path.join(resources, 'assets'))) {
|
for (const name of listFiles(path.join(resources, 'assets'))) {
|
||||||
assert(!name.path.startsWith('minecard'), `Demo media shipped: ${name.path}`);
|
assert(!name.startsWith('minecard'), `Demo media shipped: ${name}`);
|
||||||
}
|
}
|
||||||
for (const ui of ['renderer', 'settings', 'syncui']) {
|
for (const ui of ['renderer', 'settings', 'syncui']) {
|
||||||
const css = asar.extractFile(archive, path.join('dist', ui, 'style.css')).toString();
|
const css = asar.extractFile(archive, path.join('dist', ui, 'style.css')).toString();
|
||||||
@@ -147,92 +145,8 @@ async function auditPackage(context) {
|
|||||||
? path.join(context.appOutDir, `${context.packager.appInfo.productFilename}.app`)
|
? path.join(context.appOutDir, `${context.packager.appInfo.productFilename}.app`)
|
||||||
: context.appOutDir;
|
: context.appOutDir;
|
||||||
const resources = path.join(appRoot, platform === 'darwin' ? 'Contents/Resources' : 'resources');
|
const resources = path.join(appRoot, platform === 'darwin' ? 'Contents/Resources' : 'resources');
|
||||||
const appFiles = verifyContents(path.join(resources, 'app.asar'), resources, platform, arch);
|
verifyContents(path.join(resources, 'app.asar'), resources, platform, arch);
|
||||||
const files = listFiles(appRoot);
|
console.log(`Package contents verified: ${key}`);
|
||||||
const unpackedBytes = files.reduce((sum, entry) => sum + entry.bytes, 0);
|
|
||||||
const report = {
|
|
||||||
version: context.packager.appInfo.version,
|
|
||||||
platform,
|
|
||||||
arch,
|
|
||||||
unpackedBytes,
|
|
||||||
appDirectory: path.relative(context.outDir, appRoot),
|
|
||||||
largestFiles: [...files].sort((a, b) => b.bytes - a.bytes).slice(0, 25),
|
|
||||||
largestAppFiles: [...appFiles].sort((a, b) => b.bytes - a.bytes).slice(0, 25),
|
|
||||||
nativeBinaries: files.filter((entry) => /\.(node|dll|dylib)$|\.so(?:\.|$)/.test(entry.path)),
|
|
||||||
artifacts: [],
|
|
||||||
};
|
|
||||||
const output = path.join(context.outDir, `package-size-${key}.json`);
|
|
||||||
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
||||||
fs.writeFileSync(output, `${JSON.stringify(report, null, 2)}\n`);
|
|
||||||
currentReports.add(output);
|
|
||||||
console.log(
|
|
||||||
`Package contents verified: ${key}, ${(unpackedBytes / MIB).toFixed(2)} MiB unpacked`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function artifactKind(name) {
|
|
||||||
if (name.endsWith('-mac.zip')) return 'mac.zip';
|
|
||||||
if (name.endsWith('-win.zip')) return 'win.zip';
|
|
||||||
const extension = path.extname(name).slice(1);
|
|
||||||
return ['AppImage', 'dmg', 'exe'].includes(extension) ? extension : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function compareSizes(report, previous) {
|
|
||||||
assert.equal(previous.platform, report.platform);
|
|
||||||
assert.equal(previous.arch, report.arch);
|
|
||||||
assert(Number.isFinite(previous.unpackedBytes), 'Invalid previous size report');
|
|
||||||
const previousArtifacts = Array.isArray(previous.artifacts) ? previous.artifacts : [];
|
|
||||||
return {
|
|
||||||
version: previous.version,
|
|
||||||
unpackedDeltaBytes: report.unpackedBytes - previous.unpackedBytes,
|
|
||||||
artifacts: report.artifacts.flatMap((artifact) => {
|
|
||||||
const old = previousArtifacts.find(
|
|
||||||
(entry) => entry && entry.kind === artifact.kind && Number.isFinite(entry.bytes),
|
|
||||||
);
|
|
||||||
return old ? [{ kind: artifact.kind, deltaBytes: artifact.bytes - old.bytes }] : [];
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs after signing and installer creation, before release upload.
|
|
||||||
async function afterAllArtifactBuild(result) {
|
|
||||||
const reports = [];
|
|
||||||
for (const reportPath of currentReports) {
|
|
||||||
const filename = path.basename(reportPath);
|
|
||||||
const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
||||||
const key = `${report.platform}-${report.arch}`;
|
|
||||||
const files = listFiles(path.join(result.outDir, report.appDirectory));
|
|
||||||
report.unpackedBytes = files.reduce((sum, entry) => sum + entry.bytes, 0);
|
|
||||||
report.largestFiles = [...files].sort((a, b) => b.bytes - a.bytes).slice(0, 25);
|
|
||||||
report.artifacts = result.artifactPaths.flatMap((file) => {
|
|
||||||
const kind = artifactKind(file);
|
|
||||||
if (!kind) return [];
|
|
||||||
const bytes = fs.statSync(file).size;
|
|
||||||
return [{ name: path.basename(file), kind, bytes }];
|
|
||||||
});
|
|
||||||
const previousPath = path.join(result.outDir, '..', '.tmp', 'package-baseline', filename);
|
|
||||||
if (fs.existsSync(previousPath)) {
|
|
||||||
const previous = JSON.parse(fs.readFileSync(previousPath, 'utf8'));
|
|
||||||
report.comparison = compareSizes(report, previous);
|
|
||||||
}
|
|
||||||
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
||||||
const summary = [
|
|
||||||
`### Package size: ${key}`,
|
|
||||||
'',
|
|
||||||
`Unpacked: ${(report.unpackedBytes / MIB).toFixed(2)} MiB`,
|
|
||||||
...report.artifacts.map((entry) => `${entry.name}: ${(entry.bytes / MIB).toFixed(2)} MiB`),
|
|
||||||
report.comparison
|
|
||||||
? `Change from ${report.comparison.version}: ${(report.comparison.unpackedDeltaBytes / MIB).toFixed(2)} MiB unpacked`
|
|
||||||
: 'No previous size report available.',
|
|
||||||
'',
|
|
||||||
].join('\n');
|
|
||||||
console.log(summary);
|
|
||||||
if (process.env.GITHUB_STEP_SUMMARY)
|
|
||||||
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary);
|
|
||||||
reports.push(reportPath);
|
|
||||||
}
|
|
||||||
assert(reports.length > 0, 'No package size reports generated by afterPack');
|
|
||||||
return reports;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -241,6 +155,4 @@ module.exports = {
|
|||||||
verifyAppPath,
|
verifyAppPath,
|
||||||
listFiles,
|
listFiles,
|
||||||
listAppFiles,
|
listAppFiles,
|
||||||
compareSizes,
|
|
||||||
default: afterAllArtifactBuild,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import test from 'node:test';
|
|||||||
import { createPackageFromStreams } from '@electron/asar';
|
import { createPackageFromStreams } from '@electron/asar';
|
||||||
import { FileMatcher, getFileMatchers } from 'app-builder-lib/out/fileMatcher';
|
import { FileMatcher, getFileMatchers } from 'app-builder-lib/out/fileMatcher';
|
||||||
import config from '../package.json';
|
import config from '../package.json';
|
||||||
import { listAppFiles, listFiles, compareSizes, verifyAppPath } from './package-audit.cjs';
|
import { listAppFiles, listFiles, verifyAppPath } from './package-audit.cjs';
|
||||||
|
|
||||||
test('platform packaging preserves the runtime allowlist after builder normalizes global filters', () => {
|
test('platform packaging preserves the runtime allowlist after builder normalizes global filters', () => {
|
||||||
const root = process.cwd();
|
const root = process.cwd();
|
||||||
@@ -125,7 +125,7 @@ test('content audit rejects development files beneath approved roots', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('archive inventory handles native files without counting them twice on disk', async () => {
|
test('content inventory includes packed and unpacked native files', async () => {
|
||||||
const root = mkdtempSync(path.join(tmpdir(), 'subminer-audit-'));
|
const root = mkdtempSync(path.join(tmpdir(), 'subminer-audit-'));
|
||||||
try {
|
try {
|
||||||
const input = path.join(root, 'input');
|
const input = path.join(root, 'input');
|
||||||
@@ -147,35 +147,9 @@ test('archive inventory handles native files without counting them twice on disk
|
|||||||
streamGenerator: () => createReadStream(path.join(input, name)),
|
streamGenerator: () => createReadStream(path.join(input, name)),
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
assert.deepEqual(listAppFiles(archive), [
|
assert.deepEqual(listAppFiles(archive), ['main.js', 'native.node', 'dist/ai/client.js']);
|
||||||
{ path: 'main.js', bytes: 5 },
|
assert.deepEqual(listFiles(output).sort(), ['app.asar', 'app.asar.unpacked/native.node']);
|
||||||
{ path: 'native.node', bytes: 6 },
|
|
||||||
{ path: 'dist/ai/client.js', bytes: 6 },
|
|
||||||
]);
|
|
||||||
assert.equal(
|
|
||||||
listFiles(output).reduce((sum: number, entry: { bytes: number }) => sum + entry.bytes, 0),
|
|
||||||
statSync(archive).size + 6,
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
rmSync(root, { recursive: true, force: true });
|
rmSync(root, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('size comparison tolerates older reports without artifact measurements', () => {
|
|
||||||
const previous = { version: '0.19.6', platform: 'linux', arch: 'x64', unpackedBytes: 100 };
|
|
||||||
const current = { ...previous, unpackedBytes: 80, artifacts: [{ kind: 'AppImage', bytes: 40 }] };
|
|
||||||
assert.deepEqual(compareSizes(current, previous), {
|
|
||||||
version: '0.19.6',
|
|
||||||
unpackedDeltaBytes: -20,
|
|
||||||
artifacts: [],
|
|
||||||
});
|
|
||||||
assert.deepEqual(
|
|
||||||
compareSizes(current, { ...previous, artifacts: [null, { kind: 'AppImage', bytes: 50 }] })
|
|
||||||
.artifacts,
|
|
||||||
[{ kind: 'AppImage', deltaBytes: -10 }],
|
|
||||||
);
|
|
||||||
assert.throws(
|
|
||||||
() => compareSizes(current, { ...previous, unpackedBytes: 'unknown' }),
|
|
||||||
/Invalid previous size report/,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import assert from 'node:assert/strict';
|
|||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
import {
|
import {
|
||||||
executableRunLines,
|
|
||||||
jobSteps,
|
jobSteps,
|
||||||
readWorkflow,
|
readWorkflow,
|
||||||
stepRunsCommand,
|
stepRunsCommand,
|
||||||
@@ -109,20 +108,12 @@ test('prerelease workflow builds and uploads all release platforms', () => {
|
|||||||
...paths,
|
...paths,
|
||||||
'release/latest*.yml',
|
'release/latest*.yml',
|
||||||
'release/*.blockmap',
|
'release/*.blockmap',
|
||||||
'release/package-size-*.json',
|
|
||||||
]);
|
]);
|
||||||
const download = jobSteps(parsedPrereleaseWorkflow, 'release').find(
|
const download = jobSteps(parsedPrereleaseWorkflow, 'release').find(
|
||||||
(step) => step.uses === 'actions/download-artifact@v4' && step.with?.name === name,
|
(step) => step.uses === 'actions/download-artifact@v4' && step.with?.name === name,
|
||||||
);
|
);
|
||||||
assert.equal(download?.with?.path, 'release');
|
assert.equal(download?.with?.path, 'release');
|
||||||
}
|
}
|
||||||
const steps = jobSteps(parsedPrereleaseWorkflow, 'release');
|
|
||||||
const checksum = steps.find((step) => step.name === 'Generate checksums');
|
|
||||||
const publish = steps.find((step) => step.name === 'Publish Prerelease');
|
|
||||||
assert.ok(checksum);
|
|
||||||
assert.ok(publish);
|
|
||||||
assert.ok(executableRunLines(checksum).includes('files+=(release/package-size-*.json)'));
|
|
||||||
assert.ok(executableRunLines(publish).includes('release/package-size-*.json'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('release callers pass only the declared packaging secrets', () => {
|
test('release callers pass only the declared packaging secrets', () => {
|
||||||
|
|||||||
@@ -289,7 +289,6 @@ test('stable and prerelease builds use the same packaging gate', () => {
|
|||||||
for (const workflow of [releaseWorkflow, prerelease]) {
|
for (const workflow of [releaseWorkflow, prerelease]) {
|
||||||
assert.match(workflow, /uses: \.\/\.github\/workflows\/package-release\.yml/);
|
assert.match(workflow, /uses: \.\/\.github\/workflows\/package-release\.yml/);
|
||||||
assert.match(workflow, /needs: \[package\]/);
|
assert.match(workflow, /needs: \[package\]/);
|
||||||
assert.match(workflow, /release\/package-size-\*\.json/);
|
|
||||||
}
|
}
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
templateExpressionsInRunBodies(
|
templateExpressionsInRunBodies(
|
||||||
|
|||||||
Reference in New Issue
Block a user