Compare commits

3 Commits
v0.1.0 ... main

5 changed files with 142 additions and 11 deletions

View File

@@ -8,7 +8,7 @@ root-owned application state.
## Variables ## Variables
```yaml ```yaml
scriptorium_version: v0.13.0 scriptorium_version: v0.15.0
scriptorium_repository_url: https://gitea.maximumdirect.net/eric/scriptorium.git scriptorium_repository_url: https://gitea.maximumdirect.net/eric/scriptorium.git
scriptorium_source_dir: /usr/local/src/scriptorium scriptorium_source_dir: /usr/local/src/scriptorium
scriptorium_binary_path: /usr/local/bin/scriptorium scriptorium_binary_path: /usr/local/bin/scriptorium
@@ -20,6 +20,7 @@ scriptorium_config_owner: root
scriptorium_config_group: root scriptorium_config_group: root
scriptorium_config_directory_mode: "0755" scriptorium_config_directory_mode: "0755"
scriptorium_config_file_mode: "0644" scriptorium_config_file_mode: "0644"
scriptorium_configkit_imports: null
scriptorium_prompt_dir: null scriptorium_prompt_dir: null
scriptorium_profile_dir: null scriptorium_profile_dir: null
scriptorium_schema_dir: null scriptorium_schema_dir: null
@@ -30,6 +31,7 @@ scriptorium_server_max_artifact_bytes: null
scriptorium_server_max_response_bytes: null scriptorium_server_max_response_bytes: null
scriptorium_defaults_render_format: null scriptorium_defaults_render_format: null
scriptorium_backends: null scriptorium_backends: null
scriptorium_transient_retries: null
``` ```
When `scriptorium_manage_build_dependencies` is `false`, Git and the executable When `scriptorium_manage_build_dependencies` is `false`, Git and the executable
@@ -62,6 +64,57 @@ Backend entries may name a credential environment variable through
does not install prompt, profile, or schema content and does not manage a does not install prompt, profile, or schema content and does not manage a
service. service.
`scriptorium_configkit_imports` accepts a list of native ConfigKit import
mappings. Paths and globs are interpreted by Scriptorium relative to the
configuration root; the caller supplies and owns the referenced files. For
example, an inventory may select a caller-managed fragment:
```yaml
scriptorium_configkit_imports:
- path: fragments/server.yml
scriptorium_transient_retries: 0
```
Both settings are omitted when `null`. Setting `scriptorium_transient_retries`
to `0` disables additional transient attempts; omission leaves the retry
budget with Scriptorium and PromptKit. Leave these newer fields unset when
selecting an older Scriptorium version that does not support them.
## Upgrading to v0.15.0
The intervening v0.14.0 release adopted ConfigKit. It rejects empty
configuration files, unknown fields, multiple YAML documents, and YAML
anchors or aliases. Use `{}` when no application settings are selected;
the role already renders that document. See the tagged
[configuration reference](https://gitea.maximumdirect.net/eric/scriptorium/src/tag/v0.15.0/docs/config.md)
and [migration notes](https://gitea.maximumdirect.net/eric/scriptorium/src/tag/v0.14.0/docs/releases/v0.14.0.md)
for the complete acceptance and precedence rules.
ConfigKit imports are resolved relative to the selected configuration file's
canonical directory. ConfigKit files under `overlays/` are selected by the
application's `--config-overlay` flag; they are separate from PromptKit LLM
profiles selected through profile settings and `profile_dir`. Application
path *values* such as `prompt_dir` and `profile_dir` remain relative to the
Scriptorium process working directory, even when declared in an import or
overlay. This role only supplies import entries; callers own referenced files
and runtime overlay selection.
PromptKit now makes up to two additional attempts by default for eligible
transient provider responses. `scriptorium_transient_retries` can change that
application budget, while repair generations have separate budgets. Review
provider call count and latency when upgrading; see the tagged
[retry guidance](https://gitea.maximumdirect.net/eric/scriptorium/src/tag/v0.15.0/docs/config.md#credentials-and-outbound-behavior).
The legacy `deepseek-3-2`, `deepseek-4-flash`, and `deepseek-4-pro` catalog
profile IDs were removed without aliases; select a maintained profile when
updating callers.
Scriptorium v0.15.0's application source and binaries are distributed under
GPL-3.0-or-later; its tagged [copyright](https://gitea.maximumdirect.net/eric/scriptorium/src/tag/v0.15.0/COPYRIGHT),
[license](https://gitea.maximumdirect.net/eric/scriptorium/src/tag/v0.15.0/LICENSE),
and [attribution notices](https://gitea.maximumdirect.net/eric/scriptorium/src/tag/v0.15.0/docs/oss/attribution.md)
describe that grant and separately attributed material. The Ansible role's
own [CC0 license](LICENSE) is unchanged.
## Example ## Example
```yaml ```yaml
@@ -73,14 +126,17 @@ service.
## Validation ## Validation
The repository-local integration suite installs its pinned collection and runs The repository-local integration suite installs its pinned collection and runs
syntax checking and production-profile lint. It applies the default role, uses syntax checking and production-profile lint. It applies the default role,
an offline native profile inspection with the managed configuration, and then validates the managed application configuration with the native CLI, performs
requires clean repeated convergence. It also combines missing-binary recovery offline profile inspection, and requires clean repeated convergence. It also
with a caller-supplied Go executable while dependency management is disabled, combines missing-binary recovery with a caller-supplied Go executable while
and covers one representative configuration with null omission, intentional dependency management is disabled. Representative configuration coverage checks
zero and false values, backend pass-through, and offline native inspection. It null omission, intentional zero and false values, backend pass-through, native
mutates only the managed source, binary, configuration, and a private temporary validation, and offline profile inspection. A caller-owned fragment exercises
directory, then restores the default deployment during cleanup. relative imports and explicit retry zero through native `config show`. The
suite restores the default configuration during cleanup. These offline checks
do not establish provider connectivity, service operation, or suite-level
compatibility.
```sh ```sh
tests/integration tests/integration

View File

@@ -1,5 +1,5 @@
--- ---
scriptorium_version: v0.13.0 scriptorium_version: v0.15.0
scriptorium_repository_url: https://gitea.maximumdirect.net/eric/scriptorium.git scriptorium_repository_url: https://gitea.maximumdirect.net/eric/scriptorium.git
scriptorium_source_dir: /usr/local/src/scriptorium scriptorium_source_dir: /usr/local/src/scriptorium
scriptorium_binary_path: /usr/local/bin/scriptorium scriptorium_binary_path: /usr/local/bin/scriptorium
@@ -11,6 +11,7 @@ scriptorium_config_owner: root
scriptorium_config_group: root scriptorium_config_group: root
scriptorium_config_directory_mode: "0755" scriptorium_config_directory_mode: "0755"
scriptorium_config_file_mode: "0644" scriptorium_config_file_mode: "0644"
scriptorium_configkit_imports: null
scriptorium_prompt_dir: null scriptorium_prompt_dir: null
scriptorium_profile_dir: null scriptorium_profile_dir: null
scriptorium_schema_dir: null scriptorium_schema_dir: null
@@ -21,3 +22,4 @@ scriptorium_server_max_artifact_bytes: null
scriptorium_server_max_response_bytes: null scriptorium_server_max_response_bytes: null
scriptorium_defaults_render_format: null scriptorium_defaults_render_format: null
scriptorium_backends: null scriptorium_backends: null
scriptorium_transient_retries: null

View File

@@ -39,6 +39,10 @@ argument_specs:
scriptorium_config_file_mode: scriptorium_config_file_mode:
type: str type: str
required: true required: true
scriptorium_configkit_imports:
type: list
elements: dict
required: false
scriptorium_prompt_dir: scriptorium_prompt_dir:
type: path type: path
required: false required: false
@@ -69,3 +73,6 @@ argument_specs:
scriptorium_backends: scriptorium_backends:
type: dict type: dict
required: false required: false
scriptorium_transient_retries:
type: int
required: false

View File

@@ -1,6 +1,23 @@
{% if scriptorium_prompt_dir is none and scriptorium_profile_dir is none and scriptorium_schema_dir is none and scriptorium_server_addr is none and scriptorium_server_artifact_root is none and scriptorium_server_max_request_bytes is none and scriptorium_server_max_artifact_bytes is none and scriptorium_server_max_response_bytes is none and scriptorium_defaults_render_format is none and scriptorium_backends is none %} {% if (
scriptorium_configkit_imports is none
and scriptorium_prompt_dir is none
and scriptorium_profile_dir is none
and scriptorium_schema_dir is none
and scriptorium_server_addr is none
and scriptorium_server_artifact_root is none
and scriptorium_server_max_request_bytes is none
and scriptorium_server_max_artifact_bytes is none
and scriptorium_server_max_response_bytes is none
and scriptorium_defaults_render_format is none
and scriptorium_backends is none
and scriptorium_transient_retries is none
) %}
{} {}
{% else %} {% else %}
{% if scriptorium_configkit_imports is not none %}
configkit:
imports: {{ scriptorium_configkit_imports | to_json }}
{% endif %}
{% if scriptorium_prompt_dir is not none %} {% if scriptorium_prompt_dir is not none %}
prompt_dir: {{ scriptorium_prompt_dir | to_json }} prompt_dir: {{ scriptorium_prompt_dir | to_json }}
{% endif %} {% endif %}
@@ -35,4 +52,7 @@ defaults:
{% if scriptorium_backends is not none %} {% if scriptorium_backends is not none %}
backends: {{ scriptorium_backends | to_json }} backends: {{ scriptorium_backends | to_json }}
{% endif %} {% endif %}
{% if scriptorium_transient_retries is not none %}
transient_retries: {{ scriptorium_transient_retries | to_json }}
{% endif %}
{% endif %} {% endif %}

View File

@@ -16,6 +16,8 @@ ansible_playbook="$workspace_venv/bin/ansible-playbook"
binary_path=/usr/local/bin/scriptorium binary_path=/usr/local/bin/scriptorium
config_path=/usr/local/etc/scriptorium/config.yml config_path=/usr/local/etc/scriptorium/config.yml
profile_dir="$test_tmp/profiles" profile_dir="$test_tmp/profiles"
import_root="$test_tmp/config-root"
import_config_path="$import_root/config.yml"
cleanup() { cleanup() {
exit_status=$? exit_status=$?
@@ -55,6 +57,15 @@ cd "$repository_root"
baseline_log="$test_tmp/baseline.log" baseline_log="$test_tmp/baseline.log"
run_converge "$baseline_log" run_converge "$baseline_log"
"$binary_path" config validate --config "$config_path"
"$workspace_venv/bin/python" -c '
import sys
import yaml
with open(sys.argv[1], encoding="utf-8") as config_file:
config = yaml.safe_load(config_file)
assert "configkit" not in config
assert "transient_retries" not in config
' "$config_path"
"$binary_path" inspect profile --config "$config_path" \ "$binary_path" inspect profile --config "$config_path" \
--profile-dir "$profile_dir" --profile endpoint-profile --profile-dir "$profile_dir" --profile endpoint-profile
baseline_repeat_log="$test_tmp/baseline-repeat.log" baseline_repeat_log="$test_tmp/baseline-repeat.log"
@@ -90,9 +101,44 @@ representative_args=(
) )
representative_log="$test_tmp/representative.log" representative_log="$test_tmp/representative.log"
run_converge "$representative_log" "${representative_args[@]}" run_converge "$representative_log" "${representative_args[@]}"
"$binary_path" config validate --config "$config_path"
"$binary_path" inspect profile --config "$config_path" --profile backend-profile "$binary_path" inspect profile --config "$config_path" --profile backend-profile
"$ansible_playbook" -i "$inventory" \ "$ansible_playbook" -i "$inventory" \
-e "scriptorium_test_profile_dir=$profile_dir" "$verify_config" -e "scriptorium_test_profile_dir=$profile_dir" "$verify_config"
representative_repeat_log="$test_tmp/representative-repeat.log" representative_repeat_log="$test_tmp/representative-repeat.log"
run_converge "$representative_repeat_log" "${representative_args[@]}" run_converge "$representative_repeat_log" "${representative_args[@]}"
require_clean_recap "$representative_repeat_log" require_clean_recap "$representative_repeat_log"
mkdir -p "$import_root/fragments"
printf '%s\n' \
'server:' \
' addr: 127.0.0.1:18081' > "$import_root/fragments/server.yml"
import_args=(
-e "scriptorium_config_path=$import_config_path"
-e "scriptorium_config_owner=$(id -un)"
-e "scriptorium_config_group=$(id -gn)"
-e '{"scriptorium_configkit_imports": [{"path": "fragments/server.yml"}], "scriptorium_transient_retries": 0}'
)
import_log="$test_tmp/import.log"
run_converge "$import_log" "${import_args[@]}"
"$binary_path" config validate --config "$import_config_path"
"$workspace_venv/bin/python" -c '
import sys
import yaml
with open(sys.argv[1], encoding="utf-8") as config_file:
config = yaml.safe_load(config_file)
assert config["configkit"]["imports"] == [{"path": "fragments/server.yml"}]
assert config["transient_retries"] == 0
assert "server" not in config
' "$import_config_path"
"$binary_path" config show --config "$import_config_path" | \
"$workspace_venv/bin/python" -c '
import sys
import yaml
config = yaml.safe_load(sys.stdin)
assert config["server"]["addr"] == "127.0.0.1:18081"
assert config["transient_retries"] == 0
'
import_repeat_log="$test_tmp/import-repeat.log"
run_converge "$import_repeat_log" "${import_args[@]}"
require_clean_recap "$import_repeat_log"