Skip to content
Open
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
76 changes: 62 additions & 14 deletions .github/workflows/dispatch-autotest-windows-upgrade.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
GITHUB_SHA_VALUE: ${{ github.sha }}
GITHUB_REF_NAME_VALUE: ${{ github.ref_name }}
GITHUB_RUN_ID_VALUE: ${{ github.run_id }}
GITHUB_RUN_ATTEMPT_VALUE: ${{ github.run_attempt }}
run: |
set -euo pipefail

Expand All @@ -78,11 +79,15 @@ jobs:
exit 1
fi

artifact_time="$(printf "%02d" "${GITHUB_RUN_ATTEMPT_VALUE:-1}")"
artifact_name="flocks-windows-upgrade-${release_tag}-time${artifact_time}"

{
echo "release_tag=$release_tag"
echo "release_url=$release_url"
echo "rollback_version=${INPUT_ROLLBACK_VERSION:-}"
echo "run_force_fallback=${INPUT_RUN_FORCE_FALLBACK:-false}"
echo "artifact_name=$artifact_name"
} >> "$GITHUB_OUTPUT"

{
Expand All @@ -95,7 +100,7 @@ jobs:
echo "- Source sha: ${GITHUB_SHA_VALUE}"
echo "- Rollback version: ${INPUT_ROLLBACK_VERSION:-}"
echo "- Run force fallback: ${INPUT_RUN_FORCE_FALLBACK:-false}"
echo "- Expected autotest artifact prefix: flocks-windows-upgrade-${release_tag}-time"
echo "- Expected autotest artifact: ${artifact_name}"
echo "- Autotest workflow: https://github.com/AgentFlocks/flocks_autotest/actions/workflows/flocks-release-dispatch.yml"
} >> "$GITHUB_STEP_SUMMARY"

Expand All @@ -105,6 +110,7 @@ jobs:
RELEASE_URL: ${{ steps.payload.outputs.release_url }}
ROLLBACK_VERSION: ${{ steps.payload.outputs.rollback_version }}
RUN_FORCE_FALLBACK: ${{ steps.payload.outputs.run_force_fallback }}
ARTIFACT_NAME: ${{ steps.payload.outputs.artifact_name }}
SOURCE_REPOSITORY: ${{ github.repository }}
SOURCE_RUN_ID: ${{ github.run_id }}
SOURCE_SHA: ${{ github.sha }}
Expand All @@ -126,6 +132,7 @@ jobs:
"source_run_id": os.environ["SOURCE_RUN_ID"],
"source_sha": os.environ["SOURCE_SHA"],
"source_ref": os.environ["SOURCE_REF"],
"artifact_name": os.environ["ARTIFACT_NAME"],
"rollback_version": os.environ.get("ROLLBACK_VERSION", ""),
"run_force_fallback": os.environ.get("RUN_FORCE_FALLBACK", "false"),
},
Expand Down Expand Up @@ -158,7 +165,7 @@ jobs:
timeout-minutes: 210
env:
GH_TOKEN: ${{ secrets.AUTOTEST_DISPATCH_TOKEN }}
RELEASE_TAG: ${{ steps.payload.outputs.release_tag }}
EXPECTED_ARTIFACT_NAME: ${{ steps.payload.outputs.artifact_name }}
SOURCE_RUN_ID: ${{ github.run_id }}
AUTOTEST_REPOSITORY: AgentFlocks/flocks_autotest
AUTOTEST_WORKFLOW_FILE: flocks-release-dispatch.yml
Expand All @@ -178,7 +185,7 @@ jobs:

api_root = "https://api.github.com"
token = os.environ["GH_TOKEN"]
release_tag = os.environ["RELEASE_TAG"]
expected_artifact_name = os.environ["EXPECTED_ARTIFACT_NAME"]
source_run_id = os.environ["SOURCE_RUN_ID"]
autotest_repo = os.environ["AUTOTEST_REPOSITORY"]
workflow_file = os.environ["AUTOTEST_WORKFLOW_FILE"]
Expand All @@ -199,9 +206,31 @@ jobs:
with urllib.request.urlopen(request, timeout=30) as response:
return json.loads(response.read().decode("utf-8"))

