Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .downstream/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ create a named issue with the conflicted paths.
After a safe candidate merges, automation creates the immutable
`vX.Y.Z-codexrip.1` tag once. The normal Downstream Release workflow builds and
attests the linux/amd64 OCI image. A successful release dispatches Production
Deploy with `runtime=preserve`; that run waits at the GitHub `production`
Environment for the configured approval. Authorized delegated maintenance may
Deploy with `release_tag` and `confirmation=DEPLOY`; that run waits at the GitHub
`production` Environment for the configured approval. Authorized delegated maintenance may
complete that approval; the release workflow does not approve itself.

Sub2API does not store a GitHub token. The admin VersionBadge reads only public
Expand Down
8 changes: 4 additions & 4 deletions .downstream/native-domains.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ they never silently enable features.
## Release and rollback boundary

The host is delivered as one immutable OCI image with image provenance. Normal
deployment uses the existing fixed-image `deploy-preserve` flow. It does not add
deployment uses the fixed-image `deploy` flow. It does not add
backups, canaries, model calls or automatic rollback.

`rollback-preserve` still requires a verified compatible data contract. The
deployer recognizes the old and native prompt/skill directory locations by the
same content hashes; missing or ambiguous trees and changed schema remain errors.
An older tag uses the same deployment form and requires database migrations
compatible with that image. Deployment does not restore dropped schema or data;
the migration boundaries below determine whether an earlier image can start.

Migration 264 has no down path. Images at or before v0.2.11-codexrip.5 need
`sub2api_plugin_state` and `sub2api_plugin_leases`, which it drops, for their
Expand Down
17 changes: 11 additions & 6 deletions .github/scripts/test_upstream_automation_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ def test_safe_merge_tags_once_and_handoff_waits_for_approval(self):
self.assertIn("-codexrip.1", self.promoter)
self.assertIn("endsWith(github.event.workflow_run.head_branch, '-codexrip.1')", self.handoff)
self.assertIn("Manual codexrip releases must remain independent", self.handoff)
self.assertIn("operation=deploy-preserve", self.handoff)
self.assertIn('title="Deploy ${RELEASE_TAG}"', self.handoff)
self.assertIn('-f release_tag="$RELEASE_TAG"', self.handoff)
self.assertIn('-f confirmation=DEPLOY', self.handoff)
self.assertNotIn('operation=', self.handoff)
self.assertIn("environment:\n name: production", self.production)
self.assertIn("runtime=preserve", self.production)
self.assertIn('run-name: Deploy ${{ inputs.release_tag }}', self.production)
self.assertIn('remote_command="deploy ${IMAGE_REF}"', self.production)
self.assertNotIn('runtime=', self.production)
self.assertNotIn("pending_deployments", self.handoff)
self.assertNotIn("pending_deployments", self.promoter)
self.assertIn("--match-head-commit", self.promoter)
self.assertIn("operation=deploy-preserve", self.promoter)
self.assertNotIn('operation=', self.promoter)
self.assertIn("source_sha", self.publisher)
self.assertIn("ref: refs/tags/${{ env.RELEASE_TAG }}", self.publisher)
self.assertIn("ref: ${{ needs.verify.outputs.source_sha }}", self.publisher)
Expand Down Expand Up @@ -246,14 +251,14 @@ def run_command(*args):
return merged
return ""

for existing in (False, True):
for existing in (None, f"Deploy {self.tag}", f"Deploy {self.tag} (preserve)"):
def read_api(path, *args):
if path == "pulls/152":
return pr
if path == "git/refs":
return {}
if path == "actions/workflows/production-deploy.yml/runs?per_page=100":
runs = [{"display_title": f"Deploy {self.tag} (preserve)", "status": "in_progress",
runs = [{"display_title": existing, "status": "in_progress",
"conclusion": None, "html_url": "https://github.com/example/run/18"}] if existing else []
return {"workflow_runs": runs}
self.fail(f"unexpected API call: {path}")
Expand All @@ -274,7 +279,7 @@ def read_api(path, *args):
dispatches = [c for c in command.call_args_list if c.args[:3] == ("gh", "workflow", "run")]
self.assertEqual(dispatches, [] if existing else [call("gh", "workflow", "run",
"production-deploy.yml", "--repo", "HTExplicit/sub2api", "--ref", "main", "-f",
"operation=deploy-preserve", "-f", f"release_tag={self.tag}", "-f", "confirmation=DEPLOY")])
f"release_tag={self.tag}", "-f", "confirmation=DEPLOY")])


if __name__ == "__main__":
Expand Down
7 changes: 4 additions & 3 deletions .github/scripts/upstream_promote.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,16 +183,17 @@ def promote(number: int) -> None:
else:
api("git/refs", "-X", "POST", "-f", f"ref=refs/tags/{tag}", "-f", f"sha={merged}")
ensure_release(tag, merged)
title = f"Deploy {tag} (preserve)"
# Reuse deployments already dispatched before the form changed.
titles = {f"Deploy {tag}", f"Deploy {tag} (preserve)"}
deployments = api("actions/workflows/production-deploy.yml/runs?per_page=100")["workflow_runs"]
previous = [r for r in deployments if r.get("display_title") == title]
previous = [r for r in deployments if r.get("display_title") in titles]
if previous:
if previous[0]["status"] == "completed" and previous[0]["conclusion"] != "success":
raise RuntimeError("previous production run failed; runtime audit required before retry")
print("Existing protected deployment: " + previous[0]["html_url"])
return
command("gh", "workflow", "run", "production-deploy.yml", "--repo", REPOSITORY,
"--ref", "main", "-f", "operation=deploy-preserve", "-f", f"release_tag={tag}", "-f", "confirmation=DEPLOY")
"--ref", "main", "-f", f"release_tag={tag}", "-f", "confirmation=DEPLOY")
print(f"Protected deployment dispatched: {tag}; production approval remains required.")


Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/downstream-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,6 @@ jobs:
org.opencontainers.image.revision=${{ needs.verify.outputs.source_sha }}
org.opencontainers.image.created=${{ env.BUILD_DATE }}
org.opencontainers.image.licenses=LGPL-3.0
io.github.htexplicit.upstream-version=${{ env.UPSTREAM_VERSION }}
io.github.htexplicit.patch=codexrip
io.github.htexplicit.cindy-platform-v1=true
build-args: |
VERSION=${{ env.VERSION }}
COMMIT=${{ needs.verify.outputs.source_sha }}
Expand Down
Loading
Loading