Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .agents/SPECS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
specs v1
https://github.com/optum-eeps/specs
13 changes: 13 additions & 0 deletions .agents/requirements/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# semver-cli requirements

Testable requirements derived from the chapters in
[`../specifications/`](../specifications/README.md), one per file under a topic
folder. Each cites the section it refines in `spec_ref`, and the tests that
verify it cite it with `req:{id}`.

## Topics

- [Version sources](version-sources/001-file-authority.requirement.md) covers
source selection, file initialization, tag lookup, increments, and setting a
version. The requirements are verified by `src/util/config.test.ts` and
`src/util/source.test.ts`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: version-sources-001
title: File source uses VERSION as its authority
spec_ref: ".agents/specifications/version-sources.spec.md#source-selection; .agents/specifications/version-sources.spec.md#file-source"
---

# File source uses VERSION as its authority

## Requirement

Preconditions:

- The configuration omits `source` or sets it to `file`.
- `VERSION` contains a valid version, which may differ from the latest tag.

Expected behavior:

- `get` MUST report the version in `VERSION`.
- `get` MUST NOT change the existing file.

Postconditions:

- `VERSION` retains its original content.
- Tests: `src/util/config.test.ts` and `src/util/source.test.ts`
(`req:version-sources-001`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
id: version-sources-002
title: Missing VERSION is initialized from a tag or the default
spec_ref: ".agents/specifications/version-sources.spec.md#file-source"
---

# Missing VERSION is initialized from a tag or the default

## Requirement

Preconditions:

- The source is `file` and `VERSION` is absent.

Expected behavior:

- The CLI MUST create `VERSION` containing the nearest parseable local tag
reachable from the current ref, or `0.1.0` when no such tag exists.
- Initialization MUST work outside a Git repository by using `0.1.0`.

Postconditions:

- The file contains the selected version followed by a newline.
- Tests: `src/util/source.test.ts` (`req:version-sources-002`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
id: version-sources-003
title: Git source derives the current version from tags
spec_ref: ".agents/specifications/version-sources.spec.md#git-source"
---

# Git source derives the current version from tags

## Requirement

Preconditions:

- The configuration sets `source: git`.

Expected behavior:

- `get` and `parse` without a value MUST report the version from the nearest
parseable local tag reachable from the current ref, or `0.1.0` if none exists.
- If several version tags point to that commit, the highest version among them
MUST be selected.
- Resolving the current version MUST remove an existing `VERSION` file.

Postconditions:

- The resolved version is independent of the former file content.
- Tests: `src/util/config.test.ts` and `src/util/source.test.ts`
(`req:version-sources-003`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: version-sources-004
title: Git increment updates hooks without writing the source of truth
spec_ref: ".agents/specifications/version-sources.spec.md#git-source"
---

# Git increment updates hooks without writing the source of truth

## Requirement

Preconditions:

- The configuration sets `source: git` and defines a post hook.

Expected behavior:

- `inc patch` MUST report the same proposed version as `git-latest patch`.
- Every `inc` variant MUST apply configured post hooks with its proposed
version.
- It MUST NOT create `VERSION` or a tag.

Postconditions:

- Local tags are unchanged, and the hook target reflects the proposed version.
- Tests: `src/util/source.test.ts` (`req:version-sources-004`).
24 changes: 24 additions & 0 deletions .agents/requirements/version-sources/005-git-set.requirement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
id: version-sources-005
title: Git set applies hooks without persisting the specified version
spec_ref: ".agents/specifications/version-sources.spec.md#git-source"
---

# Git set applies hooks without persisting the specified version

## Requirement

Preconditions:

- The configuration sets `source: git` and defines a post hook.

Expected behavior:

- `set` MUST apply its specified version, including prerelease metadata, to the
configured post hook.
- It MUST NOT create `VERSION` or a Git tag.

Postconditions:

- The hook target reflects the specified version; the tag set is unchanged.
- Tests: `src/util/source.test.ts` (`req:version-sources-005`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
id: version-sources-006
title: Tag lookup excludes tags outside the current ref
spec_ref: ".agents/specifications/version-sources.spec.md#tag-lookup"
---

# Tag lookup excludes tags outside the current ref

## Requirement

Preconditions:

- A higher version tag exists only on an unrelated branch.
- A lower version tag is reachable from the current ref.

Expected behavior:

- Tag based resolution MUST choose the reachable tag even though the unrelated
tag has a higher version.
- It MUST work without a configured remote.

Postconditions:

- Neither the branch nor its tags are changed by lookup.
- Tests: `src/util/source.test.ts` (`req:version-sources-006`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
id: version-sources-007
title: File increment persists the proposed version and runs hooks
spec_ref: ".agents/specifications/version-sources.spec.md#file-source"
---

# File increment persists the proposed version and runs hooks

## Requirement

Preconditions:

- The source is `file`, `VERSION` contains `1.2.3`, and a post hook targets a
project file containing `1.2.3`.

Expected behavior:

- `inc patch` MUST persist `1.2.4` in `VERSION` and apply the configured hook
with that version.

Postconditions:

- Both `VERSION` and the hook target reflect `1.2.4`.
- Tests: `src/util/source.test.ts` (`req:version-sources-007`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
id: version-sources-008
title: File set persists the specified version and runs hooks
spec_ref: ".agents/specifications/version-sources.spec.md#file-source"
---

# File set persists the specified version and runs hooks

## Requirement

Preconditions:

- The source is `file` and a post hook targets a project file.

Expected behavior:

- `set` MUST write the specified version, including prerelease metadata, to
`VERSION` and apply the configured post hook with that version.

Postconditions:

- Both `VERSION` and the hook target reflect the specified version.
- Tests: `src/util/source.test.ts` (`req:version-sources-008`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
id: version-sources-009
title: Shallow clones deepen minimally to find the nearest version tag
spec_ref: ".agents/specifications/version-sources.spec.md#tag-lookup"
---

# Shallow clones deepen minimally to find the nearest version tag

## Requirement

Preconditions:

- A shallow checkout has an `origin` remote and a parseable tag beyond its
shallow boundary.
- An older reachable tag has a higher version number.
- Another branch has an unrelated, higher version tag.

Expected behavior:

- Tag based resolution MUST deepen only the current checkout's commit history,
filtering out file trees and blobs, re-checking local tags after each fetch,
until a reachable tag is found or the full history has been fetched.
- It MUST report the nearest tag's version even when an older reachable tag has
a higher version number, and even when deepening also pulls in unrelated tags.
- If the remote advertises no tags at all, it MUST use `0.1.0` without deepening
the checkout.

Postconditions:

- Historical tree and blob objects beyond the resolved tag are absent locally.
- Tests: `src/util/source.test.ts` (`req:version-sources-009`).
11 changes: 11 additions & 0 deletions .agents/specifications/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# semver-cli specification

The normative contract for semver-cli, as a set of focused chapters. Each
chapter states what the system guarantees; requirements in
[`../requirements/`](../requirements/README.md) refine chapters into testable
statements.

## Chapters

- [Version sources](version-sources.spec.md) describes how commands derive and
apply the current version.
50 changes: 50 additions & 0 deletions .agents/specifications/version-sources.spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Version sources

This chapter defines how semver-cli derives the current version and how commands
apply a proposed version. The source is selected by the top level `source`
setting in the version configuration.

## Source selection

The supported sources are `file` and `git`. When `source` is absent, the source
is `file`.

## File source

An existing `VERSION` file in the working directory is authoritative. When it is
absent, the CLI initializes it from the nearest parseable local Git tag
reachable from the current ref. If no such tag exists, it initializes the file
to `0.1.0`.

`get` reports a version derived from the current file without changing its
content. `inc` persists the proposed version to the file and applies configured
post hooks. `set` writes the specified version to the file and applies the
configured post hooks.

## Git source

The nearest parseable local Git tag reachable from the current ref is
authoritative. If no such tag exists, the current version is `0.1.0`. Commands
that resolve the current version remove an existing `VERSION` file.

`get` and `parse` without an explicit value derive their result from that
current version. `inc` calculates and reports a proposed version using the same
version calculation as `git-latest` and applies configured post hooks. It does
not write a `VERSION` file or create a tag. `set` applies the specified version
to configured post hooks without creating a `VERSION` file or a tag.

## Tag lookup

The nearest tag is the first parseable version tag reachable while walking
commits from the current ref, using only the commit history currently available
locally. When several version tags name the same commit, the highest version
among them is used. Tags on unrelated branches are excluded.

In a shallow repository with an `origin` remote, resolution first checks whether
the remote advertises any tags at all. If so, it deepens the current checkout's
commit history incrementally, filtering out file trees and blobs, and re-checks
the local tags after each fetch until it finds a reachable tag or the full
history has been fetched. This may also fetch tags unrelated to the current ref,
but never their tree or blob contents. A complete repository, a shallow
repository without `origin`, or a remote with no tags at all uses `0.1.0`
without deepening further.
17 changes: 17 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Specifications

This repository follows the specs method, as declared in `.agents/SPECS`
(<https://github.com/optum-eeps/specs>).

- Authority, highest first: specifications (`.agents/specifications/`),
requirements (`.agents/requirements/`), tests, implementation. Never change a
lower layer to contradict a higher one; if a request conflicts with the
specification or a requirement, ask before proceeding.
- A behavior change updates the specification or requirement that covers it in
the same change as the code.
- Tests cite the requirements they verify with `req:{id}`.
- Before writing specifications or requirements, use the `specs` and
`requirements` skills. Without the plugin installed, read them at
<https://raw.githubusercontent.com/optum-eeps/specs/v1.0.0/skills/specs/SKILL.md>
and
<https://raw.githubusercontent.com/optum-eeps/specs/v1.0.0/skills/requirements/SKILL.md>.
36 changes: 28 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ Options:

### About

The `get`, `set` and `inc` commands operate against your working directory. They
all operate against a `VERSION` file found in the root folder of your project.
The `get`, `set` and `inc` commands operate against your working directory. By
default, they use a `VERSION` file in the root folder of your project.

If the `VERSION` file is not found the default version is `0.1.0`. The `inc`
command will create the `VERSION` file if it doesn't already exist.
If the `VERSION` file is not found, semver initializes it from the current ref's
local git tags, falling back to `0.1.0` when there are no tags.

The `parse` command accepts a version string as input and parses and prints that
version as output if it is valid.
Expand All @@ -82,10 +82,13 @@ The `sort` command accepts one or more version strings and outputs them in
sorted order (descending by default, one version per line). Use the `-a` flag
for ascending order, or read versions from stdin using `--`.

The `git-latest` command returns the most recent parseable git tag. If an error
occurs (e.g., git is unavailable, not in a git repo, no tags, etc.), exits 1.
This **does not** create or modify tags. Supports the same subcommands and
options as `get`/`set`/`inc`, i.e.:
The `git-latest` command returns the nearest parseable local git tag reachable
from the current ref, or `0.1.0` when there are no tags. Git errors still
exit 1. In a shallow checkout with `origin`, it deepens the current checkout's
commit history only until it finds a version tag, then fetches just that tag
without historical file trees or unrelated branch histories. This **does not**
create release tags. It supports the same subcommands and options as
`get`/`set`/`inc`, i.e.:

```sh
semver git-latest # 1.2.3
Expand Down Expand Up @@ -179,6 +182,23 @@ In addition to updating the `VERSION` file, semver can optionally update source
code files in your project. To configure the version command you can provide a
configuration file at `.github/version.yml`.

The optional top level `source` setting selects where the current version comes
from. The default is `file`: an existing `VERSION` is authoritative. When the
file is missing, semver initializes it from the latest local git tag reachable
from the current ref, or `0.1.0` if there is no tag. With `source: git`, local
git tags are authoritative and any `VERSION` file is removed. `get`, `parse`
(without a value), and `inc` use the tag version, falling back to `0.1.0`. `inc`
applies configured post hooks with its proposed version, and `set` applies its
specified version to those hooks. Neither command creates a file or tag.

```yml
source: git
on:
post:
- kind: patch
file: package.json
```

There are a number of supported post hook kinds and a number of supported known
project kinds which make updating source code files easy.

Expand Down
Loading
Loading