CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
234 lines
8.8 KiB
Python
234 lines
8.8 KiB
Python
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Inventory of S3 buckets used by CI/CD systems and related functions.
|
|
|
|
See docs/development/s3_buckets.md.
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
import os
|
|
import sys
|
|
|
|
|
|
def _log(*args, **kwargs):
|
|
"""Log to stdout with flush for CI visibility."""
|
|
print(*args, **kwargs)
|
|
sys.stdout.flush()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class S3BucketConfig:
|
|
"""Metadata for a single bucket in S3"""
|
|
|
|
name: str
|
|
"""S3 bucket name (e.g. 'therock-ci-artifacts')"""
|
|
|
|
region: str = field(default="us-east-2")
|
|
"""Region in S3 (e.g. 'us-east-2')"""
|
|
|
|
iam_account: str | None = field(default="692859939525")
|
|
"""IAM account for write_access_iam_role"""
|
|
|
|
iam_role: str | None = field(default=None)
|
|
"""IAM role name that grants write access to this bucket (e.g. 'therock-ci'), if any"""
|
|
|
|
@property
|
|
def write_access_iam_role(self) -> str | None:
|
|
"""IAM role granting write access to the bucket"""
|
|
if not self.iam_role:
|
|
return None
|
|
if not self.iam_account:
|
|
raise ValueError(
|
|
f"Bucket {self.name!r} has iam_role={self.iam_role!r} but no iam_account"
|
|
)
|
|
return f"arn:aws:iam::{self.iam_account}:role/{self.iam_role}"
|
|
|
|
|
|
s3_bucket_configs = [
|
|
# CI (external repos use OIDC with therock-ci-external; fork PRs use runner base credentials)
|
|
S3BucketConfig("therock-ci-artifacts", iam_role="therock-ci"),
|
|
S3BucketConfig("therock-ci-artifacts-external", iam_role="therock-ci-external"),
|
|
# Release type "dev"
|
|
S3BucketConfig("therock-dev-artifacts", iam_role="therock-dev"),
|
|
S3BucketConfig("therock-dev-packages", iam_role="therock-dev"),
|
|
S3BucketConfig("therock-dev-python", iam_role="therock-dev"),
|
|
S3BucketConfig("therock-dev-tarball", iam_role="therock-dev"),
|
|
# Release type "nightly"
|
|
S3BucketConfig("therock-nightly-artifacts", iam_role="therock-nightly"),
|
|
S3BucketConfig("therock-nightly-packages", iam_role="therock-nightly"),
|
|
S3BucketConfig("therock-nightly-python", iam_role="therock-nightly"),
|
|
S3BucketConfig("therock-nightly-tarball", iam_role="therock-nightly"),
|
|
# Release type "prerelease"
|
|
S3BucketConfig("therock-prerelease-artifacts", iam_role="therock-prerelease"),
|
|
S3BucketConfig("therock-prerelease-packages", iam_role="therock-prerelease"),
|
|
S3BucketConfig("therock-prerelease-python", iam_role="therock-prerelease"),
|
|
S3BucketConfig("therock-prerelease-tarball", iam_role="therock-prerelease"),
|
|
# Release type "release" (no automated credentials for uploading)
|
|
S3BucketConfig("therock-release-artifacts", iam_role=None),
|
|
S3BucketConfig("therock-release-packages", iam_role=None),
|
|
S3BucketConfig("therock-release-python", iam_role=None),
|
|
S3BucketConfig("therock-release-tarball", iam_role=None),
|
|
]
|
|
|
|
|
|
_BUCKET_CONFIGS_BY_NAME = {c.name: c for c in s3_bucket_configs}
|
|
|
|
_ALLOWED_ARTIFACT_RELEASE_TYPES = {"ci", "dev", "nightly", "prerelease"}
|
|
|
|
_ALLOWED_RELEASE_TYPES = {"dev", "nightly", "prerelease"}
|
|
|
|
_ALLOWED_RELEASE_BUCKET_TYPES = {"tarball", "python", "packages"}
|
|
|
|
|
|
def get_artifacts_bucket_config(
|
|
release_type: str,
|
|
repository: str,
|
|
is_pr_from_fork: bool,
|
|
) -> S3BucketConfig:
|
|
"""Look up the artifacts bucket config for a repository.
|
|
|
|
Args:
|
|
release_type: "ci", "dev", "nightly", or "prerelease".
|
|
repository: GitHub repository (e.g. "ROCm/TheRock").
|
|
is_pr_from_fork: Whether this is a PR from a fork.
|
|
|
|
Raises:
|
|
ValueError: If release_type is invalid.
|
|
"""
|
|
if release_type not in _ALLOWED_ARTIFACT_RELEASE_TYPES:
|
|
raise ValueError(
|
|
f"release_type={release_type!r} is invalid, "
|
|
f"expected one of {_ALLOWED_ARTIFACT_RELEASE_TYPES}"
|
|
)
|
|
|
|
if release_type == "ci":
|
|
if is_pr_from_fork or repository != "ROCm/TheRock":
|
|
bucket_name = "therock-ci-artifacts-external"
|
|
else:
|
|
bucket_name = "therock-ci-artifacts"
|
|
else:
|
|
bucket_name = f"therock-{release_type}-artifacts"
|
|
return _BUCKET_CONFIGS_BY_NAME[bucket_name]
|
|
|
|
|
|
def get_release_bucket_config(
|
|
release_type: str,
|
|
bucket_type: str,
|
|
) -> S3BucketConfig:
|
|
"""Look up the release bucket config for a given release type and bucket type.
|
|
|
|
Args:
|
|
release_type: "dev", "nightly", or "prerelease".
|
|
bucket_type: "tarball", "python", or "packages".
|
|
|
|
Returns:
|
|
S3BucketConfig for the bucket ``therock-{release_type}-{bucket_type}``.
|
|
|
|
Raises:
|
|
ValueError: If release_type or bucket_type is invalid.
|
|
"""
|
|
if release_type not in _ALLOWED_RELEASE_TYPES:
|
|
raise ValueError(
|
|
f"release_type={release_type!r} is invalid, "
|
|
f"expected one of {_ALLOWED_RELEASE_TYPES}"
|
|
)
|
|
if bucket_type not in _ALLOWED_RELEASE_BUCKET_TYPES:
|
|
raise ValueError(
|
|
f"bucket_type={bucket_type!r} is invalid, "
|
|
f"expected one of {_ALLOWED_RELEASE_BUCKET_TYPES}"
|
|
)
|
|
bucket_name = f"therock-{release_type}-{bucket_type}"
|
|
return _BUCKET_CONFIGS_BY_NAME[bucket_name]
|
|
|
|
|
|
def get_artifacts_bucket_config_for_workflow_run(
|
|
github_repository: str,
|
|
release_type: str | None = None,
|
|
workflow_run_id: str | None = None,
|
|
workflow_run: dict | None = None,
|
|
) -> S3BucketConfig:
|
|
"""Look up the artifacts bucket config for a workflow run.
|
|
|
|
Combines environment-based inputs (RELEASE_TYPE, event payload) with
|
|
optional workflow run metadata from the GitHub API to determine the
|
|
correct artifacts bucket.
|
|
|
|
Args:
|
|
github_repository: GitHub repository (e.g. "ROCm/TheRock").
|
|
release_type: Release type override. If None, reads RELEASE_TYPE
|
|
from the environment (default: "ci").
|
|
workflow_run_id: If set and ``workflow_run`` is None, fetches the
|
|
workflow run from the GitHub API for fork detection.
|
|
workflow_run: Optional workflow run dict from GitHub API. If
|
|
provided, used directly for fork detection (no API call).
|
|
"""
|
|
_log("Retrieving bucket info for workflow run...")
|
|
_log(f" github_repository: {github_repository}")
|
|
|
|
if release_type is None:
|
|
release_type = os.environ.get("RELEASE_TYPE", "ci")
|
|
_log(f" release_type: {release_type}")
|
|
|
|
# Fetch workflow_run from API if not provided but workflow_run_id is set.
|
|
# Deferred import: github_actions is an optional dependency not available in
|
|
# all environments (e.g. local dev without the GHA support package installed).
|
|
if workflow_run is None and workflow_run_id is not None:
|
|
from github_actions.github_actions_api import (
|
|
GitHubAPIError,
|
|
gha_query_workflow_run_by_id,
|
|
)
|
|
|
|
try:
|
|
workflow_run = gha_query_workflow_run_by_id(
|
|
github_repository, workflow_run_id
|
|
)
|
|
except GitHubAPIError as e:
|
|
run_url = (
|
|
f"https://github.com/{github_repository}/actions/runs/{workflow_run_id}"
|
|
)
|
|
raise GitHubAPIError(
|
|
f"Failed to query workflow run {workflow_run_id} in repository "
|
|
f"{github_repository}: {run_url}\n"
|
|
f" {e}\n"
|
|
f"Hint: Did you mean to specify a different repository with "
|
|
f"--run-github-repo?"
|
|
) from e
|
|
|
|
# Extract metadata from workflow_run if available
|
|
if workflow_run is not None:
|
|
_log(f" workflow_run_id: {workflow_run['id']}")
|
|
head_github_repository = workflow_run["head_repository"]["full_name"]
|
|
is_pr_from_fork = head_github_repository != github_repository
|
|
_log(f" head_github_repository: {head_github_repository}")
|
|
_log(f" is_pr_from_fork: {is_pr_from_fork}")
|
|
else:
|
|
# Deferred import: github_actions is optional in some environments;
|
|
# only needed when resolving fork state from the on-disk event payload.
|
|
from github_actions.github_actions_api import is_current_run_pr_from_fork
|
|
|
|
is_pr_from_fork = is_current_run_pr_from_fork()
|
|
_log(f" is_pr_from_fork: {is_pr_from_fork}")
|
|
|
|
config = get_artifacts_bucket_config(
|
|
release_type=release_type,
|
|
repository=github_repository,
|
|
is_pr_from_fork=is_pr_from_fork,
|
|
)
|
|
_log(f" bucket: {config.name}")
|
|
|
|
# For fork PRs, skip OIDC and use runner base credentials instead.
|
|
# Fork PRs cannot assume IAM roles via OIDC because they don't have
|
|
# the required trust relationship. Return a config without an IAM role
|
|
# so the configure-aws-credentials step is skipped.
|
|
if is_pr_from_fork and config.iam_role is not None:
|
|
_log(" Fork PR detected, skipping OIDC (using runner base credentials)")
|
|
config = S3BucketConfig(
|
|
name=config.name,
|
|
region=config.region,
|
|
iam_account=config.iam_account,
|
|
iam_role=None,
|
|
)
|
|
|
|
return config
|