Skip to content

Commit 2fe26bb

Browse files
blarghmateyclaude
andcommitted
feat(preview-gated): make the post-deploy record issue optional
Under topology="preview-gated" the "... deployed." issue posted after every apply is pure audit trail -- nothing reads it back, since promotion to the next stage is `passed` on the deploy job itself and the gate issue already authorised the deploy. Add record_deployments=True so callers whose gate issue is doing the real work can turn the noise off. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019mvaFgGzBYBL2aatAZ2mJG
1 parent da1b183 commit 2fe26bb

2 files changed

Lines changed: 87 additions & 27 deletions

File tree

pipeline_lib/src/ol_concourse/lib/jobs/infrastructure.py

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def pulumi_jobs_chain( # noqa: PLR0913, PLR0912, PLR0915
182182
max_carried_changes: int | str | None = None,
183183
topology: Literal["deploy-chained", "preview-gated"] = "deploy-chained",
184184
auto_deploy_stages: list[str] | None = None,
185+
record_deployments: bool = True,
185186
) -> PipelineFragment:
186187
"""Create a chained sequence of jobs for running Pulumi tasks.
187188
@@ -198,6 +199,13 @@ def pulumi_jobs_chain( # noqa: PLR0913, PLR0912, PLR0915
198199
used as inputs or triggers for the jobs in the chain.
199200
:param github_issue_assignees: A list of GitHub usernames that should be assigned
200201
:param github_issue_labels: A list of GitHub labels that should be applied
202+
:param record_deployments: Only used with ``topology="preview-gated"``. When
203+
``True`` (default), every deploy posts a ``... deployed.`` GitHub issue
204+
as an audit record. Unlike in ``deploy-chained``, nothing reads that
205+
issue back -- promotion is `passed` on the deploy job itself, and the
206+
gate issue is what actually authorised the deploy -- so it is a record,
207+
not a gate. Set ``False`` to drop it once the gate issue is doing the
208+
useful work and the record is just noise.
201209
:param env_vars_from_files: The list of environment variables that should be set
202210
during the build and the files to load for populating the values (e.g. the
203211
`version` file from a GitHub resource)
@@ -233,6 +241,7 @@ def pulumi_jobs_chain( # noqa: PLR0913, PLR0912, PLR0915
233241
project_source_path=project_source_path,
234242
github_issue_repository=github_issue_repository,
235243
auto_deploy_stages=auto_deploy_stages,
244+
record_deployments=record_deployments,
236245
github_issue_assignees=github_issue_assignees,
237246
github_issue_labels=github_issue_labels,
238247
dependencies=dependencies,
@@ -249,6 +258,9 @@ def pulumi_jobs_chain( # noqa: PLR0913, PLR0912, PLR0915
249258
if auto_deploy_stages is not None:
250259
msg = "auto_deploy_stages only applies to topology='preview-gated'"
251260
raise ValueError(msg)
261+
if not record_deployments:
262+
msg = "record_deployments=False only applies to topology='preview-gated'"
263+
raise ValueError(msg)
252264

253265
chain_fragment = PipelineFragment(resource_types=[github_issues_resource()])
254266
previous_job = None
@@ -663,6 +675,7 @@ def _preview_gated_chain( # noqa: PLR0913, PLR0915
663675
project_source_path: Path,
664676
github_issue_repository: str,
665677
auto_deploy_stages: list[str] | None = None,
678+
record_deployments: bool = True,
666679
github_issue_assignees: list[str] | None = None,
667680
github_issue_labels: list[str] | None = None,
668681
dependencies: list[GetStep] | None = None,
@@ -719,6 +732,12 @@ def _preview_gated_chain( # noqa: PLR0913, PLR0915
719732
the fast feedback loop it exists to provide. Matched case-insensitively
720733
against both the full stack name and its trailing dotted segment, so both
721734
``CI`` and ``mitx.CI`` work.
735+
736+
*record_deployments* controls the ``... deployed.`` issue posted after
737+
every stage's `pulumi up`. It is an audit record only: nothing here reads
738+
it back, since promotion to the next stage is `passed` on the deploy job
739+
itself and the gate issue already authorised the deploy. Set ``False`` to
740+
drop it.
722741
"""
723742
exempt = {s.lower() for s in (auto_deploy_stages or [])}
724743

@@ -817,15 +836,32 @@ def _alerts(job: Job, stack: str, kind: str) -> None:
817836
serial_group = _stack_serial_group(project_name, stack_name)
818837
passed_from = [previous_deploy.name] if previous_deploy else None
819838

