make build reports success and produces no binary.
$ rm -f vaultik && make build; echo "EXIT=$?"
make: Nothing to be done for 'build'.
EXIT=0
$ ls vaultik
ls: cannot access 'vaultik': No such file or directory
Makefile:1 lists build in .PHONY, but no build: rule exists —
the real target is vaultik: at line 70. Declaring it phony is what
converts the error you would otherwise get ("No rule to make target") into
a silent success: make considers a phony target with no prerequisites and
no recipe already satisfied.
Found during the PR #109
review as an out-of-scope observation, then reproduced. Pre-existing — Makefile is byte-identical to main across that PR.
Why this matters more than a missing alias
make build is the documented convention for building in this org, so
this is the command a person or agent reaches for first. It exits 0. A
caller that checks the exit code — CI, a script, an agent verifying its
own work — concludes the build succeeded. Nothing is produced and nothing
says so.
That is the same failure shape this repo has now closed in five other
places: #78 (linter version skew), #85 (Docker layer cache), #93 (Go test
cache), #88/#99 (shared lint cache), each a gate reporting a green it did
not earn. This one is in the build target itself.
Definition of done
make build builds the binary. Simplest correct fix is build: vaultik, keeping vaultik: as the file rule.
rm -f vaultik && make build produces the binary and exits 0; the
negative control is that a deliberately broken build makes it exit
non-zero. Verify both — a target that cannot fail is no better than one
that cannot build.
Audit the rest of .PHONY for the same defect: any name listed there
without a corresponding rule silently succeeds. Fix or remove each.
script/cibuild exits 0.
Note
.PHONY on this line also lists all, local, install, docker, hooks, deps and others. I have not checked whether each has a rule —
item 3 covers it, and any that do not have the identical silent-success
behaviour.
`make build` reports success and produces no binary.
```
$ rm -f vaultik && make build; echo "EXIT=$?"
make: Nothing to be done for 'build'.
EXIT=0
$ ls vaultik
ls: cannot access 'vaultik': No such file or directory
```
`Makefile:1` lists `build` in `.PHONY`, but **no `build:` rule exists** —
the real target is `vaultik:` at line 70. Declaring it phony is what
converts the error you would otherwise get ("No rule to make target") into
a silent success: make considers a phony target with no prerequisites and
no recipe already satisfied.
Found during the [PR #109](https://git.eeqj.de/sneak/vaultik/pulls/109)
review as an out-of-scope observation, then reproduced. Pre-existing —
`Makefile` is byte-identical to `main` across that PR.
## Why this matters more than a missing alias
`make build` is the documented convention for building in this org, so
this is the command a person or agent reaches for first. It exits 0. A
caller that checks the exit code — CI, a script, an agent verifying its
own work — concludes the build succeeded. Nothing is produced and nothing
says so.
That is the same failure shape this repo has now closed in five other
places: #78 (linter version skew), #85 (Docker layer cache), #93 (Go test
cache), #88/#99 (shared lint cache), each a gate reporting a green it did
not earn. This one is in the build target itself.
## Definition of done
1. `make build` builds the binary. Simplest correct fix is
`build: vaultik`, keeping `vaultik:` as the file rule.
2. `rm -f vaultik && make build` produces the binary and exits 0; the
negative control is that a deliberately broken build makes it exit
non-zero. Verify both — a target that cannot fail is no better than one
that cannot build.
3. Audit the rest of `.PHONY` for the same defect: any name listed there
without a corresponding rule silently succeeds. Fix or remove each.
4. `script/cibuild` exits 0.
## Note
`.PHONY` on this line also lists `all`, `local`, `install`, `docker`,
`hooks`, `deps` and others. I have not checked whether each has a rule —
item 3 covers it, and any that do not have the identical silent-success
behaviour.
clawbot
added this to the 1.0.0 milestone 2026-08-09 19:19:10 +02:00
Implementing this together with issue #108 as one PR
against main.
Add build: vaultik as the description suggests, keeping vaultik:
as the file rule so incremental builds still work.
Verify both directions by hand and report both: rm -f vaultik && make build produces the binary and exits 0, and a
deliberately broken source tree makes the same command exit non-zero.
.PHONY audit, already done against the current Makefile: the 19
names listed are all, bootstrap, setup, check, test, lint, lint-fix, fmt, fmt-check, build, clean, deps, test-coverage, local, install, release, release-snapshot, docker, hooks. Every one of them has a rule except build, so build is the only instance of this defect. vaultik is correctly
absent from .PHONY, being a real file target.
A regression guard so the audit does not have to be redone by hand:
a test that parses the Makefile, extracts the .PHONY names and
the set of declared rules, and fails on any phony name with no rule.
It fails on the current tree (build) and passes once the rule is
added, which is also how the audit stays true for names added later.
The guard is a parse rather than an invocation of make on purpose: make test is what runs it, so shelling back into make build from
inside it would nest the build in the test and drop a binary into the
tree as a side effect of testing. The one thing a parse cannot prove —
that the recipe can still fail — is the manual negative control in
item 2.
## Plan
Implementing this together with
[issue #108](https://git.eeqj.de/sneak/vaultik/issues/108) as one PR
against `main`.
1. Add `build: vaultik` as the description suggests, keeping `vaultik:`
as the file rule so incremental builds still work.
2. Verify both directions by hand and report both:
`rm -f vaultik && make build` produces the binary and exits 0, and a
deliberately broken source tree makes the same command exit non-zero.
3. `.PHONY` audit, already done against the current `Makefile`: the 19
names listed are `all`, `bootstrap`, `setup`, `check`, `test`,
`lint`, `lint-fix`, `fmt`, `fmt-check`, `build`, `clean`, `deps`,
`test-coverage`, `local`, `install`, `release`, `release-snapshot`,
`docker`, `hooks`. Every one of them has a rule except `build`, so
`build` is the only instance of this defect. `vaultik` is correctly
absent from `.PHONY`, being a real file target.
4. A regression guard so the audit does not have to be redone by hand:
a test that parses the `Makefile`, extracts the `.PHONY` names and
the set of declared rules, and fails on any phony name with no rule.
It fails on the current tree (`build`) and passes once the rule is
added, which is also how the audit stays true for names added later.
The guard is a parse rather than an invocation of `make` on purpose:
`make test` is what runs it, so shelling back into `make build` from
inside it would nest the build in the test and drop a binary into the
tree as a side effect of testing. The one thing a parse cannot prove —
that the recipe can still fail — is the manual negative control in
item 2.
Implemented in PR #111, on branch fix-prune-json-and-build.
What was built
build: vaultik added, with vaultik: kept as the file rule.
.PHONY audit (item 3)
All 19 names on that line were checked against the rules in the file: all, bootstrap, setup, check, test, lint, lint-fix, fmt, fmt-check, build, clean, deps, test-coverage, local, install, release, release-snapshot, docker, hooks.
build was the only one without a rule. Every other name has one,
so nothing else needed fixing or removing. vaultik is correctly absent
from .PHONY, being a real file target — putting it there would have
made the binary rebuild unconditionally.
A regression guard keeps that true without a re-audit by hand: TestPhonyTargetsAllHaveRules parses the Makefile, extracts the .PHONY names and the set of declared rules, and fails on any phony
name with no rule. A companion test asserts build reaches the rule
that produces the binary, so giving build: an empty recipe of its own
would not satisfy it. Both fail on the tree before this change.
The guard parses rather than invoking make: make test is what runs
it, so shelling back into make build would nest a build inside the
test run and drop a binary into the tree as a side effect of testing.
Both directions (item 2)
$ rm -f vaultik && make build; echo "EXIT=$?"
go build -ldflags "..." -o vaultik ./cmd/vaultik
EXIT=0
$ ls -la vaultik
142829863 bytes
Negative control, with a deliberate syntax error added to a file in cmd/vaultik:
$ rm -f vaultik && make build; echo "EXIT=$?"
cmd/vaultik/zz_deliberate_break.go:4:34: syntax error: unexpected name is at end of statement
make: *** [Makefile:82: vaultik] Error 1
EXIT=2
$ ls vaultik
ls: cannot access 'vaultik': No such file or directory
So the target both builds and can fail. The broken file was removed
before committing and is not in the branch.
Item 4
script/cibuild exits 0. The three check RUN layers each echoed the
fresh CHECK_EPOCH, so make fmt-check, make lint and make test
executed rather than replaying from cache; the only CACHED layers are
below the ARG CHECK_EPOCH line.
One reporting note for whoever reads the gate next: make test now
prints 16ok lines rather than 15. cmd/vaultik had no test file
until this change and was reported as ? no test files; the Makefile
guard lives there, beside the Makefile it guards, so the package now
compiles and runs tests.
Implemented in
[PR #111](https://git.eeqj.de/sneak/vaultik/pulls/111), on branch
`fix-prune-json-and-build`.
## What was built
`build: vaultik` added, with `vaultik:` kept as the file rule.
## `.PHONY` audit (item 3)
All 19 names on that line were checked against the rules in the file:
`all`, `bootstrap`, `setup`, `check`, `test`, `lint`, `lint-fix`, `fmt`,
`fmt-check`, `build`, `clean`, `deps`, `test-coverage`, `local`,
`install`, `release`, `release-snapshot`, `docker`, `hooks`.
**`build` was the only one without a rule.** Every other name has one,
so nothing else needed fixing or removing. `vaultik` is correctly absent
from `.PHONY`, being a real file target — putting it there would have
made the binary rebuild unconditionally.
A regression guard keeps that true without a re-audit by hand:
`TestPhonyTargetsAllHaveRules` parses the `Makefile`, extracts the
`.PHONY` names and the set of declared rules, and fails on any phony
name with no rule. A companion test asserts `build` reaches the rule
that produces the binary, so giving `build:` an empty recipe of its own
would not satisfy it. Both fail on the tree before this change.
The guard parses rather than invoking `make`: `make test` is what runs
it, so shelling back into `make build` would nest a build inside the
test run and drop a binary into the tree as a side effect of testing.
## Both directions (item 2)
```
$ rm -f vaultik && make build; echo "EXIT=$?"
go build -ldflags "..." -o vaultik ./cmd/vaultik
EXIT=0
$ ls -la vaultik
142829863 bytes
```
Negative control, with a deliberate syntax error added to a file in
`cmd/vaultik`:
```
$ rm -f vaultik && make build; echo "EXIT=$?"
cmd/vaultik/zz_deliberate_break.go:4:34: syntax error: unexpected name is at end of statement
make: *** [Makefile:82: vaultik] Error 1
EXIT=2
$ ls vaultik
ls: cannot access 'vaultik': No such file or directory
```
So the target both builds and can fail. The broken file was removed
before committing and is not in the branch.
## Item 4
`script/cibuild` exits 0. The three check `RUN` layers each echoed the
fresh `CHECK_EPOCH`, so `make fmt-check`, `make lint` and `make test`
executed rather than replaying from cache; the only `CACHED` layers are
below the `ARG CHECK_EPOCH` line.
One reporting note for whoever reads the gate next: `make test` now
prints **16** `ok` lines rather than 15. `cmd/vaultik` had no test file
until this change and was reported as `? no test files`; the Makefile
guard lives there, beside the Makefile it guards, so the package now
compiles and runs tests.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
make buildreports success and produces no binary.Makefile:1listsbuildin.PHONY, but nobuild:rule exists —the real target is
vaultik:at line 70. Declaring it phony is whatconverts the error you would otherwise get ("No rule to make target") into
a silent success: make considers a phony target with no prerequisites and
no recipe already satisfied.
Found during the PR #109
review as an out-of-scope observation, then reproduced. Pre-existing —
Makefileis byte-identical tomainacross that PR.Why this matters more than a missing alias
make buildis the documented convention for building in this org, sothis is the command a person or agent reaches for first. It exits 0. A
caller that checks the exit code — CI, a script, an agent verifying its
own work — concludes the build succeeded. Nothing is produced and nothing
says so.
That is the same failure shape this repo has now closed in five other
places: #78 (linter version skew), #85 (Docker layer cache), #93 (Go test
cache), #88/#99 (shared lint cache), each a gate reporting a green it did
not earn. This one is in the build target itself.
Definition of done
make buildbuilds the binary. Simplest correct fix isbuild: vaultik, keepingvaultik:as the file rule.rm -f vaultik && make buildproduces the binary and exits 0; thenegative control is that a deliberately broken build makes it exit
non-zero. Verify both — a target that cannot fail is no better than one
that cannot build.
.PHONYfor the same defect: any name listed therewithout a corresponding rule silently succeeds. Fix or remove each.
script/cibuildexits 0.Note
.PHONYon this line also listsall,local,install,docker,hooks,depsand others. I have not checked whether each has a rule —item 3 covers it, and any that do not have the identical silent-success
behaviour.
Plan
Implementing this together with
issue #108 as one PR
against
main.build: vaultikas the description suggests, keepingvaultik:as the file rule so incremental builds still work.
rm -f vaultik && make buildproduces the binary and exits 0, and adeliberately broken source tree makes the same command exit non-zero.
.PHONYaudit, already done against the currentMakefile: the 19names listed are
all,bootstrap,setup,check,test,lint,lint-fix,fmt,fmt-check,build,clean,deps,test-coverage,local,install,release,release-snapshot,docker,hooks. Every one of them has a rule exceptbuild, sobuildis the only instance of this defect.vaultikis correctlyabsent from
.PHONY, being a real file target.a test that parses the
Makefile, extracts the.PHONYnames andthe set of declared rules, and fails on any phony name with no rule.
It fails on the current tree (
build) and passes once the rule isadded, which is also how the audit stays true for names added later.
The guard is a parse rather than an invocation of
makeon purpose:make testis what runs it, so shelling back intomake buildfrominside it would nest the build in the test and drop a binary into the
tree as a side effect of testing. The one thing a parse cannot prove —
that the recipe can still fail — is the manual negative control in
item 2.
Implemented in
PR #111, on branch
fix-prune-json-and-build.What was built
build: vaultikadded, withvaultik:kept as the file rule..PHONYaudit (item 3)All 19 names on that line were checked against the rules in the file:
all,bootstrap,setup,check,test,lint,lint-fix,fmt,fmt-check,build,clean,deps,test-coverage,local,install,release,release-snapshot,docker,hooks.buildwas the only one without a rule. Every other name has one,so nothing else needed fixing or removing.
vaultikis correctly absentfrom
.PHONY, being a real file target — putting it there would havemade the binary rebuild unconditionally.
A regression guard keeps that true without a re-audit by hand:
TestPhonyTargetsAllHaveRulesparses theMakefile, extracts the.PHONYnames and the set of declared rules, and fails on any phonyname with no rule. A companion test asserts
buildreaches the rulethat produces the binary, so giving
build:an empty recipe of its ownwould not satisfy it. Both fail on the tree before this change.
The guard parses rather than invoking
make:make testis what runsit, so shelling back into
make buildwould nest a build inside thetest run and drop a binary into the tree as a side effect of testing.
Both directions (item 2)
Negative control, with a deliberate syntax error added to a file in
cmd/vaultik:So the target both builds and can fail. The broken file was removed
before committing and is not in the branch.
Item 4
script/cibuildexits 0. The three checkRUNlayers each echoed thefresh
CHECK_EPOCH, somake fmt-check,make lintandmake testexecuted rather than replaying from cache; the only
CACHEDlayers arebelow the
ARG CHECK_EPOCHline.One reporting note for whoever reads the gate next:
make testnowprints 16
oklines rather than 15.cmd/vaultikhad no test fileuntil this change and was reported as
? no test files; the Makefileguard lives there, beside the Makefile it guards, so the package now
compiles and runs tests.