feat: build and install Seriatim from source

This commit is contained in:
2026-09-07 18:09:24 +00:00
parent b84b045f31
commit 81313f02eb
3 changed files with 217 additions and 2 deletions

View File

@@ -4,8 +4,8 @@ Installs the [Seriatim](https://gitea.maximumdirect.net/eric/seriatim)
transcript processor from source. The role supports Ubuntu 26.04 on `amd64`
and `arm64` hosts and is independently usable outside the D&D pipeline role.
The role manages its build dependencies and a root-owned source checkout. At
this point binary installation is added by subsequent implementation work.
The role manages its build dependencies and a root-owned source checkout,
builds a native static executable, validates it, and safely installs it.
## Requirements
@@ -49,6 +49,29 @@ provided independently through the managed host's Git configuration or SSH
environment; this role does not manage credentials or accept unknown host
keys.
## Build and installation behavior
Builds run from the selected checkout with `GOWORK=off`,
`GOTOOLCHAIN=local`, `CGO_ENABLED=0`, the configured ordered build flags, and
the upstream version-injection linker flags. The resulting executable is
staged and must report the exact configured version before it atomically
replaces `seriatim_binary_path`. It is installed as `root:root` mode `0755`.
The role records readable inputs, their SHA-256 fingerprint, and the installed
binary checksum beneath `seriatim_build_state_dir`. A source commit, compiler
identity, Go path, version, flags, linker flags, package, or fixed environment
change triggers a rebuild. Missing or corrupt state, a modified binary, or an
incorrect installed version also triggers rebuilding. Failed compilation or
staged validation leaves the previous installed binary and successful state
untouched.
For example, development from a branch must identify both source and output:
```yaml
seriatim_source_ref: feature/example
seriatim_build_version: v1.5.0-dev
```
## Static validation
```sh

189
tasks/build.yml Normal file
View File

@@ -0,0 +1,189 @@
---
- name: Construct canonical Seriatim build inputs
ansible.builtin.set_fact:
seriatim_build_inputs: >-
{{ {
'source_commit': seriatim_source_commit,
'go_version': seriatim_go_version_result.stdout,
'go_binary': seriatim_go_binary,
'build_version': seriatim_build_version,
'build_flags': seriatim_go_build_flags,
'ldflags': seriatim_go_ldflags,
'package': seriatim_go_package,
'environment': {
'GOWORK': 'off',
'GOTOOLCHAIN': 'local',
'CGO_ENABLED': '0'
}
} | to_nice_json(indent=2, sort_keys=true) }}
- name: Calculate desired Seriatim build fingerprint
ansible.builtin.set_fact:
seriatim_desired_fingerprint: "{{ seriatim_build_inputs | hash('sha256') }}"
seriatim_stored_fingerprint: ""
seriatim_stored_checksum: ""
- name: Inspect installed Seriatim binary
ansible.builtin.stat:
path: "{{ seriatim_binary_path }}"
checksum_algorithm: sha256
register: seriatim_installed_binary_stat
- name: Inspect Seriatim build input state
ansible.builtin.stat:
path: "{{ seriatim_build_state_dir }}/build-inputs.json"
register: seriatim_build_inputs_state_stat
- name: Inspect Seriatim fingerprint state
ansible.builtin.stat:
path: "{{ seriatim_build_state_dir }}/fingerprint.sha256"
register: seriatim_fingerprint_state_stat
- name: Inspect Seriatim checksum state
ansible.builtin.stat:
path: "{{ seriatim_build_state_dir }}/binary.sha256"
register: seriatim_checksum_state_stat
- name: Read stored Seriatim fingerprint
ansible.builtin.slurp:
src: "{{ seriatim_build_state_dir }}/fingerprint.sha256"
register: seriatim_stored_fingerprint_result
when: seriatim_fingerprint_state_stat.stat.exists
- name: Read stored Seriatim checksum
ansible.builtin.slurp:
src: "{{ seriatim_build_state_dir }}/binary.sha256"
register: seriatim_stored_checksum_result
when: seriatim_checksum_state_stat.stat.exists
- name: Record stored Seriatim state values
ansible.builtin.set_fact:
seriatim_stored_fingerprint: >-
{{ seriatim_stored_fingerprint_result.content | b64decode | trim }}
seriatim_stored_checksum: >-
{{ seriatim_stored_checksum_result.content | b64decode | trim }}
when:
- seriatim_fingerprint_state_stat.stat.exists
- seriatim_checksum_state_stat.stat.exists
- name: Read installed Seriatim version
ansible.builtin.command:
argv:
- "{{ seriatim_binary_path }}"
- --version
register: seriatim_installed_version_result
changed_when: false
failed_when: false
when: seriatim_installed_binary_stat.stat.exists
- name: Decide whether Seriatim requires rebuilding
ansible.builtin.set_fact:
seriatim_build_required: >-
{{ not seriatim_installed_binary_stat.stat.exists
or not seriatim_build_inputs_state_stat.stat.exists
or not seriatim_fingerprint_state_stat.stat.exists
or not seriatim_checksum_state_stat.stat.exists
or seriatim_stored_fingerprint != seriatim_desired_fingerprint
or seriatim_stored_checksum != (seriatim_installed_binary_stat.stat.checksum | default(''))
or (seriatim_installed_version_result.rc | default(1)) != 0
or (seriatim_installed_version_result.stdout | default('')) != 'seriatim version ' ~ seriatim_build_version }}
- name: Build, validate, and install Seriatim
when: seriatim_build_required
block:
- name: Create temporary Seriatim build directory
ansible.builtin.tempfile:
state: directory
prefix: ansible-seriatim-build-
register: seriatim_build_tempdir
- name: Build staged Seriatim binary
ansible.builtin.command:
argv: >-
{{ [seriatim_go_binary, 'build']
+ seriatim_go_build_flags
+ ['-ldflags', seriatim_go_ldflags,
'-o', seriatim_build_tempdir.path ~ '/seriatim',
seriatim_go_package] }}
chdir: "{{ seriatim_source_dir }}"
environment:
GOWORK: "off"
GOTOOLCHAIN: local
CGO_ENABLED: "0"
changed_when: true
- name: Validate staged Seriatim binary
ansible.builtin.command:
argv:
- "{{ seriatim_build_tempdir.path }}/seriatim"
- --version
register: seriatim_staged_version_result
changed_when: false
failed_when: >-
seriatim_staged_version_result.rc != 0 or
seriatim_staged_version_result.stdout != 'seriatim version ' ~ seriatim_build_version
- name: Install validated Seriatim binary
ansible.builtin.copy:
src: "{{ seriatim_build_tempdir.path }}/seriatim"
dest: "{{ seriatim_binary_path }}"
remote_src: true
owner: root
group: root
mode: "0755"
- name: Validate installed Seriatim binary
ansible.builtin.command:
argv:
- "{{ seriatim_binary_path }}"
- --version
register: seriatim_new_installed_version_result
changed_when: false
failed_when: >-
seriatim_new_installed_version_result.rc != 0 or
seriatim_new_installed_version_result.stdout != 'seriatim version ' ~ seriatim_build_version
- name: Calculate installed Seriatim checksum
ansible.builtin.stat:
path: "{{ seriatim_binary_path }}"
checksum_algorithm: sha256
register: seriatim_new_binary_stat
- name: Create Seriatim build state directory
ansible.builtin.file:
path: "{{ seriatim_build_state_dir }}"
state: directory
owner: root
group: root
mode: "0755"
- name: Record Seriatim build inputs
ansible.builtin.copy:
content: "{{ seriatim_build_inputs }}\n"
dest: "{{ seriatim_build_state_dir }}/build-inputs.json"
owner: root
group: root
mode: "0644"
- name: Record Seriatim build fingerprint
ansible.builtin.copy:
content: "{{ seriatim_desired_fingerprint }}\n"
dest: "{{ seriatim_build_state_dir }}/fingerprint.sha256"
owner: root
group: root
mode: "0644"
- name: Record installed Seriatim checksum
ansible.builtin.copy:
content: "{{ seriatim_new_binary_stat.stat.checksum }}\n"
dest: "{{ seriatim_build_state_dir }}/binary.sha256"
owner: root
group: root
mode: "0644"
always:
- name: Remove temporary Seriatim build directory
ansible.builtin.file:
path: "{{ seriatim_build_tempdir.path }}"
state: absent
when: seriatim_build_tempdir.path is defined

View File

@@ -7,3 +7,6 @@
- name: Manage Seriatim source checkout
ansible.builtin.import_tasks: source.yml
- name: Build and install Seriatim
ansible.builtin.import_tasks: build.yml