This report statically analyzes Alertmanager's Make targets and their dependency tree at upstream commit 7935b44682464fa7ba3e8a1f15a6f39eff1b3369.
The repository contains six Makefiles and one included common fragment:
- Root
Makefile Makefile.common- Legacy Elm UI
- Mantine UI
- Email template generator
- Alertmanager mixin
- TLS HA example
No other .mk fragments were found. The root Makefile's only include is Makefile.common.
This is a static verification of declared prerequisites, recursive Make calls, recipes, documentation, and CI use. It does not include clean-tree executions, timestamp experiments, or parallel-build stress tests. Findings about stale outputs and races follow directly from the declared graph and should be confirmed dynamically before changes are merged.
The normal serial CI path is mostly coherent, but the Make graph has several correctness gaps:
- Root
make testdoes not run the legacy Elm tests. - The documented
BINARIES=amtooloption does not match the variable used by the build recipe. make clean && make buildcannot restore all mandatory generated inputs.- Legacy UI and email outputs can remain stale because their proxy targets omit real inputs.
- Elm test failures can be masked when the JUnit reporter is enabled.
- Several aggregates and multi-output generators are unsafe under
make -j. - The Mantine artifact is built but is not embedded in the Go binary or included in release artifacts.
- The mixin and TLS example subgraphs have missing dependencies and multi-output modeling problems.
The root Makefile includes Makefile.common before defining repository-specific targets. The common fragment supplies a generic %: common-% forwarding rule, while the root overrides build, test, and lint with explicit targets.
The first eligible non-pattern target is common-all, so bare root make uses this graph:
make / make all
└── common-all
├── precheck
├── style → common-style
├── check_license → common-check_license
├── lint
│ ├── ui-elm → ui/app:build
│ ├── ui-mantine-lint → ui/mantine-ui:lint
│ └── common-lint
├── yamllint → common-yamllint
├── unused → common-unused
├── build
│ ├── ui-elm → ui/app:build
│ ├── ui-mantine → ui/mantine-ui:build
│ └── common-build → promu → $(PROMU)
└── test
├── ui-elm → ui/app:build
├── ui-mantine-test → ui/mantine-ui:test
└── common-test
Sources: common-all, root build, test, and lint, and frontend wrappers.
GNU Make updates a shared target only once per invocation, so the three paths to ui-elm do not normally cause three builds in one root make run.
build-all
├── assets
│ ├── ui/app/src/Data
│ │ └── api/v2/openapi.yaml
│ ├── ui-elm
│ ├── ui-mantine
│ └── template/email.tmpl
├── apiv2
│ ├── api/v2/models
│ ├── api/v2/restapi
│ └── api/v2/client
└── build
├── ui-elm
├── ui-mantine
└── common-build → promu → $(PROMU)
Source: root aggregate targets.
assets-tarball
└── ui-elm
└── archive ui/app/dist under .tarballs/
fuzz-config
└── go test -fuzz=^Fuzz -fuzztime=5s ./config
clean
├── remove template/email.tmpl
├── remove api/v2/{models,restapi,client}
├── ui/app:clean
└── ui/mantine-ui:clean
Source: assets-tarball, fuzz-config, and clean.
The forwarding rule exposes these principal aliases:
all → common-all
style → common-style
check_license → common-check_license
deps → common-deps
test-short → common-test-short
format → common-format
vet → common-vet
lint-fix → common-lint-fix
yamllint → common-yamllint
staticcheck → common-staticcheck → root lint
unused → common-unused
tarball → common-tarball
docker* → common-docker*
proto → common-proto
The Docker aggregates fan out across amd64, armv7, arm64, ppc64le, and s390x, as selected before the common Makefile is included. See the root architecture override, generated architecture target names, and Docker target definitions.
The default target in ui/app is all:
all
├── src/Data
│ └── ../../api/v2/openapi.yaml
│ ├── generate src/Data/*.elm
│ ├── generate src/DateTime.elm
│ └── make format
├── build
│ └── dist/.build_stamp
│ ├── currently discovered src/**/*.elm
│ ├── index.html
│ ├── vite.config.mjs
│ └── package-lock.json
└── test
└── node_modules
└── package-lock.json
Sources: aggregate and dependency installation, tests, build stamp, and OpenAPI generation.
Other public targets are format, review, test, dev-server, build, and clean.
The first target is node_modules, so bare make inside ui/mantine-ui only installs dependencies:
node_modules
├── package.json
└── package-lock.json
build
└── node_modules
└── npm run build
└── tsc && vite build
test
└── node_modules
└── npm run vitest
lint
└── node_modules
└── npm run check
Sources: Mantine Makefile and npm scripts.
The Make test target deliberately runs only Vitest; it does not invoke the package's composite npm test, which also checks and builds.
Bare make inside template only updates node_modules. Generation is explicit:
email.tmpl
├── email.html
├── inline-css.js
└── node_modules
└── package-lock.json
Sources: template Makefile and generator inputs and output.
default
├── vendor
│ └── jb install
├── build
│ └── vendor
│ └── generate alertmanager_alerts.yaml
└── dashboards_out
├── mixin.libsonnet
├── config.libsonnet
└── dashboards/*
Other targets are all, fmt, lint, and clean. None is declared phony. Source: mixin Makefile.
gen-certs
├── certs/ca.pem
│ └── certs/ca-csr.json
├── certs/node1.pem
│ ├── certs/ca-config.json
│ ├── certs/ca.pem
│ ├── certs/ca-key.pem
│ └── certs/node1-csr.json
├── certs/node1-key.pem
│ └── same prerequisites as node1.pem
├── certs/node2.pem
│ └── corresponding CA and node2 inputs
└── certs/node2-key.pem
└── same prerequisites as node2.pem
Source: TLS example Makefile.
Root test depends on ui-elm, which recursively runs make build, not make test. It runs Mantine Vitest and Go tests, but not the Elm test suite. See root test and ui-elm.
The omitted Elm target performs formatting validation, elm-review, and elm-test: Elm test.
CI compensates by running make all separately inside ui/app, but local root make test does not provide the behavior described in AGENTS.md. See the frontend CI sequence.
The README recommends:
make build BINARIES=amtoolSee README lines 43–46.
However, the build recipe passes $(PROMU_BINARIES) as Promu's positional binary list, and no local mapping from BINARIES to PROMU_BINARIES exists: common-build.
The effective invocation is:
make build PROMU_BINARIES=amtoolThe same incorrect command appears in AGENTS.md.
Root clean deletes the generated email template, generated API packages, generated Elm sources, and UI build outputs: root clean and Elm clean.
Root build only builds the two UIs and invokes Promu; it does not regenerate the email template or API packages: root build.
The email template is a mandatory Go embed input: template/template.go.
Only serial make build-all restores all required generated inputs after clean. Either build should encode the necessary generation dependencies, or the restricted clean/build sequence should be documented explicitly.
The Elm build stamp tracks only existing *.elm files, index.html, vite.config.mjs, and package-lock.json: build stamp rule.
It does not track:
src/main.jssrc/assets/elm-datepicker.csspublic/favicon.icoelm.jsonpackage.json- source-file deletions
src/main.js is the Vite entry and imports the CSS file, so either can change without invalidating dist/.build_stamp: src/main.js.
The Elm node_modules target also omits package.json: dependency rule.
Several relationships rely on serial prerequisite visitation rather than actual dependency edges:
- Root
build,test, andlintplaceui-elmbeside Go compilation or package loading: root targets. - Go embeds
ui/app/dist, so Go processing can begin before that directory exists or while it is being rebuilt:ui/web.go. - Root
assetsplacessrc/Datageneration besideui-elm:assets. - Elm
allmakes generation, build, and tests siblings: Elmall. build-allplaces assets, API generation, and binary building at the same level:build-all.common-allpermitsunused, which mutatesgo.modandgo.sum, to overlap with lint, build, and tests under parallel execution:common-allandcommon-unused.
These aggregates should not be considered make -j safe.
The API rule declares three normal targets that share one recipe:
api/v2/models api/v2/restapi api/v2/client: api/v2/openapi.yaml
scripts/swagger.shSee the root API rule.
The script removes and recreates all three directories, so parallel Make can launch multiple destructive generators against the same paths: scripts/swagger.sh.
The directory proxies also fail to detect an individual missing generated file while its directory remains. Inputs such as scripts/swagger.sh, COPYRIGHT.txt, and internal/tools/go.mod are not prerequisites.
A grouped target (&:) or one generation stamp would model the output set correctly.
When JUNIT_DIR is set, Elm tests run through:
elm-test --report=junit | tee ...No pipefail behavior is configured, so the pipeline normally returns tee's status. A failing elm-test may therefore appear successful.
The standalone template rule correctly depends on email.html, inline-css.js, and node_modules: template rule.
The root proxy depends only on email.html: root email rule.
If inline-css.js or package-lock.json changes while email.tmpl remains newer than email.html, root Make does not enter the sub-Make and the generated template remains stale.
The src/Data recipe also writes src/DateTime.elm, but only the directory is declared as a target: Elm generation rule.
If DateTime.elm or one generated Data file is removed while src/Data remains current, regeneration is not triggered. Generator image or Makefile changes also do not invalidate the output.
Root build and assets both build the Mantine UI: root build and assets.
However:
- The Go binary embeds only
ui/app/dist:ui/web.go. - Frontend CI uploads only
ui/app/dist: CI artifact. - Release builds download only
ui/app/dist: release workflow. - Mantine Vite has no alternate output or staging directory: Mantine Vite config.
This may be intentional during migration, but the target currently performs validation work rather than contributing to the production artifact.
The mixin graph has several missing or misleading declarations:
vendordoes not depend onjsonnetfile.jsonorjsonnetfile.lock.json.dashboards_outconsumesvendoranddashboards.jsonnetwithout depending on them.- Imported
alerts.libsonnetanddashboards.libsonnetare omitted. defaultplacesvendoranddashboards_outat the same level, creating a parallel race.buildwritesalertmanager_alerts.yaml, but that output is not the target.- No command-like target is declared
.PHONY. cleanleavesvendoranddashboards_out.
See the mixin Makefile and Jsonnet import graph.
The README also omits the required jb tool even though every build invokes jb install: mixin setup documentation.
Each certificate recipe writes both a certificate and a key, but the files are declared as independent targets: TLS certificate rules.
make -j gen-certs can therefore run duplicate commands against the same output paths. These should use grouped targets or stamps.
The README installs cfssl but not the separately invoked cfssljson, and it does not document gen-certs: TLS setup documentation.
Promu and golangci-lint use unversioned binary paths. If a binary already exists at the path, Make does not verify that it matches the configured version. See tool version variables, Promu installation, and golangci-lint installation.
The active golangci-lint version override is in the root Makefile, despite the agent guide saying it is pinned in Makefile.common: root override.
The Docker build targets expect prebuilt binaries under .build/${OS}-${ARCH} but do not depend on a cross-build target: Dockerfile inputs and Docker Make targets.
Similarly, publish, tag, and manifest targets do not depend on the preceding release stages. This may be intentional pipeline decomposition, but these targets are not self-contained.
common-all depends on precheck, but the target has no prerequisites. PRECHECK_COMMAND_template is defined but not instantiated in this repository: precheck infrastructure.
| Documented target | Assessment |
|---|---|
make build |
Builds both UIs and both Go binaries; the guide mentions only Elm assets. |
make build-all |
Accurate for serial execution, but not parallel-safe. |
make build BINARIES=amtool |
Incorrect variable; the recipe uses PROMU_BINARIES. |
make assets |
Incomplete description: it also builds both UIs and only incrementally regenerates file targets. |
make apiv2 |
Correct intent, but output tracking and parallel behavior are fragile. |
make test |
Incorrect: it skips the Elm tests. |
make test-short |
Accurate: runs short Go tests only. |
make lint |
Runs Go and Mantine checks, but only builds the Elm UI. |
make common-format |
Accurate: runs Go formatting plus configured gofumpt/goimports formatters. |
make fuzz-config |
Accurate. |
make clean |
Accurate about removals, but make build cannot restore everything it removes. |
The formatter configuration enables gofumpt and goimports: .golangci.yml.
- Add a root
ui-elm-testtarget and invoke it from roottest. - Correct the documented binary-selection variable or add a compatibility mapping from
BINARIEStoPROMU_BINARIES. - Decide whether
clean → buildshould be supported; encode generation dependencies or explicitly requireclean → build-all. - Complete the Elm build-stamp inputs and preserve Elm failures through the JUnit pipeline.
- Replace generated-directory proxies and independent multi-output rules with grouped targets or explicit stamp files.
- Add dependency edges that make root and UI aggregates safe under
make -j. - Expose all email generator inputs at the root level or make the root forwarding target phony.
- Decide whether Mantine is validation-only or part of the shipped artifact, then encode that decision in targets and documentation.
- Repair the mixin graph and document its actual tool requirements.
- Use versioned tool paths or stamps if configured tool versions must be enforced.
Before applying fixes, confirm the static findings in a disposable worktree or clean checkout:
# Inspect expanded targets and recipes.
make -pn
make -n build-all
# Verify clean-build behavior.
make clean
make build
# Verify complete serial regeneration.
make clean
make build-all
git diff --exit-code
# Exercise parallel-sensitive paths.
make clean
make -j8 build-all
make -j8 apiv2
make -C ui/app clean
make -C ui/app -j8 all
make -C examples/ha/tls -j8 gen-certs
# Check direct target semantics.
make test
make lint
make build PROMU_BINARIES=amtoolDestructive clean commands should be run only in a disposable checkout or after preserving local work.