release

package
v0.23.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 21, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package release: the cross-tool benchmark harness.

This file ports docs/research/benchmarks/run.sh into the mdsmith-release Go CLI so the local hand-refresh and the post-merge `benchmark.yml` workflow run byte-identical logic (see docs/development/release-tooling.md: workflow runtime logic goes through this binary, not inline shell).

What Go owns now that the shell script delegated or skipped:

  • Integrity. Every comparison binary is fetched at a pinned version and verified by SHA-256 before it runs. run.sh pinned hyperfine/mado/panache by release tag but never checksummed them, and pulled rumdl (uv) and markdownlint-cli2 (npm) unpinned. A tampered or silently-rebuilt tarball now fails the run loudly.
  • markdownlint-cli2 installs from a committed lockfile via `npm ci` (docs/research/benchmarks/npm/), not an unpinned `npm i`.

What is unchanged on purpose: the corpora construction, the exact hyperfine flags/commands, the JSON promoted into docs/research/benchmarks/data/, and the fragment regeneration — the last still shells out to the existing gen_fragments.py so the `bench-fragments` drift gate keeps validating the committed snapshot against the committed JSON with one generator.

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: build-time fetch of the published assets.

The demo GIF and the cross-tool benchmark numbers are not committed to the repo — they are regenerated post-merge and pushed to the orphan `assets` branch (the demo.gif pattern; see .github/workflows/{demo,benchmark}.yml). This file pulls them into the working tree at site-build time so the deployed site serves the current artifacts:

  • the demo GIF lands in website/static/ and is served as a first-party asset (was a runtime raw.githubusercontent <img> hotlink);
  • the benchmark fragments overwrite the committed in-repo snapshot so the next `mdsmith fix` bakes the live numbers into the published page.

The committed fragment snapshot stays the floor: a miss (assets branch not yet carrying benchmarks/, or a transient fetch failure) keeps it rather than failing the deploy. The `bench-fragments` CI gate is a separate workflow that never calls this, so it keeps validating the committed snapshot against the committed JSON.

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

View Source
const (
	// DefaultPublishReleaseRetryAttempts is how many times
	// PublishRelease retries the by-tag lookup before concluding the
	// draft release the `release` job just created is not visible yet.
	DefaultPublishReleaseRetryAttempts = 3
	// DefaultPublishReleaseRetryDelay is the pause between by-tag
	// lookup retries while GitHub finishes materializing the release.
	DefaultPublishReleaseRetryDelay = 2 * time.Second
)
View Source
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.

View Source
const IssueLabel = "secret-rotation"

IssueLabel is the GitHub label every reminder issue carries so the `record-rotation` workflow can find and close it.

View Source
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.

View Source
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

View Source
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 Bench added in v0.21.0

func Bench(root, workdir string) error

Bench delegates to a default-OS Toolkit (see Stamp).

func BuildNpmPlatforms

func BuildNpmPlatforms(rootDir, artifactsDir, outDir string) error

BuildNpmPlatforms delegates to a default-OS Toolkit (see Stamp).

func BuildWebsite added in v0.16.0

func BuildWebsite(srcDir, dstDir string, runFix bool) error

BuildWebsite delegates to a default-OS Toolkit (see Stamp).

func BuildWheels

func BuildWheels(rootDir, artifactsDir, outDir string) error

BuildWheels delegates to a default-OS Toolkit (see Stamp).

func Check

func Check(root string) error

Check delegates to a default-OS Toolkit (see Stamp).

func DaysBetween added in v0.14.0

func DaysBetween(later, earlier time.Time) int

DaysBetween returns the integer day count between two UTC midnights. Positive when later > earlier.

func ExecGH added in v0.14.0

func ExecGH(args []string) ([]byte, error)

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 GenerateSBOM added in v0.23.0

func GenerateSBOM(root, outPath string) error

GenerateSBOM delegates to a default-OS Toolkit (see Stamp).

func IsISODate added in v0.14.0

func IsISODate(s string) bool

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 MergeCoverage added in v0.18.0

