Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fetch artifacts from GitHub Actions #973

Merged
merged 6 commits into from
Apr 1, 2024
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
49 changes: 42 additions & 7 deletions bioconda_utils/artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def upload_pr_artifacts(config, repo, git_sha, dryrun=False, mulled_upload_targe
# no PR found for the commit
return UploadResult.NO_PR
pr = prs[0]
artifacts = set(fetch_artifacts(pr, artifact_source))
artifacts = set(fetch_artifacts(pr, artifact_source, repo))
if not artifacts:
# no artifacts found, fail and rebuild packages
logger.info("No artifacts found.")
Expand All @@ -54,13 +54,19 @@ def upload_pr_artifacts(config, repo, git_sha, dryrun=False, mulled_upload_targe
# download the artifact
if artifact_source == "azure":
artifact_path = os.path.join(tmpdir, os.path.basename(artifact))
download_artifact(artifact, artifact_path)
download_artifact(artifact, artifact_path, artifact_source)
zipfile.ZipFile(artifact_path).extractall(tmpdir)
elif artifact_source == "circleci":
artifact_dir = os.path.join(tmpdir, *(artifact.split("/")[-4:-1]))
artifact_path = os.path.join(tmpdir, artifact_dir, os.path.basename(artifact))
Path(artifact_dir).mkdir(parents=True, exist_ok=True)
download_artifact(artifact, artifact_path)
Path(artifact_dir).mkdir(parents=True, exist_ok=True)
download_artifact(artifact, artifact_path, artifact_source)
elif artifact_source == "github-actions":
artifact_dir = os.path.join(tmpdir, "artifacts")
artifact_path = os.path.join(artifact_dir, os.path.basename(artifact))
Path(artifact_dir).mkdir(parents=True, exist_ok=True)
download_artifact(artifact, artifact_path, artifact_source)
zipfile.ZipFile(artifact_path).extractall(artifact_dir)

# get all the contained packages and images and upload them
platform_patterns = [repodata.platform2subdir(repodata.native_platform())]
Expand Down Expand Up @@ -110,17 +116,24 @@ def upload_pr_artifacts(config, repo, git_sha, dryrun=False, mulled_upload_targe
backoff.expo,
requests.exceptions.RequestException
)
def download_artifact(url, to_path):
def download_artifact(url, to_path, artifact_source):
logger.info(f"Downloading artifact {url}.")
resp = requests.get(url, stream=True, allow_redirects=True)
headers = {}
if artifact_source == "github-actions":
token = os.environ.get("GITHUB_TOKEN")
if not token:
logger.critical("GITHUB_TOKEN required to download GitHub Actions artifacts")
exit(1)
headers = {"Authorization": f"token {token}"}
resp = requests.get(url, stream=True, allow_redirects=True, headers=headers)
daler marked this conversation as resolved.
Show resolved Hide resolved
resp.raise_for_status()
with open(to_path, "wb") as f:
for chunk in resp.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)


def fetch_artifacts(pr, artifact_source):
def fetch_artifacts(pr, artifact_source, repo):
"""
Fetch artifacts from a PR.

Expand Down Expand Up @@ -155,6 +168,13 @@ def fetch_artifacts(pr, artifact_source):
# Circle CI builds
artifact_url = get_circleci_artifacts(check_run, platform)
yield from artifact_url
elif (
artifact_source == "github-actions" and
check_run.app.slug == "github-actions"
):
# GitHubActions builds
artifact_url = get_gha_artifacts(check_run, platform, repo)
yield from artifact_url


def get_azure_artifacts(check_run):
Expand Down Expand Up @@ -197,3 +217,18 @@ def get_circleci_artifacts(check_run, platform):
continue
else:
yield artifact_url

def parse_gha_build_id(url: str) -> str:
# Get workflow run id from URL
return re.search("runs/(\d+)/", url).group(1)

def get_gha_artifacts(check_run, platform, repo):
gha_workflow_id = parse_gha_build_id(check_run.details_url)
if (gha_workflow_id) :
# The workflow run is different from the check run
run = repo.get_workflow_run(int(gha_workflow_id))
artifacts = run.get_artifacts()
for artifact in artifacts:
# This URL is valid for 1 min and requires a token
artifact_url = artifact.archive_download_url
yield artifact_url
2 changes: 1 addition & 1 deletion bioconda_utils/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ def build(recipe_folder, config, packages="*", git_range=None, testonly=False,
@arg('--dryrun', action='store_true', help='''Do not actually upload anything.''')
@arg('--fallback', choices=['build', 'ignore'], default='build', help="What to do if no artifacts are found in the PR.")
@arg('--quay-upload-target', help="Provide a quay.io target to push docker images to.")
@arg('--artifact-source', choices=['azure', 'circleci'], default='azure', help="Application hosting build artifacts (e.g., Azure or Circle CI).")
@arg('--artifact-source', choices=['azure', 'circleci','github-actions'], default='azure', help="Application hosting build artifacts (e.g., Azure, Circle CI, or GitHub Actions).")
@enable_logging()
def handle_merged_pr(
recipe_folder,
Expand Down