class NoRedirectHandler(urllib.request.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None

def download(url, destination):
request = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(request, timeout=300) as response:
opener = urllib.request.build_opener(NoRedirectHandler)
try:
opener.open(request, timeout=30)
except urllib.error.HTTPError as error:
if error.code not in (301, 302, 303, 307, 308):
raise
redirect_url = error.headers.get("Location")
if not redirect_url:
raise RuntimeError("Artifact download redirect did not include Location header") from error
else:
raise RuntimeError("Artifact download did not return the expected redirect")

# GitHub returns a short-lived signed URL for the artifact archive.
# Do not forward the GitHub PAT to that storage endpoint.
redirect_request = urllib.request.Request(
redirect_url,
headers={"Accept": "application/octet-stream"},
)
with urllib.request.urlopen(redirect_request, timeout=300) as response:
with open(destination, "wb") as f:
f.write(response.read())

Expand All @@ -220,7 +249,6 @@ jobs:
f"{api_root}/repos/{autotest_repo}/actions/workflows/"
f"{workflow_ref}/runs?event=repository_dispatch&per_page=30"
)
artifact_prefix = f"flocks-windows-upgrade-{release_tag}-time"
deadline = time.time() + wait_timeout
matched_run = None

Expand All @@ -246,7 +274,7 @@ jobs:
"",
f"- Status: timed out waiting for workflow run",
f"- Source run id: {source_run_id}",
f"- Expected artifact prefix: {artifact_prefix}",
f"- Expected artifact: {expected_artifact_name}",
]
)
raise SystemExit("Timed out waiting for flocks_autotest workflow run")
Expand Down Expand Up @@ -275,19 +303,37 @@ jobs:
"",
f"- Status: timed out waiting for completion",
f"- Run: {run_html_url}",
f"- Expected artifact prefix: {artifact_prefix}",
f"- Expected artifact: {expected_artifact_name}",
]
)
raise SystemExit("Timed out waiting for flocks_autotest completion")

conclusion = matched_run.get("conclusion") or "unknown"
artifacts_url = f"{api_root}/repos/{autotest_repo}/actions/runs/{run_id}/artifacts?per_page=100"
artifacts = api_json(artifacts_url).get("artifacts", [])
matching_artifacts = [
artifact
for artifact in artifacts
if not artifact.get("expired") and (artifact.get("name") or "").startswith(artifact_prefix)
]
artifact_lookup_deadline = time.time() + 120
artifacts = []
available_artifact_names = []
matching_artifacts = []
while True:
artifacts = api_json(artifacts_url).get("artifacts", [])
available_artifact_names = [
artifact.get("name") or ""
for artifact in artifacts
if not artifact.get("expired") and artifact.get("name")
]
matching_artifacts = [
artifact
for artifact in artifacts
if not artifact.get("expired") and (artifact.get("name") or "") == expected_artifact_name
]
if matching_artifacts or time.time() >= artifact_lookup_deadline:
break
available_text = ", ".join(available_artifact_names) if available_artifact_names else "(none)"
print(
f"Waiting for artifact {expected_artifact_name}. Available artifacts: {available_text}",
flush=True,
)
time.sleep(10)
matching_artifacts.sort(key=lambda artifact: artifact.get("created_at") or "", reverse=True)

artifact_name = ""
Expand Down Expand Up @@ -319,13 +365,15 @@ jobs:
"",
f"- Run: {run_html_url}",
f"- Conclusion: {conclusion}",
f"- Expected artifact prefix: {artifact_prefix}",
f"- Expected artifact: {expected_artifact_name}",
]
if artifact_name:
summary_lines.append(f"- Artifact: [{artifact_name}]({artifact_html_url})")
summary_lines.append("- Artifact copy: uploaded to this flocks workflow run")
else:
summary_lines.append("- Artifact: not found")
available_text = ", ".join(available_artifact_names) if available_artifact_names else "(none)"
summary_lines.append(f"- Available artifacts: {available_text}")
write_summary(summary_lines)

if conclusion != "success":
Expand Down