3939
4040
4141def output (name : str , value : Any ) -> None :
42+ """Write an Actions output, falling back to readable local command output."""
4243 text = str (value ).replace ("\n " , " " ) if value is not None else ""
4344 if destination := os .environ .get ("GITHUB_OUTPUT" ):
4445 with Path (destination ).open ("a" ) as stream :
@@ -48,16 +49,19 @@ def output(name: str, value: Any) -> None:
4849
4950
5051def required (env : Mapping [str , str ], name : str ) -> str :
52+ """Read a required Actions value without silently accepting an empty secret."""
5153 if not (value := env .get (name )):
5254 raise ValueError (f"Required Actions value is missing: { name } " )
5355 return value
5456
5557
5658def read (path : str | Path , kind : type [T ]) -> T :
59+ """Decode JSON through Temporal's converter so CLI and Workflow types agree."""
5760 return cast (T , value_to_type (kind , json .loads (Path (path ).read_text ())))
5861
5962
6063async def connect (env : Mapping [str , str ]) -> Client :
64+ """Connect to the configured Temporal namespace using its single API credential."""
6165 return await Client .connect (
6266 required (env , "TEMPORAL_ADDRESS" ),
6367 namespace = required (env , "TEMPORAL_NAMESPACE" ),
@@ -67,6 +71,7 @@ async def connect(env: Mapping[str, str]) -> Client:
6771
6872
6973def policy_for_settings (settings : Path ) -> str :
74+ """Recognize the checked-out Gradle project set as a fixed Maven policy."""
7075 projects = [
7176 match .group (1 )
7277 for line in settings .read_text ().splitlines ()
@@ -76,10 +81,17 @@ def policy_for_settings(settings: Path) -> str:
7681
7782
7883def git (* args : str , cwd : Path | None = None ) -> str :
84+ """Run a read-only Git command and return its whitespace-trimmed output."""
7985 return subprocess .check_output (["git" , * args ], cwd = cwd , text = True ).strip ()
8086
8187
8288def candidate_from_push (env : Mapping [str , str ]) -> CandidateIdentity :
89+ """Authorize a candidate from one newly added release-note file.
90+
91+ The push workflow is the approval boundary. This function binds that approval to
92+ exact source and automation commits, verifies ancestry, and refuses ambiguous or
93+ non-regular release-note changes before any Temporal execution is started.
94+ """
8395 if env .get ("GITHUB_REPOSITORY" ) != REPOSITORY :
8496 raise ValueError ("This automation only releases temporalio/sdk-java." )
8597 commit , automation = required (env , "RELEASE_COMMIT" ), required (env , "RELEASE_AUTOMATION_REF" )
@@ -121,15 +133,23 @@ def candidate_from_push(env: Mapping[str, str]) -> CandidateIdentity:
121133
122134
123135async def workflows (client : Client ) -> list [WorkflowExecution ]:
136+ """List only running sdk-java ReleaseWorkflow executions for discovery."""
124137 query = "ExecutionStatus = 'Running' AND WorkflowType = 'ReleaseWorkflow'"
125138 return [item async for item in client .list_workflows (query )]
126139
127140
128141async def memo (execution : WorkflowExecution , key : str , kind : type [T ]) -> T | None :
142+ """Decode one typed memo field without requiring a live Workflow Worker."""
129143 return await execution .memo_value (key , None , type_hint = kind )
130144
131145
132146async def start_candidate (client : Client , candidate : CandidateIdentity ) -> None :
147+ """Start or reuse the one Workflow execution for an immutable candidate.
148+
149+ Before starting, all running executions are checked for a conflicting use of the
150+ same release tag. Temporal's duplicate policies then make repeated delivery of the
151+ same authorized push idempotent without allowing identity replacement.
152+ """
133153 candidate .validate ()
134154 for execution in await workflows (client ):
135155 existing = await memo (execution , "CandidateIdentity" , CandidateIdentity )
@@ -153,6 +173,7 @@ async def update(
153173 name : str ,
154174 * args : Any ,
155175) -> None :
176+ """Run a short-lived Worker while delivering an update to an exact execution."""
156177 if not run_id :
157178 raise ValueError ("Release Workflow execution is invalid." )
158179 async with Worker (client , task_queue = workflow_queue (workflow_id ), workflows = [ReleaseWorkflow ]):
@@ -162,13 +183,20 @@ async def update(
162183
163184
164185def workflow_queue (workflow_id : str ) -> str :
186+ """Recover and validate the candidate-specific queue encoded in a Workflow ID."""
165187 prefix = "sdk-java-release-candidate/"
166188 if not workflow_id .startswith (prefix ):
167189 raise ValueError ("Release Workflow ID is invalid." )
168190 return candidate_queue_from_digest (workflow_id .removeprefix (prefix ))
169191
170192
171193def verify_candidate_origin (candidate : CandidateIdentity , run : Mapping [str , Any ]) -> None :
194+ """Reconfirm that GitHub authorized the candidate from the expected push workflow.
195+
196+ Discovery repeats this check on every scheduled pass so a forged Temporal memo
197+ cannot cause privileged work. Repository, workflow path, source SHA, branch policy,
198+ event type, and numeric run identity must all match the frozen candidate.
199+ """
172200 repository = run .get ("head_repository" )
173201 if (
174202 run .get ("id" ) != candidate .githubRunId
@@ -183,6 +211,7 @@ def verify_candidate_origin(candidate: CandidateIdentity, run: Mapping[str, Any]
183211
184212
185213def github_run (run_id : int ) -> Mapping [str , Any ]:
214+ """Fetch the GitHub Actions run used as candidate authorization evidence."""
186215 return cast (
187216 Mapping [str , Any ],
188217 json .loads (
@@ -196,6 +225,12 @@ def github_run(run_id: int) -> Mapping[str, Any]:
196225async def release_jobs (
197226 execution : WorkflowExecution , automation : str
198227) -> dict [str , list [dict [str , Any ]]]:
228+ """Translate one durable release snapshot into minimal Actions matrix entries.
229+
230+ Native entries contain no credentials and are emitted only for missing platforms.
231+ A publication entry is emitted only after the immutable release identity exists;
232+ its queue is derived from the current durable Maven generation.
233+ """
199234 candidate = await memo (execution , "CandidateIdentity" , CandidateIdentity )
200235 if (
201236 candidate is None
@@ -244,6 +279,7 @@ async def release_jobs(
244279
245280
246281async def discover (client : Client , env : Mapping [str , str ]) -> None :
282+ """Emit build and publication matrices for all valid running releases."""
247283 jobs : dict [str , list [dict [str , Any ]]] = {"build" : [], "publication" : []}
248284 automation = required (env , "RELEASE_AUTOMATION_REF" )
249285 for execution in await workflows (client ):
@@ -262,6 +298,12 @@ async def discover(client: Client, env: Mapping[str, str]) -> None:
262298
263299
264300async def run_worker (client : Client , queue : str , env : Mapping [str , str ]) -> None :
301+ """Poll one release Workflow queue and one exact publication generation queue.
302+
303+ Hosting both Workers in the publication job removes a separate Actions worker
304+ matrix. Activities still exist only on the privileged generation-specific queue,
305+ while deterministic Workflow code polls the candidate-specific queue.
306+ """
265307 if not queue .startswith ("sdk-java-release-" ) or "-publication-g" not in queue :
266308 raise ValueError ("Refusing to poll a non-publication Task Queue." )
267309 workflow_id = required (env , "EXPECTED_WORKFLOW_ID" )
@@ -286,6 +328,7 @@ async def run_worker(client: Client, queue: str, env: Mapping[str, str]) -> None
286328
287329
288330async def async_main (argv : list [str ], env : Mapping [str , str ]) -> None :
331+ """Dispatch the small command surface used by the release workflows."""
289332 client = await connect (env )
290333 match argv :
291334 case ["start" ]:
@@ -316,6 +359,7 @@ async def async_main(argv: list[str], env: Mapping[str, str]) -> None:
316359
317360
318361def main () -> None :
362+ """Validate the trusted checkout layout and run the asynchronous CLI."""
319363 if not (ROOT / ".github/scripts/temporal-release/reconcile-publication.sh" ).is_file ():
320364 raise RuntimeError ("The trusted repository root has an unexpected layout." )
321365 asyncio .run (async_main (sys .argv [1 :], os .environ ))
0 commit comments