Install and configure Narratio

This commit is contained in:
2026-09-09 21:45:07 +00:00
parent b97a317f9e
commit 3be78d8108
12 changed files with 877 additions and 2 deletions

155
README.md
View File

@@ -1,3 +1,154 @@
# ansible-role-narratio
# Ansible Role: Narratio
Ansible role to install the Narratio D&D transcript and session report generation suite.
Installs and configures [Narratio](https://gitea.maximumdirect.net/eric/narratio)
from source on Ubuntu 26.04 (`x86_64` or `aarch64`). The role requires gathered
facts and uses task-level privilege escalation for dependencies and managed
application state.
## Variables
```yaml
narratio_version: v2.0.0
narratio_repository_url: https://gitea.maximumdirect.net/eric/narratio.git
narratio_source_dir: /usr/local/src/narratio
narratio_binary_path: /usr/local/bin/narratio
narratio_manage_build_dependencies: true
narratio_go_snap_channel: 1.26/stable
narratio_go_binary: /snap/bin/go
narratio_user: narratio
narratio_group: narratio
narratio_config_path: /usr/local/etc/narratio/pipeline.yml
narratio_workspace_root: /var/lib/narratio
narratio_campaigns_root: /usr/local/share/narratio/campaigns
narratio_spool_root: /var/spool/narratio
narratio_cache_root: /var/cache/narratio
```
`narratio_whisperx_transcribe_url` is required and must be an absolute HTTP or
HTTPS URL. Every other pipeline setting is optional and defaults to `null`:
```yaml
narratio_configkit_imports: null
narratio_workspace_cleanup_after_publish: null
narratio_campaigns_default_campaign_id: null
narratio_storage_backend: null
narratio_storage_s3_bucket: null
narratio_storage_s3_root_prefix: null
narratio_storage_s3_region: null
narratio_storage_s3_endpoint: null
narratio_storage_s3_force_path_style: null
narratio_storage_s3_access_key_id: null
narratio_storage_s3_secret_access_key: null
narratio_spool_delete_audio_after_publish: null
narratio_cache_s3_audio: null
narratio_publish_enabled: null
narratio_publish_upload_run: null
narratio_publish_outputs: null
narratio_whisperx_language: null
narratio_whisperx_timeout: null
narratio_whisperx_retries: null
narratio_whisperx_retry_delay: null
narratio_whisperx_concurrency: null
narratio_seriatim_binary: null
narratio_seriatim_timeout: null
narratio_seriatim_output_schema: null
narratio_seriatim_coalesce_gap: null
narratio_seriatim_report: null
narratio_seriatim_env_overlap_word_run_gap: null
narratio_seriatim_env_overlap_word_run_reorder_window: null
narratio_seriatim_env_backchannel_max_duration: null
narratio_seriatim_env_filler_max_duration: null
narratio_audita_binary: null
narratio_audita_timeout: null
narratio_audita_llm_api_key: null
narratio_audita_modules: null
narratio_audita_base_url: null
narratio_audita_model: null
narratio_audita_total_llm_concurrency: null
narratio_audita_proposal_llm_concurrency: null
narratio_audita_validation_model: null
narratio_audita_validation_llm_concurrency: null
narratio_audita_transcript_description: null
narratio_audita_config_path: null
narratio_audita_output_schema: null
narratio_audita_work_dir_retention: null
narratio_audita_report: null
narratio_normalize_output_path: null
narratio_normalize_output_schema: null
narratio_normalize_report: null
narratio_trim_enabled: null
narratio_trim_output_path: null
narratio_trim_bounds_prompt_id: null
narratio_trim_bounds_profile_id: null
narratio_trim_bounds_transcript_input_name: null
narratio_trim_bounds_output_path: null
narratio_trim_bounds_timeout: null
narratio_trim_bounds_render_debug: null
narratio_trim_bounds_render_output_path: null
narratio_trim_seriatim_report: null
narratio_render_enabled: null
narratio_render_format: null
narratio_render_title: null
narratio_render_include_timestamps: null
narratio_render_include_segment_ids: null
narratio_render_include_metadata: null
narratio_scriptorium_binary: null
narratio_scriptorium_config_path: null
narratio_scriptorium_timeout: null
narratio_scriptorium_render_debug: null
narratio_scriptorium_artifacts: null
narratio_scriptorium_artifact_families: null
narratio_notarius_enabled: null
narratio_notarius_binary: null
narratio_notarius_config_path: null
narratio_notarius_pipeline_id: null
narratio_notarius_timeout: null
narratio_notarius_working_directory: null
narratio_notarius_references: null
narratio_notarius_outputs: null
narratio_notification_mode: null
```
With dependency management enabled, the role installs Git and the classic Go
Snap from `narratio_go_snap_channel`. When disabled, Git and the executable at
`narratio_go_binary` must already be available. The role checks out
`narratio_version`, builds `./cmd/narratio` to `narratio_binary_path`, and
rebuilds only when the checkout changes or the binary is absent.
The `narratio` account owns the workspace, spool, and cache roots, which are
group-writable setgid directories. The campaign registry is `root:narratio`
and operator-readable; its content remains caller-managed. The configuration
directory is `root:narratio` mode `0750`, and the pipeline is mode `0640`.
Null pipeline values and their empty parent mappings are omitted; non-null
falsey values are preserved. `narratio_publish_outputs`,
`narratio_notarius_references`, `narratio_notarius_outputs`,
`narratio_scriptorium_artifacts`, and `narratio_scriptorium_artifact_families`
are complete native caller values, so callers may compose them before assigning
them and the role does not merge or rewrite them. Credential fields name
environment variables only; credential values remain outside this role.
ConfigKit imports and profile overlays are externally managed, and this role
does not install the downstream applications, campaign content, or suite policy.
## Example
```yaml
- hosts: narratio_hosts
roles:
- role: ansible-role-narratio
vars:
narratio_whisperx_transcribe_url: https://whisperx.example.test/transcribe
```
## Validation
The repository-local integration suite installs its pinned collection, runs
syntax checking and production-profile lint, validates a baseline managed
pipeline through Narratio as its application account, and requires immediate
clean repeated convergence. It restores the baseline deployment during cleanup.
```sh
tests/integration
```
Ansible role to install the Narratio D&D transcript and session report generation suite.

2
ansible.cfg Normal file
View File

@@ -0,0 +1,2 @@
[defaults]
roles_path = ..

View File

@@ -0,0 +1,4 @@
---
collections:
- name: community.general
version: 13.3.0

94
defaults/main.yml Normal file
View File

@@ -0,0 +1,94 @@
---
narratio_version: v2.0.0
narratio_repository_url: https://gitea.maximumdirect.net/eric/narratio.git
narratio_source_dir: /usr/local/src/narratio
narratio_binary_path: /usr/local/bin/narratio
narratio_manage_build_dependencies: true
narratio_go_snap_channel: 1.26/stable
narratio_go_binary: /snap/bin/go
narratio_user: narratio
narratio_group: narratio
narratio_config_path: /usr/local/etc/narratio/pipeline.yml
narratio_workspace_root: /var/lib/narratio
narratio_campaigns_root: /usr/local/share/narratio/campaigns
narratio_spool_root: /var/spool/narratio
narratio_cache_root: /var/cache/narratio
narratio_configkit_imports: null
narratio_workspace_cleanup_after_publish: null
narratio_campaigns_default_campaign_id: null
narratio_storage_backend: null
narratio_storage_s3_bucket: null
narratio_storage_s3_root_prefix: null
narratio_storage_s3_region: null
narratio_storage_s3_endpoint: null
narratio_storage_s3_force_path_style: null
narratio_storage_s3_access_key_id: null
narratio_storage_s3_secret_access_key: null
narratio_spool_delete_audio_after_publish: null
narratio_cache_s3_audio: null
narratio_publish_enabled: null
narratio_publish_upload_run: null
narratio_publish_outputs: null
narratio_whisperx_language: null
narratio_whisperx_timeout: null
narratio_whisperx_retries: null
narratio_whisperx_retry_delay: null
narratio_whisperx_concurrency: null
narratio_seriatim_binary: null
narratio_seriatim_timeout: null
narratio_seriatim_output_schema: null
narratio_seriatim_coalesce_gap: null
narratio_seriatim_report: null
narratio_seriatim_env_overlap_word_run_gap: null
narratio_seriatim_env_overlap_word_run_reorder_window: null
narratio_seriatim_env_backchannel_max_duration: null
narratio_seriatim_env_filler_max_duration: null
narratio_audita_binary: null
narratio_audita_timeout: null
narratio_audita_llm_api_key: null
narratio_audita_modules: null
narratio_audita_base_url: null
narratio_audita_model: null
narratio_audita_total_llm_concurrency: null
narratio_audita_proposal_llm_concurrency: null
narratio_audita_validation_model: null
narratio_audita_validation_llm_concurrency: null
narratio_audita_transcript_description: null
narratio_audita_config_path: null
narratio_audita_output_schema: null
narratio_audita_work_dir_retention: null
narratio_audita_report: null
narratio_normalize_output_path: null
narratio_normalize_output_schema: null
narratio_normalize_report: null
narratio_trim_enabled: null
narratio_trim_output_path: null
narratio_trim_bounds_prompt_id: null
narratio_trim_bounds_profile_id: null
narratio_trim_bounds_transcript_input_name: null
narratio_trim_bounds_output_path: null
narratio_trim_bounds_timeout: null
narratio_trim_bounds_render_debug: null
narratio_trim_bounds_render_output_path: null
narratio_trim_seriatim_report: null
narratio_render_enabled: null
narratio_render_format: null
narratio_render_title: null
narratio_render_include_timestamps: null
narratio_render_include_segment_ids: null
narratio_render_include_metadata: null
narratio_scriptorium_binary: null
narratio_scriptorium_config_path: null
narratio_scriptorium_timeout: null
narratio_scriptorium_render_debug: null
narratio_scriptorium_artifacts: null
narratio_scriptorium_artifact_families: null
narratio_notarius_enabled: null
narratio_notarius_binary: null
narratio_notarius_config_path: null
narratio_notarius_pipeline_id: null
narratio_notarius_timeout: null
narratio_notarius_working_directory: null
narratio_notarius_references: null
narratio_notarius_outputs: null
narratio_notification_mode: null

99
meta/argument_specs.yml Normal file
View File

@@ -0,0 +1,99 @@
---
argument_specs:
main:
short_description: Install and configure Narratio from source
options:
narratio_version: {type: str, required: true}
narratio_repository_url: {type: str, required: true}
narratio_source_dir: {type: path, required: true}
narratio_binary_path: {type: path, required: true}
narratio_manage_build_dependencies: {type: bool, required: true}
narratio_go_snap_channel: {type: str, required: true}
narratio_go_binary: {type: path, required: true}
narratio_user: {type: str, required: true}
narratio_group: {type: str, required: true}
narratio_config_path: {type: path, required: true}
narratio_workspace_root: {type: path, required: true}
narratio_campaigns_root: {type: path, required: true}
narratio_spool_root: {type: path, required: true}
narratio_cache_root: {type: path, required: true}
narratio_whisperx_transcribe_url: {type: str, required: true}
narratio_configkit_imports: {type: list, required: false}
narratio_workspace_cleanup_after_publish: {type: bool, required: false}
narratio_campaigns_default_campaign_id: {type: str, required: false}
narratio_storage_backend: {type: str, required: false}
narratio_storage_s3_bucket: {type: str, required: false}
narratio_storage_s3_root_prefix: {type: str, required: false}
narratio_storage_s3_region: {type: str, required: false}
narratio_storage_s3_endpoint: {type: str, required: false}
narratio_storage_s3_force_path_style: {type: bool, required: false}
narratio_storage_s3_access_key_id: {type: str, required: false}
narratio_storage_s3_secret_access_key: {type: str, required: false}
narratio_spool_delete_audio_after_publish: {type: bool, required: false}
narratio_cache_s3_audio: {type: bool, required: false}
narratio_publish_enabled: {type: bool, required: false}
narratio_publish_upload_run: {type: bool, required: false}
narratio_publish_outputs: {type: list, required: false}
narratio_whisperx_language: {type: str, required: false}
narratio_whisperx_timeout: {type: str, required: false}
narratio_whisperx_retries: {type: int, required: false}
narratio_whisperx_retry_delay: {type: str, required: false}
narratio_whisperx_concurrency: {type: int, required: false}
narratio_seriatim_binary: {type: str, required: false}
narratio_seriatim_timeout: {type: str, required: false}
narratio_seriatim_output_schema: {type: str, required: false}
narratio_seriatim_coalesce_gap: {type: float, required: false}
narratio_seriatim_report: {type: bool, required: false}
narratio_seriatim_env_overlap_word_run_gap: {type: float, required: false}
narratio_seriatim_env_overlap_word_run_reorder_window: {type: float, required: false}
narratio_seriatim_env_backchannel_max_duration: {type: float, required: false}
narratio_seriatim_env_filler_max_duration: {type: float, required: false}
narratio_audita_binary: {type: str, required: false}
narratio_audita_timeout: {type: str, required: false}
narratio_audita_llm_api_key: {type: str, required: false}
narratio_audita_modules: {type: list, required: false}
narratio_audita_base_url: {type: str, required: false}
narratio_audita_model: {type: str, required: false}
narratio_audita_total_llm_concurrency: {type: int, required: false}
narratio_audita_proposal_llm_concurrency: {type: int, required: false}
narratio_audita_validation_model: {type: str, required: false}
narratio_audita_validation_llm_concurrency: {type: int, required: false}
narratio_audita_transcript_description: {type: str, required: false}
narratio_audita_config_path: {type: str, required: false}
narratio_audita_output_schema: {type: str, required: false}
narratio_audita_work_dir_retention: {type: str, required: false}
narratio_audita_report: {type: bool, required: false}
narratio_normalize_output_path: {type: str, required: false}
narratio_normalize_output_schema: {type: str, required: false}
narratio_normalize_report: {type: bool, required: false}
narratio_trim_enabled: {type: bool, required: false}
narratio_trim_output_path: {type: str, required: false}
narratio_trim_bounds_prompt_id: {type: str, required: false}
narratio_trim_bounds_profile_id: {type: str, required: false}
narratio_trim_bounds_transcript_input_name: {type: str, required: false}
narratio_trim_bounds_output_path: {type: str, required: false}
narratio_trim_bounds_timeout: {type: str, required: false}
narratio_trim_bounds_render_debug: {type: bool, required: false}
narratio_trim_bounds_render_output_path: {type: str, required: false}
narratio_trim_seriatim_report: {type: bool, required: false}
narratio_render_enabled: {type: bool, required: false}
narratio_render_format: {type: str, required: false}
narratio_render_title: {type: str, required: false}
narratio_render_include_timestamps: {type: bool, required: false}
narratio_render_include_segment_ids: {type: bool, required: false}
narratio_render_include_metadata: {type: bool, required: false}
narratio_scriptorium_binary: {type: str, required: false}
narratio_scriptorium_config_path: {type: str, required: false}
narratio_scriptorium_timeout: {type: str, required: false}
narratio_scriptorium_render_debug: {type: bool, required: false}
narratio_scriptorium_artifacts: {type: dict, required: false}
narratio_scriptorium_artifact_families: {type: dict, required: false}
narratio_notarius_enabled: {type: bool, required: false}
narratio_notarius_binary: {type: str, required: false}
narratio_notarius_config_path: {type: str, required: false}
narratio_notarius_pipeline_id: {type: str, required: false}
narratio_notarius_timeout: {type: str, required: false}
narratio_notarius_working_directory: {type: str, required: false}
narratio_notarius_references: {type: dict, required: false}
narratio_notarius_outputs: {type: dict, required: false}
narratio_notification_mode: {type: str, required: false}

18
meta/main.yml Normal file
View File

@@ -0,0 +1,18 @@
---
galaxy_info:
role_name: narratio
namespace: rakestraw
author: Eric Rakestraw
description: Install and configure Narratio from source
license: CC0-1.0
min_ansible_version: "2.18"
issue_tracker_url: https://gitea.maximumdirect.net/eric/ansible-role-narratio/issues
platforms:
- name: Ubuntu
versions:
- resolute
galaxy_tags:
- narratio
- transcript
- processing
dependencies: []

147
tasks/main.yml Normal file
View File

@@ -0,0 +1,147 @@
---
- name: Validate the supported platform
ansible.builtin.assert:
that:
- ansible_facts.distribution == "Ubuntu"
- ansible_facts.distribution_version == "26.04"
- ansible_facts.architecture in ["x86_64", "aarch64"]
fail_msg: Narratio supports Ubuntu 26.04 on x86_64 or aarch64.
- name: Install Git
become: true
ansible.builtin.apt:
name: git
state: present
update_cache: true
cache_valid_time: 3600
when: narratio_manage_build_dependencies
- name: Install Go
become: true
community.general.snap:
name: go
channel: "{{ narratio_go_snap_channel }}"
classic: true
state: present
when: narratio_manage_build_dependencies
- name: Create the Narratio group
become: true
ansible.builtin.group:
name: "{{ narratio_group }}"
system: true
- name: Create the Narratio account
become: true
ansible.builtin.user:
name: "{{ narratio_user }}"
group: "{{ narratio_group }}"
system: true
home: "{{ narratio_workspace_root }}"
create_home: false
shell: /usr/sbin/nologin
- name: Create installation parent directories
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- "{{ narratio_source_dir | dirname }}"
- "{{ narratio_binary_path | dirname }}"
- name: Create configuration directory
become: true
ansible.builtin.file:
path: "{{ narratio_config_path | dirname }}"
state: directory
owner: root
group: "{{ narratio_group }}"
mode: "0750"
- name: Create mutable Narratio roots
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ narratio_user }}"
group: "{{ narratio_group }}"
mode: "2775"
loop:
- "{{ narratio_workspace_root }}"
- "{{ narratio_spool_root }}"
- "{{ narratio_cache_root }}"
- name: Create the campaign registry root
become: true
ansible.builtin.file:
path: "{{ narratio_campaigns_root }}"
state: directory
owner: root
group: "{{ narratio_group }}"
mode: "0755"
- name: Check out Narratio source
become: true
ansible.builtin.git:
repo: "{{ narratio_repository_url }}"
dest: "{{ narratio_source_dir }}"
version: "{{ narratio_version }}"
force: true
register: narratio_checkout
- name: Inspect the installed binary
ansible.builtin.stat:
path: "{{ narratio_binary_path }}"
register: narratio_binary
- name: Build Narratio
become: true
ansible.builtin.command:
argv:
- "{{ narratio_go_binary }}"
- build
- -trimpath
- -ldflags
- "-s -w -X gitea.maximumdirect.net/eric/narratio/internal/buildinfo.Version={{ narratio_version }}"
- -o
- "{{ narratio_binary_path }}"
- ./cmd/narratio
chdir: "{{ narratio_source_dir }}"
environment:
CGO_ENABLED: "0"
GOWORK: "off"
GOTOOLCHAIN: local
changed_when: true
when: >-
narratio_checkout.changed or
not narratio_binary.stat.exists
- name: Converge installed metadata
become: true
ansible.builtin.file:
path: "{{ item.path }}"
state: "{{ item.state }}"
owner: root
group: root
mode: "{{ item.mode | default(omit) }}"
recurse: "{{ item.recurse | default(omit) }}"
loop:
- path: "{{ narratio_source_dir }}"
state: directory
recurse: true
- path: "{{ narratio_binary_path }}"
state: file
mode: "0755"
- name: Write Narratio pipeline configuration
become: true
ansible.builtin.template:
src: pipeline.yml.j2
dest: "{{ narratio_config_path }}"
owner: root
group: "{{ narratio_group }}"
mode: "0640"