func MergeCoverage(inputs []string, output string) error

MergeCoverage merges Go coverage profiles by summing per-block hit counts and writes the result to output.

The naive `cat a.cov b.cov` the CI used before left duplicate records for cmd/mdsmith files (one from the per-package unit run, one from the e2e subprocess binary). Codecov's Go parser is last-write-wins on duplicate blocks, so the e2e profile's zero counts for lines exercised only by in-process tests clobbered real coverage and made the patch percentage swing with e2e flush timing. Summing is the correct merge: a block is covered if any profile observed a hit.

All inputs must share one mode line. `set` mode counts are boolean, so they are OR-ed (max); `count` / `atomic` hits are summed.

func ParseFrontMatter added in v0.14.0

func ParseFrontMatter(text, path string) (map[string]any, error)

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 PublishRelease added in v0.16.1

func PublishRelease(opts PublishReleaseOptions) error

PublishRelease flips the draft GitHub release for opts.Tag to a published release. The release is created by the `release` job's softprops/action-gh-release step as a draft so that every asset uploads while the release is still mutable; this call publishes it as the final, atomic step so the result is an immutable release.

Idempotent: a release that is already published is left untouched.

func PullSiteAssets added in v0.21.0

func PullSiteAssets(root string) error

PullSiteAssets delegates to a default-OS Toolkit (see Stamp).

func RecordRotation added in v0.14.0

func RecordRotation(repoRoot, entryTitle, date string) (changed bool, err error)

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

func Stamp(root, version string) error

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 SyncDocs added in v0.16.0

func SyncDocs(srcDir, dstDir string) error

SyncDocs delegates to a default-OS Toolkit (see Stamp).

func UTCToday added in v0.14.0

func UTCToday(now time.Time) time.Time

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

func UpdateLastRotated(yamlBlock, date, path string) (string, error)

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

func ValidateSemver(v string) error

ValidateSemver rejects empty strings, leading-v tags ("v1.2.3"), and any version that isn't conforming SemVer.

func VerifyWebsiteLinks(htmlDir, baseURL string) error

VerifyWebsiteLinks runs a fixed set of probes against the rendered HTML produced by `hugo --minify ...`. Each probe targets a behavior the render-link hook is responsible for: `.md` → permalink resolution, `index.md` → section URL, no `README.md` hrefs in rule pages, javascript:/data: hrefs neutralized by html/template, and the baseURL prefix appearing on site-absolute hrefs when one was supplied at build time. None of these are visible to the synced-tree mdsmith check (it walks the markdown filesystem pre-render), so without these probes a regression in the render-link hook ships silently.

htmlDir is the Hugo output root (`public/` under website/). baseURL is the URL Hugo was built with; the path portion (e.g. `/mdsmith` for project-pages, empty for root deploys) is treated as the expected path prefix on every site-absolute href. Each probe's regex accepts both `href="value"` (Hugo's default) and `href=value` (--minify), so the function is robust whether or not the caller passed `--minify`.

Probes fail closed with a single returned error naming the probe and the file that failed; subsequent probes are not run.

Types

type CheckRotationsOptions added in v0.14.0

type CheckRotationsOptions struct {
	Now time.Time
	GH  GHRunner
	Env Environ
}

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

type CheckSecretRotationsResult struct {
	Opened  []string
	Skipped []string
}

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

type DueResult struct {
	Status       DueState
	DaysUntilDue int
}

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).

func ComputeDueState added in v0.14.0

func ComputeDueState(today, lastRotated time.Time, periodDays int) DueResult

ComputeDueState returns the due-state of a rotation given the current UTC date, the parsed lastRotated date, and the period.

type DueState added in v0.14.0

type DueState string

DueState classifies a rotation: not yet due, within the reminder window, or overdue.

const (
	DueOK      DueState = "ok"
	DueDue     DueState = "due"
	DueOverdue DueState = "overdue"
)

Possible DueState values returned by ComputeDueState.

type Environ added in v0.14.0

type Environ interface {
	Getenv(key string) string
}

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

type FindEntryResult struct {
	Path   string
	Titles []string
}

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

