Documentation
¶
Overview ¶
Package release: secret-rotations subcommands.
This file ports the two bun-executed TypeScript scripts that formerly lived under .github/scripts/:
- check-secret-rotations.ts: walks the per-secret docs, computes (today - lastRotated) for each, and opens a labelled GitHub issue when a secret is within the reminder window (or already overdue).
- record-rotation.ts: rewrites the `lastRotated:` line in one per-secret file in place.
Both are invoked by .github/workflows/{secret-rotation- reminder.yml, record-secret-rotation.yml}. Bringing them into the existing mdsmith-release Go CLI removes the bun runtime and per-script package manifest from the repo, and lets the pure logic ride the same `go test` suite the rest of the release tooling does.
Package release provides the helper logic the release workflow invokes via cmd/mdsmith-release/. The package was originally a set of bash scripts under scripts/, but JSON/TOML editing in perl regex and indirect testing through `bash <script>` made them brittle. The Go port runs under one toolchain, has direct tests, and reports actionable errors without shelling out.
Production code constructs a Toolkit via New(); tests can substitute a fake FS via NewWithFS to exercise IO error branches the real OS cannot reliably trigger.
Index ¶
- Constants
- Variables
- func BuildNpmPlatforms(rootDir, artifactsDir, outDir string) error
- func BuildWheels(rootDir, artifactsDir, outDir string) error
- func Check(root string) error
- func DaysBetween(later, earlier time.Time) int
- func ExecGH(args []string) ([]byte, error)
- func IsISODate(s string) bool
- func IssueBody(entry RotationEntry, fileBasename string, due DueResult, env Environ) string
- func ParseFrontMatter(text, path string) (map[string]any, error)
- func RecordRotation(repoRoot, entryTitle, date string) (changed bool, err error)
- func Stamp(root, version string) error
- func UTCToday(now time.Time) time.Time
- func UpdateLastRotated(yamlBlock, date, path string) (string, error)
- func ValidateSemver(v string) error
- type CheckRotationsOptions
- type CheckSecretRotationsResult
- type DueResult
- type DueState
- type Environ
- type FS
- type FindEntryResult
- type FrontMatterError
- type GHRunner
- type LoadedRotation
- type Manifest
- type ManifestKind
- type MapEnviron
- type RotationEntry
- type Runner
- type Toolkit
- func (t *Toolkit) BuildNpmPlatforms(rootDir, artifactsDir, outDir string) error
- func (t *Toolkit) BuildWheels(rootDir, artifactsDir, outDir string) error
- func (t *Toolkit) Check(root string) error
- func (t *Toolkit) Stamp(root, version string) error
- func (t *Toolkit) TrackedManifests(root string) []Manifest
Constants ¶
const DevSentinel = "0.0.0-dev"
DevSentinel is the version every tracked manifest carries between releases. Stamp rewrites it to the cleaned tag during release; Check fails the version-guard CI job when a hand-edit pushes anything else to main.
const IssueLabel = "secret-rotation"
IssueLabel is the GitHub label every reminder issue carries so the `record-rotation` workflow can find and close it.
const ReminderWindowDays = 30
ReminderWindowDays is the lead time before `lastRotated + periodDays` at which the reminder workflow opens a labelled issue. Matches the value the previous TS scripts used.
const RotationsDirName = "docs/development/secret-rotations"
RotationsDirName is the path (relative to the repo root) that holds one markdown file per tracked secret. The per-secret files carry the canonical front matter consumed by both CheckSecretRotations and RecordRotation.
Variables ¶
var PlatformPackages = []string{
"@mdsmith/linux-x64",
"@mdsmith/linux-arm64",
"@mdsmith/darwin-x64",
"@mdsmith/darwin-arm64",
"@mdsmith/win32-x64",
}
PlatformPackages enumerates the @mdsmith/<platform> optional- dependency keys the npm root must list. Stays in lock-step with the build matrix in .github/workflows/release.yml.
Functions ¶
func BuildNpmPlatforms ¶
BuildNpmPlatforms delegates to a default-OS Toolkit (see Stamp).
func BuildWheels ¶
BuildWheels delegates to a default-OS Toolkit (see Stamp).
func DaysBetween ¶ added in v0.14.0
DaysBetween returns the integer day count between two UTC midnights. Positive when later > earlier.
func ExecGH ¶ added in v0.14.0
ExecGH is the default GHRunner: shells out to `gh` on PATH. On a non-zero exit, the returned error embeds the gh stderr text so the workflow log shows what gh actually said rather than the bare "exit status N" message.
func IsISODate ¶ added in v0.14.0
IsISODate reports whether s is a real calendar date in YYYY-MM-DD form. Round-trips the parsed components so normalized invalid dates are rejected.
func IssueBody ¶ added in v0.14.0
func IssueBody(entry RotationEntry, fileBasename string, due DueResult, env Environ) string
IssueBody renders the GitHub issue body for one due/overdue rotation. The format mirrors the previous TS implementation byte-for-byte so a switch-over does not flip existing labelled issues' bodies on the next reminder run.
func ParseFrontMatter ¶ added in v0.14.0
ParseFrontMatter extracts and YAML-parses the front matter block of a markdown file. The block must be fenced by `---\n` on both sides and the root must be a YAML mapping.
func RecordRotation ¶ added in v0.14.0
RecordRotation updates the `lastRotated` field of the per-secret file whose `title` matches entryTitle. Returns (true, nil) if the file was rewritten, (false, nil) if the date was already at the requested value (no-op), or (false, err) on any validation failure.
func Stamp ¶
Stamp delegates to a default-OS Toolkit so callers without an explicit Toolkit (for example, the cmd binary) can stay terse. Tests that need fault injection construct a Toolkit via NewWithFS instead.
func UTCToday ¶ added in v0.14.0
UTCToday returns today's date as UTC midnight. Cron schedules run in UTC, so the due-state computation must match.
func UpdateLastRotated ¶ added in v0.14.0
UpdateLastRotated rewrites the `lastRotated:` line in a YAML front-matter block to the new date. Quoting is normalized to double quotes regardless of source quoting. Returns the rewritten block, or an error if no `lastRotated:` line was found in the input.
func ValidateSemver ¶
ValidateSemver rejects empty strings, leading-v tags ("v1.2.3"), and any version that isn't conforming SemVer.
Types ¶
type CheckRotationsOptions ¶ added in v0.14.0
CheckRotationsOptions configures CheckSecretRotations. Now is the wall-clock time to compute due-state from; GH is the gh runner (overridable for tests). Env is the environment reader used to render issue bodies (GITHUB_SERVER_URL etc).
type CheckSecretRotationsResult ¶ added in v0.14.0
CheckSecretRotationsResult records the per-run outcome the workflow log surfaces.
func CheckSecretRotations ¶ added in v0.14.0
func CheckSecretRotations(repoRoot string, opts CheckRotationsOptions) (CheckSecretRotationsResult, error)
CheckSecretRotations is the body of the scheduled reminder workflow. Walks the per-secret docs, classifies each, and opens or skips a labelled issue per entry. The `gh` CLI is shelled out via opts.GH for the existing-issue lookup and the create/label calls — same as the prior TS implementation, no new go-github dependency. Tests inject a fake GHRunner so they never touch the real `gh` binary.
type DueResult ¶ added in v0.14.0
DueResult pairs the classification with a signed day count. DaysUntilDue is positive while the rotation is still in the future, zero on the due date, negative once past due. Callers format the value differently per status: "due in N days" / "due today" / "OVERDUE by N days" (negate).
type DueState ¶ added in v0.14.0
type DueState string
DueState classifies a rotation: not yet due, within the reminder window, or overdue.
type Environ ¶ added in v0.14.0
Environ abstracts os.Getenv for testability. The default (osEnviron) reads the process environment.
type FS ¶
type FS interface {
// Stat returns the FileInfo for the named file, mirroring
// os.Stat.
Stat(name string) (os.FileInfo, error)
// ReadFile reads the named file, mirroring os.ReadFile.
ReadFile(name string) ([]byte, error)
// WriteFile writes data to the named file, mirroring
// os.WriteFile.
WriteFile(name string, data []byte, perm fs.FileMode) error
// ReadDir reads the named directory, mirroring os.ReadDir.
ReadDir(name string) ([]os.DirEntry, error)
// MkdirAll creates name and any parents, mirroring
// os.MkdirAll.
MkdirAll(path string, perm fs.FileMode) error
// MkdirTemp creates a new temporary directory, mirroring
// os.MkdirTemp.
MkdirTemp(dir, pattern string) (string, error)
// Rename renames (moves) oldpath to newpath, mirroring
// os.Rename.
Rename(oldpath, newpath string) error
// RemoveAll removes path and any children, mirroring
// os.RemoveAll.
RemoveAll(path string) error
}
FS is the small filesystem surface the release toolkit uses. Production paths use osFS (delegating to the real syscalls); tests inject a fault-injecting fake to exercise IO error branches that real filesystems don't reliably trigger (mid-pipeline mkdir failure, rename target on a non-directory, disk-full WriteFile, etc.).
We intentionally keep this independent of stdlib's read-only `io/fs.FS`. The release toolkit needs Mkdir/Write/Rename alongside Read/Stat, and stdlib's interface only covers reads.
type FindEntryResult ¶ added in v0.14.0
FindEntryResult is what FindEntry returns: the matched path and the sorted set of titles seen during the scan.
func FindEntry ¶ added in v0.14.0
func FindEntry(rotationsDir, entryTitle string) (FindEntryResult, error)
FindEntry locates the per-secret file whose front matter `title` matches entryTitle. Fails loudly on any malformed per-secret file and on duplicate titles across files, so a rewrite of `lastRotated` cannot mutate the wrong file or silently shadow an unrelated entry.
type FrontMatterError ¶ added in v0.14.0
FrontMatterError describes a malformed per-secret file. Tests match on the wrapped error's message; the path prefix gives the maintainer a clear pointer to the bad file.
func (*FrontMatterError) Error ¶ added in v0.14.0
func (e *FrontMatterError) Error() string
type GHRunner ¶ added in v0.14.0
GHRunner is the injection point for the `gh` CLI. Production uses ExecGH (shells out to the `gh` binary on PATH); tests pass a fake function that records invocations and returns canned output. Returns the stdout bytes; on a non-zero exit the error wraps the gh stderr so workflow logs can diagnose the failure.
type LoadedRotation ¶ added in v0.14.0
type LoadedRotation struct {
Entry RotationEntry
FileBasename string
}
LoadedRotation pairs a parsed entry with the base filename it came from. The filename feeds the issue body's "rotation procedure" link.
func LoadRotations ¶ added in v0.14.0
func LoadRotations(rotationsDir string) ([]LoadedRotation, error)
LoadRotations walks rotationsDir, parses every *.md file, and returns them sorted by title for deterministic iteration order.
type Manifest ¶
type Manifest struct {
Path string
Kind ManifestKind
OptionalDeps bool
}
Manifest is one tracked file. OptionalDeps marks the npm root — only that file carries @mdsmith/* pins.
func TrackedManifests ¶
TrackedManifests is a thin wrapper over the default Toolkit so callers without an explicit Toolkit can still introspect the tracked set.
type ManifestKind ¶
type ManifestKind int
ManifestKind tells the rewrite/check helpers which syntax to expect: JSON for npm package manifests, TOML for pyproject.
const ( // ManifestJSON marks JSON manifests (npm package.json files). ManifestJSON ManifestKind = iota // ManifestTOML marks TOML manifests (python pyproject.toml). ManifestTOML )
type MapEnviron ¶ added in v0.14.0
MapEnviron is an in-memory Environ for tests.
func (MapEnviron) Getenv ¶ added in v0.14.0
func (m MapEnviron) Getenv(key string) string
Getenv returns the value of key in the map, or empty string.
type RotationEntry ¶ added in v0.14.0
type RotationEntry struct {
Title string
LastRotated string
PeriodDays int
Provider string
IssuerURL string
UsedBy string
Scope string
}
RotationEntry is the parsed per-secret front matter. Field names match the YAML keys exactly (camelCase per the existing docs).
func ValidateRotationEntry ¶ added in v0.14.0
func ValidateRotationEntry(fm map[string]any, path string) (RotationEntry, error)
ValidateRotationEntry projects a front-matter map into a RotationEntry, failing loudly on missing keys, malformed lastRotated, non-integer periodDays, and non-positive periodDays. Separated from LoadRotations so the validation is reachable from unit tests without filesystem fixtures.
type Runner ¶
type Runner interface {
// RunCommand executes name+args in dir, with stdout/stderr
// inherited from the calling process. Mirrors exec.Cmd.Run
// semantics: a non-zero exit returns *exec.ExitError; other
// failures (binary not found, IO) return their underlying
// error.
RunCommand(dir, name string, args ...string) error
}
Runner runs an external command. The release toolkit shells out to `python -m build` and `python -m wheel tags` for the PyPI publish path; tests inject a fake Runner to cover those branches without putting python on PATH.
type Toolkit ¶
type Toolkit struct {
// contains filtered or unexported fields
}
Toolkit owns a configured FS and Runner and exposes the release helpers (Stamp, Check, BuildNpmPlatforms, BuildWheels) as methods. `New()` returns a Toolkit backed by the real OS for both; tests can use `NewWithFS(fakeFS)` or `NewWithDeps` to drive error paths that the OS does not expose.
func New ¶
func New() *Toolkit
New returns a Toolkit backed by the real OS filesystem and command runner.
func NewWithDeps ¶
NewWithDeps returns a Toolkit with custom FS and Runner. Used by tests that exercise both IO and command-execution faults.
func NewWithFS ¶
NewWithFS returns a Toolkit with a custom FS and the OS-backed Runner. Convenience helper for tests that only need IO faults.
func (*Toolkit) BuildNpmPlatforms ¶
BuildNpmPlatforms emits one ready-to-publish npm sub-package directory per supported platform under outDir, copying the matching release artifact from artifactsDir. Stamp must run first because the version is taken from rootDir/npm/mdsmith/package.json.
func (*Toolkit) BuildWheels ¶
BuildWheels builds one platform-tagged wheel per supported host from prebuilt binaries in artifactsDir, writing the wheels to outDir. The python source tree at rootDir/python is staged per build with the matching binary embedded under mdsmith/_bin/, then `python -m build` produces a py3-none-any wheel which `python -m wheel tags` retags to the correct platform tag (in both the filename and the dist-info/WHEEL metadata).
Requires `python -m build`, `python -m wheel`, and the hatchling build backend on PATH. Stamp must run first so pyproject.toml carries the published version.
func (*Toolkit) Check ¶
Check accumulates every problem with the tracked manifests (missing file, missing field, drifted version, missing or drifted @mdsmith/* pin) into one multi-line error. The version-guard CI job uses Check; reporting all problems at once is more useful than failing on the first.
func (*Toolkit) Stamp ¶
Stamp rewrites every tracked manifest under root from the dev sentinel to version. Idempotent: running with the same version twice produces no further change. A required manifest that's missing a version field — or, for the npm root, missing the @mdsmith/* optionalDependencies block — is a hard error.
func (*Toolkit) TrackedManifests ¶
TrackedManifests returns the set of manifests Stamp rewrites and Check verifies via the Toolkit's FS. The npm/platforms/ directory is not in source control — BuildNpmPlatforms emits the platform sub-packages under its own outDir argument (npm/dist in the release workflow). Anything that has already materialised npm/platforms/<plat>/package.json (a previous local Stamp+BuildNpmPlatforms invocation, a custom staging step) is stamped here too; if the directory is absent the helper just returns the four checked-in manifests.