Skip to content

Commit f19a537

Browse files
magifd2claude
andcommitted
feat(build): Developer ID codesigning, Apple notarization, package target
Adopts the org-wide convention defined in nlink-jp/.github CONVENTIONS.md §Code Signing and Notarization. Adds: - scripts/codesign-darwin.sh + scripts/notarize-darwin.sh (verbatim from nlink-jp/.github/templates/) - codesign call in `build` and after the darwin lines in `build-all` (incl. the darwin-universal binary produced by lipo) - new `package` target that zips all 5 platforms + darwin-universal with versioned naming (splunk-cli-vX.Y.Z-<os>-<arch>.zip), includes README.md alongside each binary, and notarizes the 3 darwin zips (amd64, arm64, universal) Defaults (CODESIGN_IDENTITY="Developer ID Application", NOTARY_PROFILE=nlink-jp-notary) match generic keychain entries — no personal identifiers, certificates, or credentials land in the repo. Builds on machines without a matching cert/profile fall back gracefully to ad-hoc + un-notarized with a warning. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d6092d9 commit f19a537

3 files changed

Lines changed: 156 additions & 1 deletion

File tree

Makefile

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,20 @@ LDFLAGS := -ldflags "-X main.version=$(VERSION)"
33
BINARY := splunk-cli
44
CMD := .
55

6-
.PHONY: build test vet lint check build-all clean \
6+
# macOS Developer ID signing / notarization (see nlink-jp/.github
7+
# CONVENTIONS.md §Code Signing). Defaults match any Developer ID
8+
# Application cert in the keychain and the org-standard notary
9+
# profile. Builds without these fall back to ad-hoc / un-notarized
10+
# with a one-line warning — see scripts/codesign-darwin.sh.
11+
CODESIGN_IDENTITY ?= Developer ID Application
12+
NOTARY_PROFILE ?= nlink-jp-notary
13+
14+
.PHONY: build test vet lint check build-all package clean \
715
splunk-up splunk-down integration-test
816

917
build: _dist
1018
go build $(LDFLAGS) -o dist/$(BINARY) $(CMD)
19+
@scripts/codesign-darwin.sh dist/$(BINARY) "$(CODESIGN_IDENTITY)"
1120

