502 lines
22 KiB
Markdown
502 lines
22 KiB
Markdown
# Implementation Plan 0001: Seriatim Source Installation
|
|
|
|
Status: proposed
|
|
|
|
Target executor: GPT-5.6-Terra
|
|
|
|
Feature roadmap:
|
|
`docs/roadmap/features/0001-seriatim-source-installation.md`
|
|
|
|
## Objective
|
|
|
|
Implement `ansible-role-seriatim` as an independently usable role that installs
|
|
Seriatim `v1.5.0` from its pinned Git source, builds it with the local Go 1.26
|
|
Snap toolchain, installs it safely, and converges idempotently. Preserve the
|
|
feature roadmap as the authority for scope, rationale, target state, and
|
|
acceptance criteria.
|
|
|
|
## Execution Rules
|
|
|
|
- Execute Stage 1 through Stage 4 in numeric order.
|
|
- Before each stage, confirm its preconditions and the working-tree state of
|
|
every repository named by that stage.
|
|
- Preserve unrelated and pre-existing changes. Stop if the owning repository
|
|
is not clean at a stage boundary unless the changes are the expected output
|
|
of the immediately preceding stage.
|
|
- Do not modify `rakestraw-ansible`, any upstream application repository, any
|
|
other role, or the collection.
|
|
- Do not make new product or architecture decisions. Stop and report any
|
|
conflict between this plan, the feature roadmap, and an upstream contract.
|
|
- Prefer `ansible.builtin` modules where they directly fit. Use the approved
|
|
`community.general.snap` module for channel-aware Snap management and pin
|
|
its collection dependency.
|
|
- Run every stage's validation before committing it. Do not begin a dependent
|
|
stage after a validation failure.
|
|
- Commit each completed stage in `ansible-role-seriatim` using the specified
|
|
commit intent. Do not push unless separately instructed.
|
|
- Treat `/usr/local/src/seriatim`, `/usr/local/bin/seriatim`, and the adjacent
|
|
role build-state directory as disposable managed test state during local
|
|
integration. Do not remove or alter unrelated host paths.
|
|
- Leave workspace roadmap and status files unchanged during implementation;
|
|
report stage commits and evidence for review instead.
|
|
|
|
## Starting State and Preconditions
|
|
|
|
- `ansible-workspace-dnd` and `ansible-role-seriatim` start clean on `main`.
|
|
- The active implementation plan has been reviewed and committed before
|
|
execution begins.
|
|
- The controller host is Ubuntu 26.04.1 `amd64` and has passwordless privilege
|
|
escalation available for the approved local integration run.
|
|
- Workspace tools are available at `../.venv/bin` from the role repository,
|
|
including Ansible Core 2.21.3 and ansible-lint 26.8.0.
|
|
- The managed host has working Snap infrastructure and outbound access to the
|
|
Seriatim Git repository, the Snap Store, and Go module sources.
|
|
- Upstream tag `v1.5.0` resolves to commit
|
|
`0fc92f364316c874d366b00cdc884a33e1900b25`.
|
|
- The upstream `v1.5.0` contract is:
|
|
- module requirement `go 1.25`;
|
|
- build package `./cmd/seriatim`;
|
|
- `CGO_ENABLED=0` and `-trimpath`;
|
|
- linker flags `-s -w -X
|
|
gitea.maximumdirect.net/eric/seriatim/internal/buildinfo.Version=<version>`;
|
|
- `seriatim --version` output `seriatim version <version>`.
|
|
|
|
## Repository Scope
|
|
|
|
- `ansible-role-seriatim` is the only implementation repository and owns every
|
|
role, test, dependency, and README change in this plan.
|
|
- `ansible-workspace-dnd` owns this plan and the feature roadmap but remains
|
|
read-only during out-of-band execution.
|
|
- `../seriatim` may be inspected as the authoritative upstream contract and
|
|
must remain unchanged.
|
|
- All other repositories in `workspace.yml` remain unchanged.
|
|
|
|
## Fixed Role Interface
|
|
|
|
Define and document these public defaults. All paths are managed-host paths.
|
|
|
|
| Variable | Default | Purpose |
|
|
| --- | --- | --- |
|
|
| `seriatim_version` | `v1.5.0` | Release identity expected from the installed binary |
|
|
| `seriatim_repository_url` | `https://gitea.maximumdirect.net/eric/seriatim.git` | Authoritative source repository |
|
|
| `seriatim_source_ref` | `{{ seriatim_version }}` | Git tag, branch, or commit to check out |
|
|
| `seriatim_build_version` | `{{ seriatim_version }}` | Value injected into Seriatim build metadata |
|
|
| `seriatim_source_dir` | `/usr/local/src/seriatim` | Fully managed source checkout |
|
|
| `seriatim_binary_path` | `/usr/local/bin/seriatim` | Installed executable |
|
|
| `seriatim_build_state_dir` | `/usr/local/src/seriatim.ansible-build-state` | Sidecar build inputs, fingerprint, and checksum |
|
|
| `seriatim_manage_build_dependencies` | `true` | Whether the role installs Git and Go |
|
|
| `seriatim_go_snap_channel` | `1.26/stable` | Go Snap channel |
|
|
| `seriatim_go_binary` | `/snap/bin/go` | Go executable used for version discovery and builds |
|
|
| `seriatim_go_build_flags` | `[-trimpath]` | Ordered arguments passed before `-ldflags` |
|
|
| `seriatim_go_ldflags` | upstream release linker flags using `seriatim_build_version` | Linker argument passed as one value |
|
|
| `seriatim_go_package` | `./cmd/seriatim` | Package built from the checkout |
|
|
|
|
`seriatim_source_ref` and `seriatim_build_version` are deliberately distinct.
|
|
A development branch or commit override must set an honest build-version value
|
|
as well as the source ref. Fixed build environment values are not public
|
|
overrides: `GOWORK=off`, `GOTOOLCHAIN=local`, and `CGO_ENABLED=0`.
|
|
|
|
## Stage 1: Establish the Role Contract and Static Validation
|
|
|
|
Completed: `ac35c1f` (`feat: define Seriatim role contract`)
|
|
|
|
### Objective
|
|
|
|
Create a conventional role skeleton with a fully specified variable contract,
|
|
argument validation, platform guards, dependency manifest, and syntax-test
|
|
entry point, without installing application state yet.
|
|
|
|
### Preconditions
|
|
|
|
- `ansible-role-seriatim` is clean on `main` and tracks `origin/main`.
|
|
- The workspace virtual environment passes
|
|
`../.venv/bin/ansible-playbook --version`.
|
|
- The feature roadmap and Fixed Role Interface above have no unresolved
|
|
conflicts.
|
|
|
|
### Owning Repository
|
|
|
|
`ansible-role-seriatim`
|
|
|
|
### Required Changes
|
|
|
|
1. Add the standard role paths `defaults/`, `meta/`, `tasks/`, `tests/`, and
|
|
`collections/`. Use YAML document markers and fully qualified module names.
|
|
2. Define every variable in the Fixed Role Interface in `defaults/main.yml`.
|
|
Do not expose aliases, compatibility variables, removal controls, or
|
|
configuration-file settings.
|
|
3. Add `meta/main.yml` with Eric Rakestraw as author, BSD-3-Clause licensing,
|
|
minimum Ansible Core 2.18, Ubuntu `resolute` platform metadata, source and
|
|
issue URLs, and tags appropriate to Seriatim and transcript processing.
|
|
4. Add `meta/argument_specs.yml`. Validate booleans, strings, lists, and paths
|
|
by type; require nonempty repository/ref/version/build values; and describe
|
|
every public variable.
|
|
5. Add `tasks/main.yml` and `tasks/validate.yml`. Run validation before every
|
|
task that can change the host. Assert:
|
|
- distribution is Ubuntu;
|
|
- distribution version is `26.04`;
|
|
- architecture is `x86_64` or `aarch64`;
|
|
- source, binary, and state paths are absolute and pairwise distinct;
|
|
- repository, source ref, build version, Go binary, build flags, linker
|
|
flags, and package are nonempty;
|
|
- build version contains no whitespace.
|
|
6. Pin `community.general` version `13.3.0` in
|
|
`collections/requirements.yml`. This collection requires Ansible Core 2.18
|
|
or newer and supplies the channel-aware `community.general.snap` module.
|
|
7. Extend `.gitignore` only for repository-local validation state, including
|
|
`.ansible/`, test output, and retry files. Preserve the existing general
|
|
ignore entries.
|
|
8. Add `tests/inventory` targeting localhost with local connection and an
|
|
explicit `/usr/bin/python3.14` interpreter. Add `tests/converge.yml` that
|
|
gathers facts, enables privilege escalation, and applies the repository
|
|
role through `ANSIBLE_ROLES_PATH`.
|
|
9. Replace the placeholder README with the role purpose, supported platform,
|
|
dependency-installation command, complete variable table, development-ref
|
|
rule, and an explicit statement that this stage does not yet install a
|
|
binary.
|
|
|
|
### Validation
|
|
|
|
From `ansible-role-seriatim`, run:
|
|
|
|
```sh
|
|
../.venv/bin/ansible-galaxy collection install \
|
|
-p .ansible/collections -r collections/requirements.yml
|
|
ANSIBLE_COLLECTIONS_PATH="$PWD/.ansible/collections" \
|
|
../.venv/bin/ansible-playbook -i tests/inventory tests/converge.yml \
|
|
--syntax-check
|
|
ANSIBLE_COLLECTIONS_PATH="$PWD/.ansible/collections" \
|
|
../.venv/bin/ansible-lint .
|
|
git diff --check
|
|
```
|
|
|
|
Expected result: `community.general` 13.3.0 is installed under the ignored
|
|
repository-local path, the test playbook passes syntax checking, ansible-lint
|
|
reports no violations, and Git reports no whitespace errors. Do not run the
|
|
converge playbook without `--syntax-check` during this stage.
|
|
|
|
### Completion Criteria
|
|
|
|
- The public defaults exactly match the Fixed Role Interface.
|
|
- Invalid platform and malformed-variable paths fail before host mutation.
|
|
- Static validation passes with the pinned collection available locally.
|
|
- Only `ansible-role-seriatim` contains implementation changes.
|
|
|
|
### Commit
|
|
|
|
Commit intent: `feat: define Seriatim role contract`
|
|
|
|
## Stage 2: Manage Build Dependencies and the Source Checkout
|
|
|
|
Completed: `b84b045` (`feat: manage Seriatim source prerequisites`)
|
|
|
|
### Objective
|
|
|
|
Implement idempotent Git and Go prerequisite management and converge the
|
|
root-owned Seriatim checkout to the selected source revision, without building
|
|
or installing the application binary yet.
|
|
|
|
### Preconditions
|
|
|
|
- Stage 1 is committed and its validation still passes.
|
|
- `snap info go` shows the configured `1.26/stable` channel.
|
|
- No unmanaged data that must be retained exists under the configured source
|
|
directory; that tree is deliberately authoritative and disposable.
|
|
|
|
### Owning Repository
|
|
|
|
`ansible-role-seriatim`
|
|
|
|
### Required Changes
|
|
|
|
1. Add `tasks/dependencies.yml` and include it after validation.
|
|
2. When `seriatim_manage_build_dependencies` is true:
|
|
- install the Ubuntu `git` package with `ansible.builtin.apt`, using a
|
|
reasonable cache-validity interval rather than refreshing on every run;
|
|
- install the single `go` Snap through `community.general.snap` with
|
|
`classic: true`, `channel: "{{ seriatim_go_snap_channel }}"`, and
|
|
`state: present`.
|
|
3. Whether dependency management is enabled or disabled, resolve Git and the
|
|
configured Go executable and fail clearly if either is unavailable. Run
|
|
`go version`, extract its semantic version, and require Go 1.25 or newer.
|
|
4. Add `tasks/source.yml` and include it after dependencies. Create the parent
|
|
source directory as `root:root` before cloning.
|
|
5. Use `ansible.builtin.git` with the configured repository and source ref,
|
|
`update: true`, `force: true`, and no shallow-clone assumption. Do not accept
|
|
host keys automatically and do not embed credentials.
|
|
6. Make the checkout fully managed: detect tracked or untracked dirt after the
|
|
Git task, reset tracked files to the selected checkout, and remove untracked
|
|
and ignored content with explicit Git commands only when dirt exists. Keep
|
|
the build-state sidecar outside the checkout so cleanup cannot remove it.
|
|
7. Enforce root ownership on the checkout and resolve the final commit with
|
|
`git rev-parse HEAD`. Assert that the default `v1.5.0` execution resolves to
|
|
`0fc92f364316c874d366b00cdc884a33e1900b25` in the local integration test;
|
|
nondefault refs record their actual resolved commit without that assertion.
|
|
8. Update the README to describe dependency opt-out behavior, channel
|
|
convergence, authoritative checkout cleanup, and requirements for private
|
|
repository authentication overrides without documenting any credentials.
|
|
|
|
### Validation
|
|
|
|
Run the Stage 1 static commands, then run:
|
|
|
|
```sh
|
|
ANSIBLE_COLLECTIONS_PATH="$PWD/.ansible/collections" \
|
|
ANSIBLE_ROLES_PATH="$(dirname "$PWD")" \
|
|
../.venv/bin/ansible-playbook -i tests/inventory tests/converge.yml
|
|
/snap/bin/go version
|
|
sudo git -C /usr/local/src/seriatim rev-parse HEAD
|
|
sudo test "$(stat -c '%U:%G' /usr/local/src/seriatim)" = root:root
|
|
```
|
|
|
|
Expected result: the play completes successfully without creating
|
|
`/usr/local/bin/seriatim`; Go is on the `1.26/stable` track and satisfies the
|
|
minimum version; the checkout resolves to the expected `v1.5.0` commit; and its
|
|
root directory is owned by `root:root`.
|
|
|
|
Create an untracked file and a tracked-file modification beneath the checkout,
|
|
rerun convergence, and verify both are removed. Run convergence once more and
|
|
require `changed=0` in the play recap.
|
|
|
|
### Completion Criteria
|
|
|
|
- Dependency management and opt-out behavior fail or converge as specified.
|
|
- Go channel and minimum-version validation are explicit and idempotent.
|
|
- Default source state is the expected clean, root-owned commit.
|
|
- Unmanaged checkout changes are discarded by a subsequent role run.
|
|
- A clean repeated run reports no changes.
|
|
|
|
### Commit
|
|
|
|
Commit intent: `feat: manage Seriatim source prerequisites`
|
|
|
|
## Stage 3: Build and Install Seriatim Reproducibly
|
|
|
|
### Objective
|
|
|
|
Add fingerprint-driven source compilation, staged validation, atomic binary
|
|
installation, and durable build-state recording without replacing a working
|
|
binary after a failed build.
|
|
|
|
### Preconditions
|
|
|
|
- Stage 2 is committed and its clean repeated convergence reports no changes.
|
|
- The default checkout is the expected `v1.5.0` commit.
|
|
- `/snap/bin/go version` reports a toolchain compatible with the upstream
|
|
module requirement.
|
|
|
|
### Owning Repository
|
|
|
|
`ansible-role-seriatim`
|
|
|
|
### Required Changes
|
|
|
|
1. Add `tasks/build.yml` after source convergence.
|
|
2. Construct one canonical build-input document containing at least:
|
|
- resolved source commit;
|
|
- complete `go version` output;
|
|
- configured Go binary path;
|
|
- build version;
|
|
- ordered build flags;
|
|
- linker flags;
|
|
- package path;
|
|
- fixed `GOWORK`, `GOTOOLCHAIN`, and `CGO_ENABLED` values.
|
|
Store the readable input document and its SHA-256 fingerprint in
|
|
`seriatim_build_state_dir` only after successful installation.
|
|
3. Record the installed binary's SHA-256 checksum beside the fingerprint.
|
|
Require a rebuild when the binary, any state file, or the checkout is
|
|
missing; when the desired fingerprint differs; when the installed checksum
|
|
differs; or when the installed version command fails or differs from
|
|
`seriatim version {{ seriatim_build_version }}`. Malformed or unreadable
|
|
state must cause a rebuild rather than an unhandled template failure.
|
|
4. When no rebuild is required, perform no mutating build or install task and
|
|
report no change.
|
|
5. When rebuilding, create a unique temporary directory with
|
|
`ansible.builtin.tempfile`. Invoke the configured Go binary with
|
|
`ansible.builtin.command` `argv`, the fixed environment, configured build
|
|
flags, one `-ldflags` value, a staged output path, and the configured package
|
|
from `seriatim_source_dir`.
|
|
6. Validate the staged binary before installation. Require exact stdout
|
|
`seriatim version {{ seriatim_build_version }}` and a successful exit from
|
|
`--version`.
|
|
7. Install the validated staged binary to `seriatim_binary_path` using an
|
|
atomic Ansible file operation with `root:root` ownership and mode `0755`.
|
|
Validate the installed binary again, calculate its checksum, and only then
|
|
write the build-input, fingerprint, and checksum state files.
|
|
8. Use an `always` cleanup path so temporary build content is removed after
|
|
success or failure. Never delete or overwrite the previously installed
|
|
binary or successful build state before a replacement has passed staged
|
|
validation.
|
|
9. Give command tasks precise `changed_when` and `failed_when` behavior so
|
|
ansible-lint passes and check/reporting semantics are honest.
|
|
10. Update the README with the exact build contract, rebuild triggers, state
|
|
location, ownership, safe replacement behavior, and development override
|
|
example setting both `seriatim_source_ref` and
|
|
`seriatim_build_version`.
|
|
|
|
### Validation
|
|
|
|
Run static validation, then converge and inspect:
|
|
|
|
```sh
|
|
ANSIBLE_COLLECTIONS_PATH="$PWD/.ansible/collections" \
|
|
ANSIBLE_ROLES_PATH="$(dirname "$PWD")" \
|
|
../.venv/bin/ansible-playbook -i tests/inventory tests/converge.yml
|
|
/usr/local/bin/seriatim --version
|
|
sudo stat -c '%U:%G %a' /usr/local/bin/seriatim
|
|
sudo find /usr/local/src/seriatim.ansible-build-state \
|
|
-maxdepth 1 -type f -print -exec sed -n '1,80p' {} \;
|
|
```
|
|
|
|
Expected result: the command prints exactly `seriatim version v1.5.0`; the
|
|
binary is `root:root` mode `755`; readable build inputs, fingerprint, and
|
|
checksum exist in the sidecar state directory; and no temporary staging
|
|
directory remains.
|
|
|
|
Run the same convergence immediately again and require `changed=0`. Then change
|
|
one effective build flag through an extra variable, run convergence, and
|
|
require the build/install tasks to report changes. Repeat with that override
|
|
and require `changed=0`; finally restore defaults and require exactly one more
|
|
rebuild followed by a clean repeat.
|
|
|
|
### Completion Criteria
|
|
|
|
- The installed binary matches the upstream version and ownership contract.
|
|
- Build state records every required input and the installed checksum.
|
|
- Missing, corrupt, or changed binary/state/input conditions rebuild.
|
|
- Unchanged convergence reports no changes.
|
|
- Temporary content is cleaned on both successful and failed task paths.
|
|
- Static validation remains clean.
|
|
|
|
### Commit
|
|
|
|
Commit intent: `feat: build and install Seriatim from source`
|
|
|
|
## Stage 4: Add Local Integration Regression Coverage
|
|
|
|
### Objective
|
|
|
|
Turn the required local validation into a repeatable, failure-safe repository
|
|
test harness and complete the role's operator and maintainer documentation.
|
|
|
|
### Preconditions
|
|
|
|
- Stage 3 is committed, statically clean, and manually converges twice with the
|
|
second run reporting no changes.
|
|
- The default installed binary and build state are known-good before testing
|
|
deliberate failure paths.
|
|
- No unrelated local host data exists at the managed Seriatim paths.
|
|
|
|
### Owning Repository
|
|
|
|
`ansible-role-seriatim`
|
|
|
|
### Required Changes
|
|
|
|
1. Add `tests/verify.yml` with nonmutating assertions for:
|
|
- supported platform facts;
|
|
- source commit and cleanliness;
|
|
- source, binary, and state ownership;
|
|
- binary mode;
|
|
- exact installed version output;
|
|
- presence and consistency of build inputs, fingerprint, and installed
|
|
checksum.
|
|
2. Add an executable `tests/integration` shell driver that locates the
|
|
workspace virtual environment relative to the repository, installs the
|
|
pinned collection into `.ansible/collections`, exports local collection and
|
|
role paths, and runs syntax checking plus ansible-lint before host mutation.
|
|
3. Have the driver run default convergence, verification, and an immediate
|
|
second convergence. Parse the second recap and fail unless `changed=0` and
|
|
`failed=0`.
|
|
4. Test the mutation guard with separate plays that override gathered facts to
|
|
represent an unsupported distribution, Ubuntu release, and architecture.
|
|
Each play must fail in validation before a mutating task; capture the
|
|
installed binary checksum and successful state beforehand and require both
|
|
to remain unchanged afterward.
|
|
5. Test source authority by creating both a tracked modification and an
|
|
untracked file in the managed checkout, converging, and verifying a clean
|
|
checkout at the expected commit.
|
|
6. Test compiler-input invalidation with a temporary Go wrapper. Its `version`
|
|
path must report a distinct valid Go 1.26 patch identity while all other
|
|
commands delegate to `/snap/bin/go`. Converge with
|
|
`seriatim_go_binary` set to the wrapper, require a rebuild, repeat and
|
|
require idempotence, then restore the default Go path and require one final
|
|
rebuild followed by idempotence.
|
|
7. Test failure preservation with a second temporary Go wrapper that reports a
|
|
valid version but fails the build invocation. Capture the installed binary
|
|
checksum and successful state files before the expected failed play. After
|
|
failure, require the binary checksum and state contents to be byte-for-byte
|
|
unchanged and require `seriatim --version` still to report `v1.5.0`.
|
|
8. Use a temporary directory and cleanup trap for wrappers and captured test
|
|
output. The driver must restore default role convergence before exit where
|
|
possible and must never delete unrelated host content.
|
|
9. Update the README with development-environment setup, static validation,
|
|
local integration warnings, the single test-driver command, expected host
|
|
mutations, and recovery instructions limited to rerunning default
|
|
convergence.
|
|
10. Remove any Stage 1 wording that says installation is not implemented.
|
|
|
|
### Validation
|
|
|
|
From `ansible-role-seriatim`, run:
|
|
|
|
```sh
|
|
tests/integration
|
|
git diff --check
|
|
git status --short
|
|
```
|
|
|
|
Expected result: the driver completes every static, convergence, idempotence,
|
|
source-cleanup, compiler-change, failure-preservation, and final verification
|
|
case; the installed host state ends at default Seriatim `v1.5.0`; Git reports
|
|
no whitespace errors; and only intended repository changes are present before
|
|
the stage commit.
|
|
|
|
After committing, run `tests/integration` once more. The repository must remain
|
|
clean afterward.
|
|
|
|
### Completion Criteria
|
|
|
|
- One documented command reproduces all MVP validation on the local Ubuntu
|
|
26.04 proxy.
|
|
- Idempotence, compiler-version invalidation, managed-source cleanup, exact
|
|
versioning, and failed-build preservation have executable regression
|
|
coverage.
|
|
- README instructions match the implemented role and test behavior.
|
|
- The post-commit integration run passes and leaves the role repository clean.
|
|
|
|
### Commit
|
|
|
|
Commit intent: `test: verify Seriatim source installation`
|
|
|
|
## Final Validation and Handoff
|
|
|
|
After Stage 4, run:
|
|
|
|
```sh
|
|
tests/integration
|
|
git log --oneline -4
|
|
git status --short --branch
|
|
cd ..
|
|
./scripts/status
|
|
```
|
|
|
|
The implementation handoff must report:
|
|
|
|
- the four stage commit hashes in order;
|
|
- the final `seriatim --version` output;
|
|
- the final source commit, Go version, binary checksum, ownership, and mode;
|
|
- the clean-repeat recap showing `changed=0` and `failed=0`;
|
|
- confirmation that deliberate build failure preserved the prior binary and
|
|
state;
|
|
- every deviation from this plan or feature roadmap;
|
|
- any unresolved findings or recommended follow-up work.
|
|
|
|
Do not mark Feature 0001 `done` and do not edit workspace status during the
|
|
out-of-band execution. Completion requires subsequent review.
|
|
|
|
## Progress Notes
|
|
|
|
- Stage 1 completed in `ansible-role-seriatim` commit `ac35c1f`.
|
|
- Stage 2 completed in `ansible-role-seriatim` commit `b84b045`.
|