activity-core/scripts/smoke_issue_core_emission.py
tegwick 1f1b698c35
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 53s
Build and Publish Container Image / build-and-push (push) Successful in 51s
Finish ACTIVITY-WP-0009 issue-core task emission promotion
Close the intent-gap workplan by documenting the weekly-sbom-staleness
promotion path, adding a null-sink and live REST smoke script, and recording
rollback steps for Railiance. Update SCOPE and deployment docs to reflect
ISSUE_SINK_TYPE=rest and the remaining actcore-runtime-secret key patch.
2026-07-07 22:30:46 +02:00

121 lines
No EOL
4.1 KiB
Python
Executable file

#!/usr/bin/env python3
"""Smoke weekly-sbom-staleness task emission through NullSink and IssueCoreRestSink.
Phase 1 always runs in dry-run mode and prints the rendered TaskSpec fields for
operator review. Phase 2 (--live) POSTs the same spec to issue-core and asserts
the returned task reference is not synthetic.
Workstation live smoke requires issue-core's default backend to be `local`. A
remote Gitea default backend will hang on ingest. See
`~/ops-warden/wiki/playbooks/activity-core-issue-sink.md`.
Example:
uv run python scripts/smoke_issue_core_emission.py
ISSUE_CORE_URL=http://127.0.0.1:8765 ISSUE_CORE_API_KEY=... \\
uv run python scripts/smoke_issue_core_emission.py --live
"""
from __future__ import annotations
import argparse
import os
import sys
import uuid
from pathlib import Path
from activity_core.definition_parser import parse_file
from activity_core.issue_sink import IssueCoreRestSink, NullSink
from activity_core.rules.actions import expand_rule_actions
from activity_core.rules.models import TaskRef, TaskSpec
_DEFINITION = (
Path(__file__).resolve().parent.parent / "activity-definitions" / "weekly-sbom-staleness.md"
)
class _CronEvent:
pass
def _stale_repo_spec() -> dict:
rule = parse_file(_DEFINITION).rules[0]
specs = expand_rule_actions(
[rule],
_CronEvent(),
{"repos": {"repos": [{"repo_slug": "activity-core", "sbom_age_days": 45}]}},
)
if len(specs) != 1:
raise RuntimeError(f"expected one stale-repo spec, got {len(specs)}")
return specs[0]
def _task_spec(spec_dict: dict, *, trigger: str) -> TaskSpec:
return TaskSpec(
title=spec_dict["title"],
description=spec_dict["description"],
target_repo=spec_dict["target_repo"],
priority=spec_dict["priority"],
labels=spec_dict["labels"],
due_in_days=spec_dict["due_in_days"],
source_type="rule",
source_id=spec_dict["source_id"],
triggering_event_id=trigger,
activity_definition_id="weekly-sbom-staleness",
)
def run_null_sink(spec_dict: dict) -> TaskRef:
trigger = str(uuid.uuid4())
spec = _task_spec(spec_dict, trigger=trigger)
print("null-sink dry-run spec:")
print(f" title={spec.title!r}")
print(f" target_repo={spec.target_repo!r}")
print(f" source_id={spec.source_id!r}")
print(f" labels={spec.labels!r}")
print(f" triggering_event_id={spec.triggering_event_id!r}")
ref = NullSink().emit(spec)
print(f"null-sink ref: backend={ref.backend} external_id={ref.external_id}")
if ref.backend != "null" or not ref.external_id.startswith("null-"):
raise RuntimeError("NullSink returned an unexpected reference shape")
return ref
def run_live_rest(spec_dict: dict) -> TaskRef:
base_url = os.environ.get("ISSUE_CORE_URL", "http://127.0.0.1:8765").rstrip("/")
api_key = os.environ.get("ISSUE_CORE_API_KEY", "").strip()
if not api_key:
raise RuntimeError("ISSUE_CORE_API_KEY is required for --live")
trigger = str(uuid.uuid4())
spec = _task_spec(spec_dict, trigger=trigger)
ref = IssueCoreRestSink(base_url=base_url, api_key=api_key).emit(spec)
print(f"live rest ref: backend={ref.backend!r} external_id={ref.external_id!r}")
if ref.backend == "null" or ref.external_id.startswith("null-"):
raise RuntimeError("IssueCoreRestSink returned a synthetic reference")
return ref
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--live",
action="store_true",
help="POST the reviewed spec to issue-core after the null-sink dry-run.",
)
args = parser.parse_args()
spec_dict = _stale_repo_spec()
run_null_sink(spec_dict)
if args.live:
run_live_rest(spec_dict)
print("duplicate posture: issue-core server dedupe is deferred; Temporal retries surface in workflow history")
print("smoke passed")
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception as exc:
print(f"smoke failed: {exc}", file=sys.stderr)
raise SystemExit(1) from exc