820-
record_issue = github_issues(
821-
auth_method="token",
822-
name=Identifier(f"gh-{project_name.lower()}-{slug}-deployed"),
823-
repository=github_issue_repository,
824-
issue_title_template=f"[bot] Pulumi {project_name} {stack_name} deployed.",
825-
issue_prefix=f"[bot] Pulumi {project_name} {stack_name} deployed.",
826-
issue_state="open",
827-
)
828-
chain.resources.append(record_issue)
839+
record_issue = None
840+
if record_deployments:
841+
deployed_title = f"[bot] Pulumi {project_name} {stack_name} deployed."
842+
record_issue = github_issues(
843+
auth_method="token",
844+
name=Identifier(f"gh-{project_name.lower()}-{slug}-deployed"),
845+
repository=github_issue_repository,
846+
issue_title_template=deployed_title,
847+
issue_prefix=deployed_title,
848+
issue_state="open",
849+
)
850+
chain.resources.append(record_issue)
851+
852+
def _record_put(
853+
stack: str = stack_name, issue: Resource | None = record_issue
854+
) -> PutStep | None:
855+
if not issue:
856+
return None
857+
return PutStep(
858+
put=issue.name,
859+
params={
860+
"assignees": github_issue_assignees or [],
861+
"labels": _record_labels(stack),
862+
"body_files": [f"{pulumi_resource.name}/{DEPLOY_SUMMARY_FILENAME}"],
863+
},
864+
)
829865

830866
if is_exempt(stack_name):
831867
deploy = Job(
@@ -849,16 +885,7 @@ def _alerts(job: Job, stack: str, kind: str) -> None:
849885
),
850886
*post_steps,
851887
],
852-
on_success=PutStep(
853-
put=record_issue.name,
854-
params={
855-
"assignees": github_issue_assignees or [],
856-
"labels": _record_labels(stack_name),
857-
"body_files": [
858-
f"{pulumi_resource.name}/{DEPLOY_SUMMARY_FILENAME}"
859-
],
860-
},
861-
),
888+
on_success=_record_put(),
862889
)
863890
_alerts(deploy, stack_name, "deploy")
864891
chain.jobs.append(deploy)
@@ -973,14 +1000,7 @@ def _alerts(job: Job, stack: str, kind: str) -> None:
9731000
),
9741001
*post_steps,
9751002
],
976-
on_success=PutStep(
977-
put=record_issue.name,
978-
params={
979-
"assignees": github_issue_assignees or [],
980-
"labels": _record_labels(stack_name),
981-
"body_files": [f"{pulumi_resource.name}/{DEPLOY_SUMMARY_FILENAME}"],
982-
},
983-
),
1003+
on_success=_record_put(),
9841004
)
9851005

9861006
_alerts(deploy, stack_name, "deploy")

pipeline_lib/tests/test_infrastructure.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,46 @@ def test_every_index_keyed_parameter_is_now_honoured(self):
593593
)
594594

595595

596+
class TestRecordDeployments:
597+
"""The "deployed" issue is an audit record, not a gate -- it can be dropped."""
598+
599+
def test_record_deployments_false_rejected_on_default_topology(self):
600+
with pytest.raises(ValueError, match="only applies to"):
601+
pulumi_jobs_chain(
602+
_make_pulumi_code(),
603+
stack_names=["CI"],
604+
project_name="p",
605+
project_source_path=Path("x"),
606+
github_issue_repository="org/repo",
607+
record_deployments=False,
608+
)
609+
610+
def test_default_still_posts_the_record_on_both_stage_kinds(self):
611+
fragment = _gated_chain()
612+
exempt_deploy = _job(fragment, "deploy-ol-substructure-keycloak-ci")
613+
gated_deploy = _job(fragment, "deploy-ol-substructure-keycloak-qa")
614+
assert "deployed" in str(exempt_deploy.on_success.put)
615+
assert "deployed" in str(gated_deploy.on_success.put)
616+
617+
def test_record_deployments_false_drops_the_on_success_put(self):
618+
fragment = _gated_chain(record_deployments=False)
619+
exempt_deploy = _job(fragment, "deploy-ol-substructure-keycloak-ci")
620+
gated_deploy = _job(fragment, "deploy-ol-substructure-keycloak-qa")
621+
assert exempt_deploy.on_success is None
622+
assert gated_deploy.on_success is None
623+
624+
def test_record_deployments_false_drops_the_issue_resources(self):
625+
fragment = _gated_chain(record_deployments=False)
626+
assert not any("deployed" in str(r.name) for r in fragment.resources)
627+
628+
def test_record_deployments_false_keeps_the_gate(self):
629+
"""Dropping the record must not touch the actual promotion gate."""
630+
fragment = _gated_chain(record_deployments=False)
631+
assert _job(fragment, "preview-ol-substructure-keycloak-qa") is not None
632+
assert any("gate-post" in str(r.name) for r in fragment.resources)
633+
assert any("gate-trigger" in str(r.name) for r in fragment.resources)
634+
635+
596636
class TestPreviewGatedStageInputs:
597637
"""Stage inputs are artifacts the Pulumi run consumes, not just triggers.
598638

0 commit comments

Comments
 (0)