Documentation
¶
Overview ¶
Package segment computes, writes and tags the versions of the segments in a Claude Code plugin marketplace monorepo.
The distinguishing choice is the baseline: a segment's previous version is found in the git history of its own plugin.json, not in a git tag. Tags are therefore decoration here rather than state, which is what lets the tool stay correct on a repository that has never been tagged — and stops a forgotten tag silently corrupting the next computation.
Index ¶
- Constants
- Variables
- func TagName(segment string, v Version) string
- type Author
- type Bump
- type Commit
- type Git
- func (g *Git) BaselineCommit(_ context.Context, segmentDir string) (string, error)
- func (g *Git) CommitsTouching(_ context.Context, dir, since string) ([]Commit, error)
- func (g *Git) CreateTag(_ context.Context, tag, message, at string) error
- func (g *Git) PushBranch(ctx context.Context, remoteURL, branch, username, token string) error
- func (g *Git) PushTags(ctx context.Context, remoteURL, username, token string, tags []string) error
- func (g *Git) Repo() Repository
- func (g *Git) StageAndCommit(ctx context.Context, message string, paths []string, author Author) (string, error)
- func (g *Git) TagExists(_ context.Context, tag string) bool
- func (g *Git) VersionAt(_ context.Context, sha, segmentDir string) (string, error)
- type Inconsistency
- type Marketplace
- type Plan
- type Planner
- type Repository
- type Segment
- type Version
- type WriteResult
Constants ¶
const ManifestPath = ".claude-plugin/plugin.json"
ManifestPath is where a segment's plugin.json lives, relative to the segment.
Variables ¶
var ( // ErrNoVersionKey reports a manifest with no version member. ErrNoVersionKey = errors.NewSentinel("skillup.no_version_key", `manifest has no "version" key`) // ErrAmbiguousVersion reports a manifest with more than one version member, // where guessing which one is meant would be worse than refusing. ErrAmbiguousVersion = errors.NewSentinel("skillup.ambiguous_version", `manifest has more than one "version" key`) )
Sentinels for the two ways a manifest can be unusable. Static, so a caller can match on them with errors.Is rather than on a formatted string.
var ErrNoBaseline = errors.NewSentinel("skillup.no_baseline", "no baseline commit for segment")
ErrNoBaseline reports a segment whose manifest has no history yet.
var ErrNotSemver = errors.NewSentinel("skillup.not_semver", "not a semver core")
ErrNotSemver reports a version string that is not a plain semver core.
Functions ¶
Types ¶
type Author ¶
Author identifies who made a commit.
Required explicitly rather than left to go-git: go-git reads no git config, so on a machine that happens to have user.name set a commit succeeds, and in a CI container it fails with "author field is required". Taking it as an argument makes the behaviour the same in both places.
type Bump ¶
type Bump int
Bump is the size of change a set of commits warrants.
func HighestBump ¶
HighestBump reduces a set of commits to the single bump they collectively warrant.
type Commit ¶
Commit is the part of a git commit this tool reasons about.
type Git ¶
type Git struct {
// contains filtered or unexported fields
}
Git answers the history questions skillup asks, and performs the writes it makes, over go-git via go/repo.
Nothing here shells out. Handing a path or a ref to /bin/git means quoting an external value into a command line every time, and a version tool is not important enough to be the place that rule gets bent.
func NewGitFrom ¶
func NewGitFrom(r Repository) *Git
NewGitFrom wraps an already-open repository, for tests and callers that hold one.
func (*Git) BaselineCommit ¶
BaselineCommit returns the commit at which a segment's version last changed.
This is the whole point of the tool. Deriving the baseline from the manifest's own history — rather than from a git tag, as cocogitto does — means a tag is never load-bearing, so a forgotten one costs visibility instead of silently corrupting the next version.
An empty string means the manifest has no history yet: treat every commit that touched the segment as in scope.
func (*Git) CommitsTouching ¶
CommitsTouching returns the commits after since (exclusive) that changed anything under dir, newest first. An empty since means all history.
func (*Git) PushBranch ¶
PushBranch pushes HEAD to a branch on a remote URL, authenticating with a token. The credential never reaches a command line, because there is no command line.
func (*Git) Repo ¶
func (g *Git) Repo() Repository
Repo exposes the underlying repository for the commands that write.
func (*Git) StageAndCommit ¶
func (g *Git) StageAndCommit(ctx context.Context, message string, paths []string, author Author) (string, error)
StageAndCommit stages the given paths and commits them, returning the new hash. It reports an empty hash and no error when nothing was staged.
func (*Git) VersionAt ¶
VersionAt reads a segment's version as it was at a commit.
Needed because the working tree and the baseline commit can disagree: after apply writes a bump but before it is committed, the manifest has advanced and history has not. Computing from the working tree in that window bumps twice.
type Inconsistency ¶
Inconsistency is a disagreement between the marketplace catalogue and what is on disk.
func CheckConsistency ¶
func CheckConsistency(root string) ([]Inconsistency, error)
CheckConsistency compares the marketplace catalogue against the segments on disk.
Three ways a marketplace drifts, all of which are invisible until a consumer hits them:
- a catalogue entry pointing at a directory that is not there
- a segment on disk that the catalogue never lists, so nobody can install it
- a version declared in both places, disagreeing
The last is the subtle one. A marketplace entry may carry its own version, and where it does it wins for resolution — so a stale entry pins consumers to an old version while plugin.json says something else entirely.
type Marketplace ¶
type Marketplace struct {
Plugins []struct {
Name string `json:"name"`
Source string `json:"source"`
Version string `json:"version,omitempty"`
} `json:"plugins"`
}
Marketplace is the subset of marketplace.json this tool reads.
type Plan ¶
type Plan struct {
Segment Segment `json:"segment"`
Current string `json:"current"`
Baseline string `json:"baseline_commit,omitempty"`
Commits int `json:"commits_considered"`
Bump string `json:"bump"`
Next string `json:"next"`
Action string `json:"action"` // "none", "raise", "manifest-ahead"
Reasons []string `json:"reasons,omitempty"`
}
Plan is what skillup would do to one segment.
type Planner ¶
Planner computes plans.
func NewPlanner ¶
NewPlanner opens the repository at root and returns a Planner for it.
type Repository ¶
type Repository interface {
WithRepo(func(*git.Repository) error) error
WithTree(func(*git.Worktree) error) error
Commit(context.Context, string, *git.CommitOptions) (plumbing.Hash, error)
Push(context.Context, *git.PushOptions) error
}
Repository is the slice of gitlab.com/phpboyscout/go/repo that skillup needs.
Declared here rather than taking repo.RepoLike wholesale: this is the estate's own "depend on the narrowest role" rule, and it means a fake in a test implements four methods instead of nine roles.
type Version ¶
type Version struct{ Major, Minor, Patch int }
Version is a semver core. Pre-release and build metadata are deliberately unmodelled: a marketplace segment ships from a branch, so there is nowhere for a pre-release to live.
func ParseVersion ¶
ParseVersion reads a plain semver core.
func ReadVersion ¶
ReadVersion returns the version declared in a segment's manifest.
type WriteResult ¶
WriteResult records what WriteVersion did, including declining to act.
func WriteVersion ¶
func WriteVersion(segmentDir string, want Version) (WriteResult, error)
WriteVersion sets the version in a segment's manifest, preserving every other byte. It refuses to lower a version: see WriteResult.