type FrontMatterError struct {
	Path string
	Msg  string
}

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

type GHRunner func(args []string) ([]byte, error)

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 HTTPGetter added in v0.21.0

type HTTPGetter interface {
	// Get fetches url and returns the HTTP status code and the
	// fully-read body. A transport-level failure (DNS, refused,
	// timeout) returns a non-nil error with status 0; an HTTP
	// error response (404, 500, …) returns the status and the
	// body with a nil error, so callers decide per-asset whether
	// a miss is fatal or a keep-the-committed-copy fallback.
	Get(url string) (status int, body []byte, err error)
}

HTTPGetter is the one-call HTTP surface the release toolkit uses to fetch pinned benchmark tool tarballs and the published benchmark numbers / demo GIF from the orphan `assets` branch. Production uses osHTTPGetter (net/http); tests inject a fake so the assets-pull and download-integrity branches run without a network.

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
	RequiredMdsmithPins []string
}

Manifest is one tracked file. RequiredMdsmithPins lists the @mdsmith/* dependency keys this manifest must declare at the dev sentinel. When non-empty, Stamp rewrites every @mdsmith/* pin found in the file (so a future extra entry stays in sync) and Check verifies each listed key is present at DevSentinel.

func TrackedManifests

func TrackedManifests(root string) []Manifest

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

type MapEnviron map[string]string

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 PublishReleaseOptions added in v0.16.1

type PublishReleaseOptions struct {
	Repository string
	Tag        string
	Token      string

	APIBaseURL    string
	RetryAttempts int
	RetryDelay    time.Duration
	Client        *http.Client
	Sleep         func(time.Duration)
}

PublishReleaseOptions describes the GitHub release to flip from draft to published and the HTTP settings to reach the API.

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, Runner, and HTTPGetter and exposes the release helpers (Stamp, Check, BuildNpmPlatforms, BuildWheels, Bench, PullSiteAssets) as methods. `New()` returns a Toolkit backed by the real OS for all three; tests can use `NewWithFS(fakeFS)`, `NewWithDeps`, or `NewWithHTTP` 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, command runner, and HTTP client.

func NewWithDeps

func NewWithDeps(fsys FS, runner Runner) *Toolkit

NewWithDeps returns a Toolkit with custom FS and Runner and the OS-backed HTTPGetter. Used by tests that exercise both IO and command-execution faults.

func NewWithFS

func NewWithFS(fsys FS) *Toolkit

NewWithFS returns a Toolkit with a custom FS and the OS-backed Runner/HTTPGetter. Convenience helper for tests that only need IO faults.

func NewWithHTTP added in v0.21.0

func NewWithHTTP(fsys FS, getter HTTPGetter) *Toolkit

NewWithHTTP returns a Toolkit with a custom FS and HTTPGetter and the OS-backed Runner. Used by the PullSiteAssets tests to drive the build-time assets fetch without a network.

func (*Toolkit) Bench added in v0.21.0

func (t *Toolkit) Bench(root, workdir string) error

Bench runs the full cross-tool benchmark from the repo root: build mdsmith, fetch + integrity-verify the pinned comparison binaries, build the two corpora, drive hyperfine, promote the JSON into the committed data dir, and regenerate the doc fragments via gen_fragments.py. workdir caches every heavy artifact (built binaries, corpora) so a local rerun is cheap; CI passes a fresh /tmp path so every run is cold.

It deliberately does NOT run `mdsmith fix` to refresh the <?include?> bodies — run.sh never did either, and the benchmark.yml workflow owns that normalization before publishing to the assets branch.

func (*Toolkit) BuildNpmPlatforms

func (t *Toolkit) BuildNpmPlatforms(rootDir, artifactsDir, outDir string) error

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) BuildWebsite added in v0.16.0

func (t *Toolkit) BuildWebsite(srcDir, dstDir string, runFix bool) error

BuildWebsite prepares the Hugo content tree: optionally runs `mdsmith fix` against srcDir so every <?catalog?>/<?include?> body is current, snapshots srcDir into dstDir via SyncDocs, then publishes the rule directory (internal/rules/index.md) as the browsable Rules section under dstDir and copies each per-rule README as its own website page. It is the canonical implementation behind the website sync — both local dev and the pages-deploy workflow call this rather than carrying the fix+sync sequence as inline shell (see docs/development/release-tooling.md: every workflow that needs runtime logic goes through this binary).

The fix step shells out to `go run ./cmd/mdsmith` through the Runner seam, mirroring how BuildWheels invokes python; it expects the caller's working directory to be the repo root (true in CI and for the documented local invocation).

func (*Toolkit) BuildWheels

func (t *Toolkit) BuildWheels(rootDir, artifactsDir, outDir string) error

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

func (t *Toolkit) Check(root string) error

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) GenerateSBOM added in v0.23.0

func (t *Toolkit) GenerateSBOM(root, outPath string) error

GenerateSBOM writes a CycloneDX SBOM for the Go module at root to outPath. Implemented as a single `go run github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@<pinned> mod -licenses -json -output <outPath>` invocation. `go run` fetches the pinned tool through the Go module cache and executes it — no `go install` and no PATH-dependent second hop, so the command works on any runner with a Go toolchain on PATH. The Runner is injectable so tests can drive the success and failure branches without invoking go.

func (*Toolkit) PullSiteAssets added in v0.21.0

func (t *Toolkit) PullSiteAssets(root string) error

PullSiteAssets fetches every published artifact into the working tree before the Hugo build. Per-asset policy: a 200 overwrites the destination; a transport error or non-200 on a required asset fails the deploy loudly; the same miss on a non-required asset logs and keeps the committed copy.

func (*Toolkit) Stamp

func (t *Toolkit) Stamp(root, version string) error

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 missing the @mdsmith/* optionalDependencies block on a manifest that declares any RequiredMdsmithPins — is a hard error.

func (*Toolkit) SyncDocs added in v0.16.0

func (t *Toolkit) SyncDocs(srcDir, dstDir string) error

SyncDocs snapshots srcDir into dstDir for a Hugo build. The transforms reconcile two mismatches between mdsmith's docs/ tree and Hugo's expectations:

  • proto.md files are schema templates — their front matter holds CUE constraint strings, not real values — and would blow up Hugo's metadata parser. Dropped.
  • index.md is the docs/-tree convention for a directory overview; Hugo's matching convention is _index.md (the former turns the directory into a leaf bundle whose siblings become resources rather than pages). Renamed.
  • Non-markdown, non-image files (Go embeds, scripts) ride along in docs/ for repo reasons but never render. Skipped.
  • Literal {{< ... >}} and {{% ... %}} patterns inside documentation about Hugo would otherwise resolve as shortcodes during the build. Escaped to {{</* ... */>}} and {{%/* ... */%}}.
  • mdsmith docs carry the page title as the first body H1 (Hugo themes expect front-matter title:, no body H1). The first H1 is promoted to front-matter title: and removed from the body (see reconcileDocForHugo).
  • mdsmith <?name … ?> / <?/name?> directive markers are source syntax with no meaning to Hugo. Removed, while the same syntax inside code fences/spans (directive documentation) is preserved (see reconcileDocForHugo).

dstDir is removed before the copy, so SyncDocs is idempotent.

Returns an error without touching the filesystem if srcDir and dstDir overlap (equal, dstDir under srcDir, or srcDir under dstDir). The destination is wiped before the copy, so an overlap would irrevocably delete the source tree before reading it. The check compares absolute, cleaned paths so a caller passing relative paths or trailing slashes still gets the guard.

func (*Toolkit) TrackedManifests

func (t *Toolkit) TrackedManifests(root string) []Manifest

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.

The VS Code extension manifest pins @mdsmith/cli as an optionalDependency so the release-time `bun install` pulls the just-published binary into node_modules and build.ts bundles it into the .vsix. Stamp keeps that pin in lockstep with the release version; without the rewrite, bun would still chase the 0.0.0-dev sentinel and silently skip it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL