shipping-build-artifacts
Skill111 starsChanged 18 days agoMITView on GitHub
Make the build step a real gate on what you actually distribute — build scripts that warn and exit 0 on a missing input, size checks with only an upper bound, hand-maintained file lists that drift from the entrypoints they must cover, committed bundles that go stale when only the source changes, GNU-only shell in release scripts that aborts on the other OS, and verification that runs against the source tree instead of the artifact. Use when writing or reviewing a build/package script, a `dist/` copy step, a release workflow that uploads a zip or installer, or a committed compiled asset.
1 other project uses a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- kajisho5/ffmpeg-skilledited, first seen Oct 8, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 1
- kajisho5/ffmpeg-skill★ 4.8k89% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository41 other files it gives its agents.
- building-accessible-interfacesSkill
- wiring-application-configSkill
- permissioning-ci-workflowsSkill
- concurrent-branchesSkill
- shipping-across-surfacesSkill
- writing-defect-reportsSkill
- reporting-derived-metricsSkill
- guarding-destructive-operationsSkill
- running-github-actions-efficientlySkill
- keeping-git-repos-cleanSkill
- designing-interaction-statesSkill
- designing-marketing-pagesSkill
- rendering-untrusted-contentSkill
- reproducing-ci-locallySkill
- running-resumable-sync-jobsSkill
- normalizing-text-for-measurementSkill
- calibrating-thresholds-and-baselinesSkill
- designing-interfacesSkill
- verifying-external-behaviorSkill
- building-go-projectsSkill
- building-browser-extensionsSkill
- designing-python-apisSkill
- building-python-clisSkill
- improving-python-code-qualitySkill
- building-python-communitiesSkill
- documenting-python-librariesSkill
- deferring-heavy-dependenciesSkill
- reviewing-python-librariesSkill
- building-llm-backed-featuresSkill
- building-python-mcp-serversSkill
- packaging-python-librariesSkill
- optimizing-python-performanceSkill
- setting-up-python-librariesSkill
- managing-python-releasesSkill
- auditing-python-securitySkill
- testing-python-librariesSkill
- building-python-web-appsSkill
- building-rust-cratesSkill
- building-scala-projectsSkill
- building-swift-appsSkill
- shipping-swift-appsSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 82
When a compiled or minified bundle is committed and served directly, *the bundle is the program* and its source is a comment until someone …
- Distinctive: line 113
Read structured metadata with a parser instead of a regex, and prefer a runtime you already depend on:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Shipping Build Artifacts
Lint, type-check, and tests run against the source tree. What users install is a different set of bytes — assembled by a script that most gates never look at, then uploaded by a workflow that trusts whatever the script left behind. Every failure below ships a broken or stale artifact under fully green CI.
A script that warns and exits 0 is not a gate
The shape is universal: a declared list of inputs, a copy loop, a friendly warning when one is missing.
for (const f of DIST_FILES) {
if (!fs.existsSync(f)) {
console.warn(`Warning: ${f} not found, skipping`); // build "succeeds"
continue;
}
fs.copyFileSync(f, path.join("dist", f));
}Move one required file aside and the script prints a line nobody reads, exits 0,
and produces a dist/ without it. Nothing downstream notices: the test job ran
against the source tree, and the release job zips dist/ and attaches it to a
public release. The artifact is wholly non-functional — the entrypoint imports a
file that isn't there — and the failure is discovered by users.
const missing = DIST_FILES.filter((f) => !fs.existsSync(f));
if (missing.length) {
console.error(`Missing build inputs: ${missing.join(", ")}`);
process.exit(1);
}The rule: inside a build script, warn may only describe something the artifact
survives without. If you cannot say what still works when that file is absent, it
is an error and the process must exit non-zero.
Bound the artifact size on both sides
A packaging check with only a ceiling — "fail if the zip exceeds 500 KB" — is a cost guard, not a correctness one. A build that silently dropped half its files is smaller, so it passes the only check that exists.
assert(bytes < 500 * 1024, "package too large");
assert(bytes > 20 * 1024, "package suspiciously small — inputs likely missing");
assert(entries.length === DIST_FILES.length, "package entry count mismatch");Better still, assert on contents rather than a proxy: list the archive's entries and compare against the set the entrypoints require.
Derive the file list, or check it against the entrypoints
DIST_FILES — like a build backend's only-include, or a hand-written
package_data — is a second copy of "what this app is made of." The first copy
is the manifest, the entry HTML, and the import graph. They drift in one
direction: someone adds utils.js, references it from the popup, and forgets the
copy list. The build stays green and the feature is dead in the packaged app.
Either derive the list (bundle from the real entrypoints), or add a check that
every path referenced by the manifest and by <script src> / importScripts
exists in dist/ after the build. A hand-maintained allowlist with no such check
is a bug scheduled for a future commit.
The same rule covers any place dependency or asset metadata is restated by hand —
a standalone launcher script whose inline dependency header duplicates the
project manifest's dependencies, for instance. If duplication is unavoidable,
add a test that normalizes both lists and compares them, so drift fails in CI
instead of at a user's install.
Committed build outputs go stale silently
When a compiled or minified bundle is committed and served directly, the bundle is the program and its source is a comment until someone rebuilds. Editing only the source ships nothing; the page keeps serving the previous bundle, and no test or linter says a word.
- Rebuild in CI and
diffagainst the committed output; fail on drift. - Pin the builder to an exact version — the diff is only meaningful if the output is byte-deterministic.
- Confirm that determinism once across the environments people actually use (native toolchain vs. container image), so the check is runnable locally too.
npx -y esbuild@0.24.2 src/app.jsx --jsx=transform --minify --outfile=/tmp/app.js
diff /tmp/app.js web/app.jsRebuild and commit the output in the same commit as the source change. A "rebuild bundles" follow-up commit means every commit in between shipped code that does not match its source.
Release scripts run on an OS you didn't write them on
Build scripts are written on a developer machine and executed on the runner.
GNU-only tooling is the usual break, and set -e turns it into a total abort on
a line that merely reads a version number:
# Breaks under BSD grep (macOS): -P / lookbehind are GNU extensions.
VERSION=$(grep -Po '(?<=^version = ")[^"]+' pyproject.toml)Read structured metadata with a parser instead of a regex, and prefer a runtime you already depend on:
VERSION=$(python -c 'import tomllib;print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')
VERSION=$(jq -r .version package.json)Then pin it with a test: the version the build script extracts must equal the
version declared in the project manifest. Without that, the failure mode is a
release tagged v1.4.0 whose artifact reports 1.3.2, and nothing in the
pipeline disagrees.
Verify the artifact, then publish — in that order, in that job
Attaching a file to a public release, pushing a tag, or uploading to a registry are the least reversible steps in the project. The verification must sit between the build and the upload, in the same job. A separate green "test" job proves nothing about the artifact: it ran against the source tree.
Minimum ordering:
- Build.
- List every entry in the artifact and assert the entrypoints are present.
- Install or load it from a directory the source tree is not on the load path of, and exercise one real symbol or command — not merely that a top-level name resolves.
- Only then upload.
Step 3 is the one that gets skipped, and it is the only step that distinguishes "the archive has files in it" from "the thing runs." Run it somewhere else on disk, or it passes against the sources and proves nothing.
For language-specific artifact inspection (wheels, sdists, console scripts), see
packaging-python-libraries. For pinning the tools that produce and check these
artifacts, see the CI tooling section of setting-up-python-libraries.
Checklist
Build script:
- [ ] Missing declared input → non-zero exit, not a warning
- [ ] Size assertions have a floor as well as a ceiling
- [ ] File list is derived, or checked against manifest/entry-HTML references
- [ ] Duplicated dependency metadata has a drift test
- [ ] No GNU-only flags (grep -P, sed -i'' semantics) in scripts CI also runs
- [ ] Version extracted with a parser, and asserted equal to the declared version
Committed build outputs:
- [ ] CI rebuilds and diffs; drift fails
- [ ] Builder pinned to an exact version; output confirmed deterministic
- [ ] Output committed alongside the source change, not in a follow-up
Release:
- [ ] Artifact contents listed and asserted before upload
- [ ] Artifact installed/loaded from outside the repo and exercised
- [ ] Verification runs in the same job as the upload, before itDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