286
templates/pipeline.yml.j2 Normal file
View File

@@ -0,0 +1,286 @@
{% if narratio_configkit_imports is not none %}
configkit:
imports: {{ narratio_configkit_imports | to_json }}
{% endif %}
workspace:
root: {{ narratio_workspace_root | to_json }}
{% if narratio_workspace_cleanup_after_publish is not none %}
cleanup_after_publish: {{ narratio_workspace_cleanup_after_publish | to_json }}
{% endif %}
campaigns:
root: {{ narratio_campaigns_root | to_json }}
{% if narratio_campaigns_default_campaign_id is not none %}
default_campaign_id: {{ narratio_campaigns_default_campaign_id | to_json }}
{% endif %}
{% if narratio_storage_backend is not none or narratio_storage_s3_bucket is not none or narratio_storage_s3_root_prefix is not none or narratio_storage_s3_region is not none or narratio_storage_s3_endpoint is not none or narratio_storage_s3_force_path_style is not none or narratio_storage_s3_access_key_id is not none or narratio_storage_s3_secret_access_key is not none %}
storage:
{% if narratio_storage_backend is not none %}
backend: {{ narratio_storage_backend | to_json }}
{% endif %}
{% if narratio_storage_s3_bucket is not none or narratio_storage_s3_root_prefix is not none or narratio_storage_s3_region is not none or narratio_storage_s3_endpoint is not none or narratio_storage_s3_force_path_style is not none or narratio_storage_s3_access_key_id is not none or narratio_storage_s3_secret_access_key is not none %}
s3:
{% if narratio_storage_s3_bucket is not none %}
bucket: {{ narratio_storage_s3_bucket | to_json }}
{% endif %}
{% if narratio_storage_s3_root_prefix is not none %}
root_prefix: {{ narratio_storage_s3_root_prefix | to_json }}
{% endif %}
{% if narratio_storage_s3_region is not none %}
region: {{ narratio_storage_s3_region | to_json }}
{% endif %}
{% if narratio_storage_s3_endpoint is not none %}
endpoint: {{ narratio_storage_s3_endpoint | to_json }}
{% endif %}
{% if narratio_storage_s3_force_path_style is not none %}
force_path_style: {{ narratio_storage_s3_force_path_style | to_json }}
{% endif %}
{% if narratio_storage_s3_access_key_id is not none %}
access_key_id: {{ narratio_storage_s3_access_key_id | to_json }}
{% endif %}
{% if narratio_storage_s3_secret_access_key is not none %}
secret_access_key: {{ narratio_storage_s3_secret_access_key | to_json }}
{% endif %}
{% endif %}
{% endif %}
spool:
root: {{ narratio_spool_root | to_json }}
{% if narratio_spool_delete_audio_after_publish is not none %}
delete_audio_after_publish: {{ narratio_spool_delete_audio_after_publish | to_json }}
{% endif %}
cache:
root: {{ narratio_cache_root | to_json }}
{% if narratio_cache_s3_audio is not none %}
s3_audio: {{ narratio_cache_s3_audio | to_json }}
{% endif %}
{% if narratio_publish_enabled is not none or narratio_publish_upload_run is not none or narratio_publish_outputs is not none %}
publish:
{% if narratio_publish_enabled is not none %}
enabled: {{ narratio_publish_enabled | to_json }}
{% endif %}
{% if narratio_publish_upload_run is not none %}
upload_run: {{ narratio_publish_upload_run | to_json }}
{% endif %}
{% if narratio_publish_outputs is not none %}
outputs: {{ narratio_publish_outputs | to_json }}
{% endif %}
{% endif %}
whisperx:
transcribe_url: {{ narratio_whisperx_transcribe_url | to_json }}
{% if narratio_whisperx_language is not none %}
language: {{ narratio_whisperx_language | to_json }}
{% endif %}
{% if narratio_whisperx_timeout is not none %}
timeout: {{ narratio_whisperx_timeout | to_json }}
{% endif %}
{% if narratio_whisperx_retries is not none %}
retries: {{ narratio_whisperx_retries | to_json }}
{% endif %}
{% if narratio_whisperx_retry_delay is not none %}
retry_delay: {{ narratio_whisperx_retry_delay | to_json }}
{% endif %}
{% if narratio_whisperx_concurrency is not none %}
concurrency: {{ narratio_whisperx_concurrency | to_json }}
{% endif %}
{% if narratio_seriatim_binary is not none or narratio_seriatim_timeout is not none or narratio_seriatim_output_schema is not none or narratio_seriatim_coalesce_gap is not none or narratio_seriatim_report is not none or narratio_seriatim_env_overlap_word_run_gap is not none or narratio_seriatim_env_overlap_word_run_reorder_window is not none or narratio_seriatim_env_backchannel_max_duration is not none or narratio_seriatim_env_filler_max_duration is not none %}
seriatim:
{% if narratio_seriatim_binary is not none %}
binary: {{ narratio_seriatim_binary | to_json }}
{% endif %}
{% if narratio_seriatim_timeout is not none %}
timeout: {{ narratio_seriatim_timeout | to_json }}
{% endif %}
{% if narratio_seriatim_output_schema is not none %}
output_schema: {{ narratio_seriatim_output_schema | to_json }}
{% endif %}
{% if narratio_seriatim_coalesce_gap is not none %}
coalesce_gap: {{ narratio_seriatim_coalesce_gap | to_json }}
{% endif %}
{% if narratio_seriatim_report is not none %}
report: {{ narratio_seriatim_report | to_json }}
{% endif %}
{% if narratio_seriatim_env_overlap_word_run_gap is not none or narratio_seriatim_env_overlap_word_run_reorder_window is not none or narratio_seriatim_env_backchannel_max_duration is not none or narratio_seriatim_env_filler_max_duration is not none %}
env:
{% if narratio_seriatim_env_overlap_word_run_gap is not none %}
overlap_word_run_gap: {{ narratio_seriatim_env_overlap_word_run_gap | to_json }}
{% endif %}
{% if narratio_seriatim_env_overlap_word_run_reorder_window is not none %}
overlap_word_run_reorder_window: {{ narratio_seriatim_env_overlap_word_run_reorder_window | to_json }}
{% endif %}
{% if narratio_seriatim_env_backchannel_max_duration is not none %}
backchannel_max_duration: {{ narratio_seriatim_env_backchannel_max_duration | to_json }}
{% endif %}
{% if narratio_seriatim_env_filler_max_duration is not none %}
filler_max_duration: {{ narratio_seriatim_env_filler_max_duration | to_json }}
{% endif %}
{% endif %}
{% endif %}
{% if narratio_audita_binary is not none or narratio_audita_timeout is not none or narratio_audita_llm_api_key is not none or narratio_audita_modules is not none or narratio_audita_base_url is not none or narratio_audita_model is not none or narratio_audita_total_llm_concurrency is not none or narratio_audita_proposal_llm_concurrency is not none or narratio_audita_validation_model is not none or narratio_audita_validation_llm_concurrency is not none or narratio_audita_transcript_description is not none or narratio_audita_config_path is not none or narratio_audita_output_schema is not none or narratio_audita_work_dir_retention is not none or narratio_audita_report is not none %}
audita:
{% if narratio_audita_binary is not none %}
binary: {{ narratio_audita_binary | to_json }}
{% endif %}
{% if narratio_audita_timeout is not none %}
timeout: {{ narratio_audita_timeout | to_json }}
{% endif %}
{% if narratio_audita_llm_api_key is not none %}
llm_api_key: {{ narratio_audita_llm_api_key | to_json }}
{% endif %}
{% if narratio_audita_modules is not none %}
modules: {{ narratio_audita_modules | to_json }}
{% endif %}
{% if narratio_audita_base_url is not none %}
base_url: {{ narratio_audita_base_url | to_json }}
{% endif %}
{% if narratio_audita_model is not none %}
model: {{ narratio_audita_model | to_json }}
{% endif %}
{% if narratio_audita_total_llm_concurrency is not none %}
total_llm_concurrency: {{ narratio_audita_total_llm_concurrency | to_json }}
{% endif %}
{% if narratio_audita_proposal_llm_concurrency is not none %}
proposal_llm_concurrency: {{ narratio_audita_proposal_llm_concurrency | to_json }}
{% endif %}
{% if narratio_audita_validation_model is not none %}
validation_model: {{ narratio_audita_validation_model | to_json }}
{% endif %}
{% if narratio_audita_validation_llm_concurrency is not none %}
validation_llm_concurrency: {{ narratio_audita_validation_llm_concurrency | to_json }}
{% endif %}
{% if narratio_audita_transcript_description is not none %}
transcript_description: {{ narratio_audita_transcript_description | to_json }}
{% endif %}
{% if narratio_audita_config_path is not none %}
config_path: {{ narratio_audita_config_path | to_json }}
{% endif %}
{% if narratio_audita_output_schema is not none %}
output_schema: {{ narratio_audita_output_schema | to_json }}
{% endif %}
{% if narratio_audita_work_dir_retention is not none %}
work_dir_retention: {{ narratio_audita_work_dir_retention | to_json }}
{% endif %}
{% if narratio_audita_report is not none %}
report: {{ narratio_audita_report | to_json }}
{% endif %}
{% endif %}
{% if narratio_normalize_output_path is not none or narratio_normalize_output_schema is not none or narratio_normalize_report is not none %}
normalize:
{% if narratio_normalize_output_path is not none %}
output_path: {{ narratio_normalize_output_path | to_json }}
{% endif %}
{% if narratio_normalize_output_schema is not none %}
output_schema: {{ narratio_normalize_output_schema | to_json }}
{% endif %}
{% if narratio_normalize_report is not none %}
report: {{ narratio_normalize_report | to_json }}
{% endif %}
{% endif %}
{% if narratio_trim_enabled is not none or narratio_trim_output_path is not none or narratio_trim_bounds_prompt_id is not none or narratio_trim_bounds_profile_id is not none or narratio_trim_bounds_transcript_input_name is not none or narratio_trim_bounds_output_path is not none or narratio_trim_bounds_timeout is not none or narratio_trim_bounds_render_debug is not none or narratio_trim_bounds_render_output_path is not none or narratio_trim_seriatim_report is not none %}
trim:
{% if narratio_trim_enabled is not none %}
enabled: {{ narratio_trim_enabled | to_json }}
{% endif %}
{% if narratio_trim_output_path is not none %}
output_path: {{ narratio_trim_output_path | to_json }}
{% endif %}
{% if narratio_trim_bounds_prompt_id is not none or narratio_trim_bounds_profile_id is not none or narratio_trim_bounds_transcript_input_name is not none or narratio_trim_bounds_output_path is not none or narratio_trim_bounds_timeout is not none or narratio_trim_bounds_render_debug is not none or narratio_trim_bounds_render_output_path is not none %}
bounds:
{% if narratio_trim_bounds_prompt_id is not none %}
prompt_id: {{ narratio_trim_bounds_prompt_id | to_json }}
{% endif %}
{% if narratio_trim_bounds_profile_id is not none %}
profile_id: {{ narratio_trim_bounds_profile_id | to_json }}
{% endif %}
{% if narratio_trim_bounds_transcript_input_name is not none %}
transcript_input_name: {{ narratio_trim_bounds_transcript_input_name | to_json }}
{% endif %}
{% if narratio_trim_bounds_output_path is not none %}
output_path: {{ narratio_trim_bounds_output_path | to_json }}
{% endif %}
{% if narratio_trim_bounds_timeout is not none %}
timeout: {{ narratio_trim_bounds_timeout | to_json }}
{% endif %}
{% if narratio_trim_bounds_render_debug is not none %}
render_debug: {{ narratio_trim_bounds_render_debug | to_json }}
{% endif %}
{% if narratio_trim_bounds_render_output_path is not none %}
render_output_path: {{ narratio_trim_bounds_render_output_path | to_json }}
{% endif %}
{% endif %}
{% if narratio_trim_seriatim_report is not none %}
seriatim:
report: {{ narratio_trim_seriatim_report | to_json }}
{% endif %}
{% endif %}
{% if narratio_render_enabled is not none or narratio_render_format is not none or narratio_render_title is not none or narratio_render_include_timestamps is not none or narratio_render_include_segment_ids is not none or narratio_render_include_metadata is not none %}
render:
{% if narratio_render_enabled is not none %}
enabled: {{ narratio_render_enabled | to_json }}
{% endif %}
{% if narratio_render_format is not none %}
format: {{ narratio_render_format | to_json }}
{% endif %}
{% if narratio_render_title is not none %}
title: {{ narratio_render_title | to_json }}
{% endif %}
{% if narratio_render_include_timestamps is not none %}
include_timestamps: {{ narratio_render_include_timestamps | to_json }}
{% endif %}
{% if narratio_render_include_segment_ids is not none %}
include_segment_ids: {{ narratio_render_include_segment_ids | to_json }}
{% endif %}
{% if narratio_render_include_metadata is not none %}
include_metadata: {{ narratio_render_include_metadata | to_json }}
{% endif %}
{% endif %}
{% if narratio_scriptorium_binary is not none or narratio_scriptorium_config_path is not none or narratio_scriptorium_timeout is not none or narratio_scriptorium_render_debug is not none or narratio_scriptorium_artifacts is not none or narratio_scriptorium_artifact_families is not none %}
scriptorium:
{% if narratio_scriptorium_binary is not none %}
binary: {{ narratio_scriptorium_binary | to_json }}
{% endif %}
{% if narratio_scriptorium_config_path is not none %}
config_path: {{ narratio_scriptorium_config_path | to_json }}
{% endif %}
{% if narratio_scriptorium_timeout is not none %}
timeout: {{ narratio_scriptorium_timeout | to_json }}
{% endif %}
{% if narratio_scriptorium_render_debug is not none %}
render_debug: {{ narratio_scriptorium_render_debug | to_json }}
{% endif %}
{% if narratio_scriptorium_artifacts is not none %}
artifacts: {{ narratio_scriptorium_artifacts | to_json }}
{% endif %}
{% if narratio_scriptorium_artifact_families is not none %}
artifact_families: {{ narratio_scriptorium_artifact_families | to_json }}
{% endif %}
{% endif %}
{% if narratio_notarius_enabled is not none or narratio_notarius_binary is not none or narratio_notarius_config_path is not none or narratio_notarius_pipeline_id is not none or narratio_notarius_timeout is not none or narratio_notarius_working_directory is not none or narratio_notarius_references is not none or narratio_notarius_outputs is not none %}
notarius:
{% if narratio_notarius_enabled is not none %}
enabled: {{ narratio_notarius_enabled | to_json }}
{% endif %}
{% if narratio_notarius_binary is not none %}
binary: {{ narratio_notarius_binary | to_json }}
{% endif %}
{% if narratio_notarius_config_path is not none %}
config_path: {{ narratio_notarius_config_path | to_json }}
{% endif %}
{% if narratio_notarius_pipeline_id is not none %}
pipeline_id: {{ narratio_notarius_pipeline_id | to_json }}
{% endif %}
{% if narratio_notarius_timeout is not none %}
timeout: {{ narratio_notarius_timeout | to_json }}
{% endif %}
{% if narratio_notarius_working_directory is not none %}
working_directory: {{ narratio_notarius_working_directory | to_json }}
{% endif %}
{% if narratio_notarius_references is not none %}
references: {{ narratio_notarius_references | to_json }}
{% endif %}
{% if narratio_notarius_outputs is not none %}
outputs: {{ narratio_notarius_outputs | to_json }}
{% endif %}
{% endif %}
{% if narratio_notification_mode is not none %}
notification:
mode: {{ narratio_notification_mode | to_json }}
{% endif %}

