Files
TheRock/build_tools/_therock_utils/s3_buckets.py
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

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