Three tries in one day to decide what not to deploy
Oct 25, 2026 Β· Tools Β· νκ΅μ΄
This site has no build step. The source files are the site, and the whole repository root goes to Firebase Hosting. That means any file in the repository can end up on the site. Tools, documents, art sources and private settings must not. I changed how I decide what to leave out three times in a single day.
Firebase Hosting is Google's static site hosting. Files listed in the ignore list of the firebase.json config file are not uploaded. A deploy is done by GitHub Actions, which checks the repository out fresh, so files that git ignores don't exist in the deploy environment at all. The question was how to pick out the files that are in git but must not go up.
At first I filtered by extensions such as *.md and by auth file names (gha-creds-*). I kept the game status in a list.json inside the games folder, which is a folder published on the site. I excluded just that file by name, but there was no way to check whether anything had slipped through. If a file isn't caught by a rule you can't know, and one mismatch quietly puts a file online. A file I meant to keep private did get served from the site once.
I moved the status file to a config/ folder and excluded config/** as a whole. A comment inside a deployed file still mentioned the file's path, so I removed that too. I added two checks to the deploy step. It fails if config/** is missing from the ignore list. It also fails if a deployed file mentions the status file's name. Moving to folders made sure this one file was blocked. But the other files I had been filtering with name rules like *.md were still the same.
The same day I unified the rule. What stays off the site is decided only by the names of folders directly under the root and names that start with a dot. Tools go in tools/, documents in docs/, operational settings in config/, art sources in art-src/ and logs in logs/. I removed every extension and filename rule.
"ignore": ["config/**", "tools/**", "docs/**", "logs/**", "art-src/**",
"**/.*", "**/.*/**", "firebase.json", "CLAUDE.md", "games/CLAUDE.md"]
For that I had to move every file that depended on a name rule into an excluded folder. The documents (CREDITS, NOTES, TODO) went to docs/, the security rules file to config/, the new game template to tools/game-template/, and each game's art source to art-src/. The auth file is created during the deploy, so instead of excluding it by name I made the workflow move it out of the working folder.
Stop the deploy on anything unknown
The check matters more. In the step that builds the deploy copy, every entry in the root is examined. Each one must be an excluded folder, a name starting with a dot, a file listed in the ignore list, or in the list of site files. If an entry is in none of those, the deploy fails.
const unknown = fs.readdirSync(ROOT).filter((f) => !f.startsWith('.')
&& !outDirs.includes(f) && !ignore.includes(f) && !SITE_ROOT.includes(f));
if (unknown.length) throw new Error(`unknown root entries: ${unknown.join(', ')}`);
This is a simplified example. The point is that it is an allow list. When you create a new file in the root, a person has to decide whether it goes on the site before the deploy will pass. The devlog/ folder I created for this dev log also had to be added to the list of site files by hand.
References
- Firebase, Hosting configuration (ignore): https://firebase.google.com/docs/hosting/full-config
More posts
- I deployed, and the old page kept showing for an hour Oct 25, 2026
- Why there is always a cloud within reach in Cloud Hop Oct 25, 2026
- Why the crumbling clouds never sit on the path Oct 25, 2026
- I removed the background and a pink edge was left Oct 25, 2026
- A ten-line FPS counter for browser games Oct 25, 2026
