Skip to content
Merged
Show file tree
Hide file tree
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
35 changes: 35 additions & 0 deletions .github/actions/configure_aws_artifacts_credentials/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT

name: "Configure AWS credentials for artifact uploads"

description: >-
Determine the IAM role for the artifacts S3 bucket and assume it via OIDC.
* Forks and external repos skip OIDC and use runner base credentials.
* Release builds (dev/nightly/prerelease) use 'therock-{release_type}' roles.
* Other CI runs use the 'therock-ci' role.
See the "Authentication" section in docs/development/s3_buckets.md.

inputs:
release_type:
description: 'Release type: "" for CI, or "dev", "nightly", "prerelease".'
default: ""

runs:
using: "composite"
steps:
- name: Determine IAM role
if: ${{ always() }}
id: iam
shell: bash
run: |
python build_tools/github_actions/write_artifacts_bucket_info.py \
--release-type "${{ inputs.release_type }}"

- name: Configure AWS Credentials
if: ${{ always() && steps.iam.outputs.iam_role != '' }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: ${{ steps.iam.outputs.aws_region }}
role-to-assume: ${{ steps.iam.outputs.iam_role }}
special-characters-workaround: ${{ runner.os == 'Windows' }}
9 changes: 2 additions & 7 deletions .github/workflows/build_portable_linux_artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,9 @@ jobs:
python3 build_tools/resource_info.py --finalize
python3 build_tools/analyze_build_times.py --build-dir build

# Assume the therock-ci OIDC role in ROCm/TheRock. Other repos
# fall back to runner base credentials (therock-ci-artifacts-external).
- name: Configure AWS Credentials
if: ${{ always() && github.repository == 'ROCm/TheRock' && !github.event.pull_request.head.repo.fork }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
Comment on lines -172 to -175
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also update

- name: Configure AWS Credentials
if: ${{ github.repository_owner == 'ROCm' && !cancelled() }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-${{ env.RELEASE_TYPE }}

but that workflow uses more than just the therock-{release_type}-artifacts bucket, it also uses

S3_BUCKET_PY: "therock-${{ needs.setup_metadata.outputs.release_type }}-python"

For multi-arch release workflows, I plan on having the existing build workflows upload artifacts,logs,etc. to therock-{release_type}-artifacts and then have new workflow code copy from those buckets to e.g. therock-{release_type}-tarball


By the way, I decided to NOT update

- name: Configure AWS Credentials
if: ${{ github.repository == 'ROCm/TheRock' }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updating release_portable_linux_packages.yml should be fine as the role allows to access the other buckets as well. Can be in a follow up however.

if: ${{ always() }}
uses: ./.github/actions/configure_aws_artifacts_credentials

- name: Post Build Upload
if: always()
Expand Down
8 changes: 1 addition & 7 deletions .github/workflows/build_portable_linux_python_packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,8 @@ jobs:
--dest-dir="${{ env.PACKAGES_DIR }}" \
--version="${{ inputs.package_version }}"

# Assume the therock-ci OIDC role in ROCm/TheRock. Other repos
# fall back to runner base credentials (therock-ci-artifacts-external).
- name: Configure AWS Credentials
if: ${{ github.repository == 'ROCm/TheRock' && !github.event.pull_request.head.repo.fork }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
uses: ./.github/actions/configure_aws_artifacts_credentials

# NOTE: we use `github.run_id` and NOT `env.ARTIFACT_RUN_ID` here!
# This ensures that if they are different we _download_ artifacts from the
Expand Down
10 changes: 2 additions & 8 deletions .github/workflows/build_windows_artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,9 @@ jobs:
$fsout | % {$_.Used/=1GB;$_.Free/=1GB;$_} | Write-Host
get-disk | Select-object @{Name="Size(GB)";Expression={$_.Size/1GB}} | Write-Host

# Assume the therock-ci OIDC role in ROCm/TheRock. Other repos
# fall back to runner base credentials (therock-ci-artifacts-external).
- name: Configure AWS Credentials
if: ${{ always() && github.repository == 'ROCm/TheRock' && !github.event.pull_request.head.repo.fork }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
special-characters-workaround: true
if: ${{ always() }}
uses: ./.github/actions/configure_aws_artifacts_credentials

- name: Post Build Upload
if: always()
Expand Down
9 changes: 1 addition & 8 deletions .github/workflows/build_windows_python_packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,8 @@ jobs:
--dest-dir="${{ env.PACKAGES_DIR }}" \
--version="${{ inputs.package_version }}"

# Assume the therock-ci OIDC role in ROCm/TheRock. Other repos
# fall back to runner base credentials (therock-ci-artifacts-external).
- name: Configure AWS Credentials
if: ${{ github.repository == 'ROCm/TheRock' && !github.event.pull_request.head.repo.fork }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
special-characters-workaround: true
uses: ./.github/actions/configure_aws_artifacts_credentials

# NOTE: we use `github.run_id` and NOT `env.ARTIFACT_RUN_ID` here!
# This ensures that if they are different we _download_ artifacts from the
Expand Down
11 changes: 3 additions & 8 deletions .github/workflows/multi_arch_build_portable_linux_artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,9 @@ jobs:
echo "Artifacts:"
ls -lh "${BUILD_DIR}"/artifacts/*.tar.xz 2>/dev/null || echo "No artifacts found"

# Assume the therock-ci OIDC role in ROCm/TheRock. Other repos
# fall back to runner base credentials (therock-ci-artifacts-external).
- name: Configure AWS Credentials
if: ${{ always() && github.repository == 'ROCm/TheRock' && !github.event.pull_request.head.repo.fork }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
- name: Configure AWS credentials for artifact uploads
if: ${{ always() }}
uses: ./.github/actions/configure_aws_artifacts_credentials

# TODO(#3336): Attempt upload `always()`?
# * If some part of the build failed, partial artifacts may be useful for debugging.
Expand Down
12 changes: 3 additions & 9 deletions .github/workflows/multi_arch_build_windows_artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,9 @@ jobs:
echo "Artifacts:"
ls -lh "${BUILD_DIR}"/artifacts/*.tar.xz 2>/dev/null || echo "No artifacts found"

# Assume the therock-ci OIDC role in ROCm/TheRock. Other repos
# fall back to runner base credentials (therock-ci-artifacts-external).
- name: Configure AWS Credentials
if: ${{ always() && github.repository == 'ROCm/TheRock' && !github.event.pull_request.head.repo.fork }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
special-characters-workaround: true
- name: Configure AWS credentials for artifact uploads
if: ${{ always() }}
uses: ./.github/actions/configure_aws_artifacts_credentials

# TODO(#3336): Attempt upload `always()`?
# * If some part of the build failed, partial artifacts may be useful for debugging.
Expand Down
13 changes: 2 additions & 11 deletions .github/workflows/multi_arch_ci_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,8 @@ jobs:
- name: Install artifact manager dependencies
run: pip install boto3

# Assume the therock-ci OIDC role in ROCm/TheRock. Other repos
# fall back to runner base credentials (therock-ci-artifacts-external).
#
# This is just needed for write access to the current run's bucket,
# all possible source run buckets have public read access.
- name: Configure AWS Credentials
if: ${{ github.repository == 'ROCm/TheRock' && !github.event.pull_request.head.repo.fork }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
- name: Configure AWS credentials for artifact uploads
uses: ./.github/actions/configure_aws_artifacts_credentials

- name: Copy prebuilt stage artifacts
env:
Expand Down
13 changes: 2 additions & 11 deletions .github/workflows/multi_arch_ci_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,8 @@ jobs:
- name: Install artifact manager dependencies
run: pip install boto3

# Assume the therock-ci OIDC role in ROCm/TheRock. Other repos
# fall back to runner base credentials (therock-ci-artifacts-external).
#
# This is just needed for write access to the current run's bucket,
# all possible source run buckets have public read access.
- name: Configure AWS Credentials
if: ${{ github.repository == 'ROCm/TheRock' && !github.event.pull_request.head.repo.fork }}
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
- name: Configure AWS credentials for artifact uploads
uses: ./.github/actions/configure_aws_artifacts_credentials

- name: Copy prebuilt stage artifacts
env:
Expand Down
198 changes: 198 additions & 0 deletions build_tools/_therock_utils/s3_buckets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# 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 json
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 (self-hosted runners include credentials for therock-ci-artifacts-external)
S3BucketConfig("therock-ci-artifacts", iam_role="therock-ci"),
S3BucketConfig("therock-ci-artifacts-external", iam_role=None),
# 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_RELEASE_TYPES = {"dev", "nightly", "prerelease"}

# Repositories allowed to use release_type. Only these repositories are trusted
# to assume release IAM roles that grant write access to release buckets.
_ALLOWED_RELEASE_REPOS = {"ROCm/TheRock", "ROCm/rockrel"}


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: "" for CI builds, or "dev", "nightly", "prerelease".
repository: GitHub repository (e.g. "ROCm/TheRock").
is_pr_from_fork: Whether this is a PR from a fork.
"""
if release_type:
if release_type not in _ALLOWED_RELEASE_TYPES:
raise ValueError(
f"release_type={release_type!r} is invalid, "
f"expected empty string or one of {_ALLOWED_RELEASE_TYPES}"
)
if repository not in _ALLOWED_RELEASE_REPOS:
raise ValueError(
f"release_type={release_type!r} is set but "
f"repository {repository!r} is not one of "
f"{_ALLOWED_RELEASE_REPOS}"
)
bucket_name = f"therock-{release_type}-artifacts"
else:
if is_pr_from_fork or repository != "ROCm/TheRock":
bucket_name = "therock-ci-artifacts-external"
else:
bucket_name = "therock-ci-artifacts"
return _BUCKET_CONFIGS_BY_NAME[bucket_name]


def _is_current_run_pr_from_fork() -> bool:
"""Check if the current workflow run is a pull request from a fork.

Reads the GitHub event payload to check the .fork property on the
head repo, matching the behavior of the GitHub Actions expression
``github.event.pull_request.head.repo.fork``.

Returns False for non-pull_request events or if the event payload
is not available (e.g. local development).
"""
event_name = os.environ.get("GITHUB_EVENT_NAME", "")
if event_name != "pull_request":
return False

event_path = os.environ.get("GITHUB_EVENT_PATH")
if not event_path:
return False

with open(event_path) as f:
event = json.load(f)

return bool(
event.get("pull_request", {}).get("head", {}).get("repo", {}).get("fork", False)
)


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: empty string = CI build).
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", "")
if release_type:
_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 gha_query_workflow_run_by_id

workflow_run = gha_query_workflow_run_by_id(github_repository, workflow_run_id)

# 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:
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}")
return config
Loading
Loading