security: fresh-authorize CI job reruns (Closes #1481)
CI / lint (pull_request) Successful in 4m0s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 7m40s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-27 20:08:15 +00:00
1 parent 1484c579f9
commit f23be99ab4
4 files changed
+242

No files matched your search

+79
View File
@@ -606,6 +606,7 @@ StepUpAction = Literal[
"merge_pull",
"delete_source_branch",
"prepare_release_rollback",
"retry_ci_job",
"submit_pull_review",
"close_issue",
"delete_comment",
@@ -7863,13 +7864,38 @@ async def pull_action_failure(
)
async def retry_pull_action_job(
retry: PullReadyRequest,
request: Request,
owner: str,
repo: str,
number: int = PathParam(gt=0),
run_id: int = PathParam(gt=0),
job_index: int = PathParam(ge=0),
step_up_grant: str | None = Header(
default=None, alias="X-Step-Up-Grant", max_length=128
),
) -> JSONResponse:
repository = f"{owner}/{repo}"
target = (
f"{repository}#{number}@{retry.expected_head_sha}:"
f"actions/{run_id}/jobs/{job_index}"
)
await _require_step_up(
request, step_up_grant, action="retry_ci_job", target=target
)
journal = _security_event_store()
try:
operation_id = await asyncio.to_thread(
journal.reserve,
"ci_job_retried",
principal_id=await _security_principal_id(request),
target=target,
)
except SecurityEventStoreError:
return JSONResponse(
{"error": "Security activity is temporarily unavailable. No job was retried."},
status_code=503,
headers={"Cache-Control": "no-store", "Retry-After": "1"},
)
async def retry_job():
if not _has_pull_workspace_access(
@@ -7885,14 +7911,26 @@ async def retry_pull_action_job(
retry_job(), timeout=ISSUE_ACTION_TIMEOUT_SECONDS
)
except HTTPException:
try:
await asyncio.to_thread(journal.discard, operation_id)
except SecurityEventStoreError:
pass
raise
except gitea_proxy.StalePullError:
try:
await asyncio.to_thread(journal.discard, operation_id)
except SecurityEventStoreError:
pass
return JSONResponse(
{"error": "New commits arrived. Reload checks before retrying this job."},
status_code=409,
headers={"Cache-Control": "no-store"},
)
except ValueError:
try:
await asyncio.to_thread(journal.discard, operation_id)
except SecurityEventStoreError:
pass
return JSONResponse(
{"error": "This check is no longer failed or cannot be retried."},
status_code=409,
@@ -7904,6 +7942,10 @@ async def retry_pull_action_job(
status_code=503,
headers={"Cache-Control": "no-store", "Retry-After": "1"},
)
try:
await asyncio.to_thread(journal.finalize, operation_id)
except SecurityEventStoreError:
pass
return JSONResponse(
result, status_code=202, headers={"Cache-Control": "no-store"}
)
@@ -8359,14 +8401,39 @@ async def release_action_failure(
"/checks/{run_id}/jobs/{job_index}/retry"
)
async def retry_release_action_job(
request: Request,
owner: str,
repo: str,
number: int = PathParam(gt=0),
commit_sha: str = PathParam(min_length=7, max_length=64, pattern=r"^[A-Fa-f0-9]+$"),
run_id: int = PathParam(gt=0),
job_index: int = PathParam(ge=0),
step_up_grant: str | None = Header(
default=None, alias="X-Step-Up-Grant", max_length=128
),
) -> JSONResponse:
repository = f"{owner}/{repo}"
target = (
f"{repository}#{number}@{commit_sha}:"
f"actions/{run_id}/jobs/{job_index}"
)
await _require_step_up(
request, step_up_grant, action="retry_ci_job", target=target
)
journal = _security_event_store()
try:
operation_id = await asyncio.to_thread(
journal.reserve,
"ci_job_retried",
principal_id=await _security_principal_id(request),
target=target,
)
except SecurityEventStoreError:
return JSONResponse(
{"error": "Security activity is temporarily unavailable. No job was retried."},
status_code=503,
headers={"Cache-Control": "no-store", "Retry-After": "1"},
)
async def retry_job():
if not await gitea_proxy.can_recover_merged_release(
@@ -8382,8 +8449,16 @@ async def retry_release_action_job(
retry_job(), timeout=ISSUE_ACTION_TIMEOUT_SECONDS
)
except HTTPException:
try:
await asyncio.to_thread(journal.discard, operation_id)
except SecurityEventStoreError:
pass
raise
except ValueError:
try:
await asyncio.to_thread(journal.discard, operation_id)
except SecurityEventStoreError:
pass
return JSONResponse(
{"error": "This release check is no longer failed or cannot be retried."},
status_code=409,
@@ -8395,6 +8470,10 @@ async def retry_release_action_job(
status_code=503,
headers={"Cache-Control": "no-store", "Retry-After": "1"},
)
try:
await asyncio.to_thread(journal.finalize, operation_id)
except SecurityEventStoreError:
pass
return JSONResponse(
result, status_code=202, headers={"Cache-Control": "no-store"}
)