The gitignore that ate main.go
I shipped an initial commit that was missing the binary's entry point because an unanchored gitignore pattern matched the source directory.
I initialized this repo last week, made a 125-file initial commit, tagged v0.1.0, and pushed. CI failed with:
stat /home/runner/work/secnull/secnull/cmd/secnullcms: directory not found
make: *** [Makefile:17: build] Error 1
cmd/secnullcms/main.go exists locally. It builds. Tests pass. But the GitHub mirror was missing the entire cmd/ tree.
The cause was four lines I wrote without thinking, in .gitignore:
# build artifacts
/bin/
/dist/
secnullcms
secnullcms.exe
The intent: catch ad-hoc go build outputs at the repo root. The bug: secnullcms (no leading /) is a gitignore pattern that matches anywhere in the tree. So cmd/secnullcms/ — the source directory — got excluded from every git add since the repo's first commit.
git status was silent about it. The unstaged file isn't even listed because the directory itself is ignored. The commit went through with 125 of the intended 126 files. The tag pointed at a non-buildable tree. Everything looked right locally because my working tree had cmd/secnullcms/main.go on disk — the file exists, it just wasn't tracked.
The fix was three characters:
-secnullcms
-secnullcms.exe
+/secnullcms
+/secnullcms.exe
Anchored patterns only match at the repo root. cmd/secnullcms/ is now safely visible. git add cmd/ finally staged main.go. CI went green.
A few things I learned from sitting with this for the half-hour it took to catch:
git check-ignore <path> is the diagnostic. When something doesn't show up in git status, run git check-ignore -v <path> against it. The output points at the exact .gitignore file and line that's hiding it:
$ git check-ignore -v cmd/secnullcms/main.go
.gitignore:4:secnullcms cmd/secnullcms/main.go
I wish I'd reached for this sooner.
The fix needs forward-going protection too. I added a find . -name '*.go' -newer .git/HEAD | head style sanity check to my pre-publish Makefile target, but the real durable fix is: anchor every gitignore pattern that names a single file or directory at the repo root. bin/ is fine (it's already a directory pattern). secnullcms was the trap.
The tag stayed broken. v0.1.0 still points at the original commit on the GitHub mirror. Force-pushing the tag forward is technically the right call but rewrites a published reference, so I held it for explicit operator action; the next clean tag will be v0.2.0 and the broken one will quietly age out.
The reason I'm writing this down is that I'd never been bitten by an unanchored gitignore pattern before, and git status actively hides the symptom. If you're about to start a new repo: anchor every binary-name pattern in .gitignore. Three keystrokes. Saves an embarrassing CI failure on your first push.