6
tests/converge.yml Normal file
View File

@@ -0,0 +1,6 @@
---
- name: Converge Narratio role
hosts: localhost
gather_facts: true
roles:
- role: ansible-role-narratio

49
tests/integration Executable file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
repository_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
workspace_venv="$repository_root/../.venv"
test_tmp=$(mktemp -d)
chmod 0700 "$test_tmp"
export ANSIBLE_COLLECTIONS_PATH="$test_tmp/collections"
export ANSIBLE_ROLES_PATH="$(dirname "$repository_root")"
inventory="$repository_root/tests/inventory"
converge="$repository_root/tests/converge.yml"
verify_baseline="$repository_root/tests/verify-baseline.yml"
ansible_playbook="$workspace_venv/bin/ansible-playbook"
baseline_args=(-e 'narratio_whisperx_transcribe_url=http://127.0.0.1:8000/transcribe')
cleanup() {
exit_status=$?
trap - EXIT
"$ansible_playbook" -i "$inventory" "${baseline_args[@]}" "$converge" >/dev/null 2>&1 || true
rm -rf -- "$test_tmp"
exit "$exit_status"
}
trap cleanup EXIT
require_clean_recap() {
grep -Eq 'changed=0 .*failed=0' "$1"
}
run_converge() {
output_file=$1
shift
"$ansible_playbook" -i "$inventory" "$@" "$converge" | tee "$output_file"
}
cd "$repository_root"
"$workspace_venv/bin/ansible-galaxy" collection install \
-r collections/requirements.yml \
-p "$ANSIBLE_COLLECTIONS_PATH"
"$ansible_playbook" -i "$inventory" --syntax-check "$converge"
"$workspace_venv/bin/ansible-lint" "$converge" "$verify_baseline"
baseline_log="$test_tmp/baseline.log"
run_converge "$baseline_log" "${baseline_args[@]}"
"$ansible_playbook" -i "$inventory" "$verify_baseline"
baseline_repeat_log="$test_tmp/baseline-repeat.log"
run_converge "$baseline_repeat_log" "${baseline_args[@]}"
require_clean_recap "$baseline_repeat_log"

1
tests/inventory Normal file
View File

@@ -0,0 +1 @@
localhost ansible_connection=local

18
tests/verify-baseline.yml Normal file
View File

@@ -0,0 +1,18 @@
---
- name: Validate the managed Narratio pipeline
hosts: localhost
gather_facts: false
tasks:
- name: Validate the pipeline as the Narratio account
become: true
ansible.builtin.command:
argv:
- /usr/bin/sudo
- -u
- narratio
- /usr/local/bin/narratio
- config
- validate
- --config
- /usr/local/etc/narratio/pipeline.yml
changed_when: false