1221
test:
1322
go test ./...
@@ -31,6 +40,27 @@ build-all: _dist
3140
dist/$(BINARY)-darwin-amd64 dist/$(BINARY)-darwin-arm64; \
3241
echo "Universal macOS binary: dist/$(BINARY)-darwin-universal"; \
3342
fi
43+
@scripts/codesign-darwin.sh dist/$(BINARY)-darwin-amd64 "$(CODESIGN_IDENTITY)"
44+
@scripts/codesign-darwin.sh dist/$(BINARY)-darwin-arm64 "$(CODESIGN_IDENTITY)"
45+
@if [ -f dist/$(BINARY)-darwin-universal ]; then \
46+
scripts/codesign-darwin.sh dist/$(BINARY)-darwin-universal "$(CODESIGN_IDENTITY)"; \
47+
fi
48+
49+
## package: Build all platforms, zip with version suffix + README, notarize darwin → dist/
50+
package: build-all
51+
@cd dist && for f in $(BINARY)-linux-amd64 $(BINARY)-linux-arm64 $(BINARY)-darwin-amd64 $(BINARY)-darwin-arm64 $(BINARY)-darwin-universal $(BINARY)-windows-amd64.exe; do \
52+
[ -f "$$f" ] || continue; \
53+
suffix=$${f#$(BINARY)-}; \
54+
suffix=$${suffix%%.exe}; \
55+
cp ../README.md .; \
56+
zip -j "$(BINARY)-$(VERSION)-$${suffix}.zip" "$$f" README.md; \
57+
rm -f README.md; \
58+
done
59+
@scripts/notarize-darwin.sh dist/$(BINARY)-$(VERSION)-darwin-amd64.zip "$(NOTARY_PROFILE)"
60+
@scripts/notarize-darwin.sh dist/$(BINARY)-$(VERSION)-darwin-arm64.zip "$(NOTARY_PROFILE)"
61+
@if [ -f dist/$(BINARY)-$(VERSION)-darwin-universal.zip ]; then \
62+
scripts/notarize-darwin.sh dist/$(BINARY)-$(VERSION)-darwin-universal.zip "$(NOTARY_PROFILE)"; \
63+
fi
3464

3565
_dist:
3666
mkdir -p dist

scripts/codesign-darwin.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/bin/sh
2+
# codesign-darwin.sh — sign a darwin Mach-O binary with a Developer ID
3+
# Application identity (Hardened Runtime + Apple timestamp), or skip
4+
# gracefully if codesigning is not possible.
5+
#
6+
# Usage:
7+
# codesign-darwin.sh <binary> [identity]
8+
#
9+
# Identity defaults to "Developer ID Application" — matches any Developer
10+
# ID Application certificate in the user's keychain. Override via env or
11+
# 2nd argument when more than one Developer ID identity is present.
12+
#
13+
# Behaviour:
14+
# - Skips silently on non-macOS hosts (cross-compile from Linux/etc.)
15+
# - Skips with a one-line warning if no matching identity exists
16+
# (contributors without an Apple Developer Program account can still
17+
# build; the binary keeps its Go-linker ad-hoc signature)
18+
# - Skips silently if the target file is not Mach-O (Linux/Windows
19+
# binaries fall through untouched)
20+
#
21+
# Why --options runtime + --timestamp:
22+
# Apple's notary service rejects binaries that lack Hardened Runtime
23+
# or an Apple-issued secure timestamp. Setting both at sign time keeps
24+
# `make package` notarize-ready without a second resign pass.
25+
26+
set -e
27+
28+
BINARY="${1:?Usage: $0 <binary> [identity]}"
29+
IDENTITY="${2:-${CODESIGN_IDENTITY:-Developer ID Application}}"
30+
31+
# Cross-compile from non-Darwin: nothing to do
32+
if [ "$(uname)" != "Darwin" ]; then
33+
exit 0
34+
fi
35+
36+
if [ ! -f "$BINARY" ]; then
37+
echo "[codesign] $BINARY not found, skipping" >&2
38+
exit 0
39+
fi
40+
41+
# Non-Mach-O target (e.g. Linux/Windows binary produced by cross-compile)
42+
if ! file "$BINARY" | grep -q 'Mach-O'; then
43+
exit 0
44+
fi
45+
46+
# No Developer ID identity in keychain — keep the linker ad-hoc signature
47+
if ! security find-identity -v -p codesigning 2>/dev/null | grep -q "$IDENTITY"; then
48+
echo "[codesign] No '$IDENTITY' identity in keychain; $BINARY keeps the Go-linker ad-hoc signature" >&2
49+
exit 0
50+
fi
51+
52+
codesign --force --options runtime --timestamp --sign "$IDENTITY" "$BINARY"
53+
echo "[codesign] Signed $BINARY with '$IDENTITY'"

scripts/notarize-darwin.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/bin/sh
2+
# notarize-darwin.sh — submit a zip-packaged darwin binary to Apple's
3+
# notary service and wait for the verdict.
4+
#
5+
# Usage:
6+
# notarize-darwin.sh <zip-path> [profile-name]
7+
#
8+
# Profile name defaults to the value of NOTARY_PROFILE env, then to
9+
# `nlink-jp-notary`. Credentials are stored once per machine via:
10+
#
11+
# xcrun notarytool store-credentials nlink-jp-notary \
12+
# --key <p8> --key-id <id> --issuer <uuid>
13+
#
14+
# Behaviour:
15+
# - Skips on non-Darwin hosts (cross-compile from Linux/etc.)
16+
# - Skips when the keychain profile isn't present (other
17+
# contributors / CI without credentials still produce the zip,
18+
# just un-notarised). A one-line warning is printed.
19+
# - On failure, prints the Apple-returned log and exits non-zero
20+
# so a release pipeline can stop.
21+
#
22+
# Why we don't staple: notarisation of bare CLI binaries inside a
23+
# zip cannot be stapled — `stapler staple` only works on app
24+
# bundles, dmgs, and pkgs. The notarisation ticket lives on Apple's
25+
# servers and macOS checks it online the first time the binary is
26+
# launched on a given machine. This is the standard pattern for
27+
# non-bundle distributables (cf. the official notarytool docs).
28+
29+
set -e
30+
31+
ZIP="${1:?Usage: $0 <zip-path> [profile]}"
32+
PROFILE="${2:-${NOTARY_PROFILE:-nlink-jp-notary}}"
33+
34+
if [ "$(uname)" != "Darwin" ]; then
35+
exit 0
36+
fi
37+
38+
if [ ! -f "$ZIP" ]; then
39+
echo "[notarize] $ZIP not found, skipping" >&2
40+
exit 0
41+
fi
42+
43+
# Probe the keychain profile cheaply (notarytool has no dedicated
44+
# "is profile present" command). `history` returns quickly without
45+
# uploading anything, so we use it as a liveness check.
46+
if ! xcrun notarytool history --keychain-profile "$PROFILE" >/dev/null 2>&1; then
47+
echo "[notarize] Keychain profile '$PROFILE' not found; $ZIP will ship un-notarised" >&2
48+
echo "[notarize] To enable, run once per machine:" >&2
49+
echo "[notarize] xcrun notarytool store-credentials $PROFILE --key <p8> --key-id <id> --issuer <uuid>" >&2
50+
exit 0
51+
fi
52+
53+
echo "[notarize] Submitting $ZIP to Apple notary service (this typically takes 30s-2m)..."
54+
SUBMISSION_OUT=$(xcrun notarytool submit "$ZIP" --keychain-profile "$PROFILE" --wait 2>&1) || {
55+
echo "[notarize] $ZIP: submission failed" >&2
56+
echo "$SUBMISSION_OUT" >&2
57+
exit 1
58+
}
59+
60+
echo "$SUBMISSION_OUT"
61+
62+
# notarytool exits 0 on Accepted, non-zero otherwise. As an extra
63+
# guard, parse the status line in the output and fail explicitly
64+
# on anything other than "Accepted" so a release pipeline halts
65+
# even if Apple shifts exit-code semantics in a future release.
66+
if printf '%s\n' "$SUBMISSION_OUT" | grep -q 'status: Accepted'; then
67+
echo "[notarize] $ZIP: Accepted"
68+
exit 0
69+
fi
70+
71+
echo "[notarize] $ZIP: notarisation did not succeed (see status above)" >&2
72+
exit 1

0 commit comments

Comments
 (0)