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

Add support for Cirun on self-hosted GHA runners #1703

Merged
merged 75 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
e6dcdff
Add support for cirun on self-hosted GHA runners
jaimergp Dec 19, 2022
fd031cd
pre-commit
jaimergp Dec 19, 2022
7836cce
add more labels and runners
jaimergp Dec 27, 2022
2598d9a
produce .cirun.yml
jaimergp Jan 9, 2023
e6158f9
update runner config
jaimergp Jan 9, 2023
5f96e5e
refactor os-matrix building in gha template
jaimergp Jan 9, 2023
a366a1f
update vm [ci skip]
jaimergp Jan 10, 2023
4104525
default to gpu_large
jaimergp Jan 23, 2023
d6ae11a
support cancel_in_progress
jaimergp Jan 23, 2023
9e01400
raw
jaimergp Jan 23, 2023
dfb99f2
Merge branch 'main' of github.com:conda-forge/conda-smithy into gha-s…
jaimergp Feb 9, 2023
adc5426
Merge branch 'main' of github.com:conda-forge/conda-smithy into gha-s…
jaimergp Jul 26, 2023
bc96c16
add stubs for opt-in ci registration
jaimergp Jul 26, 2023
d3258f9
TEMP
aktech Aug 5, 2023
f5dad75
add utilities to handle adding cirun
aktech Aug 6, 2023
eb8811e
move cirun stuff to cirun utils
aktech Aug 17, 2023
8d4a674
Merge branch 'main' into gha-self-hosted-cirun
aktech Aug 17, 2023
f672f73
some cleanups
aktech Aug 21, 2023
156e27d
undo conda-build version change
aktech Aug 21, 2023
0a7bdd9
make precommit happy
aktech Aug 22, 2023
7b0c3e3
add installation id
aktech Aug 22, 2023
fd37814
add policy args
aktech Aug 26, 2023
e72cb95
set installation id as environment variable
aktech Aug 26, 2023
9126d1c
Merge branch 'main' into gha-self-hosted-cirun
aktech Aug 26, 2023
832f85d
move conda-forge to environ variable
aktech Aug 26, 2023
d384958
use owner/org from cli instead of env var
aktech Aug 26, 2023
d084434
minor fixes
aktech Aug 26, 2023
6355aa8
assert installation id
aktech Aug 27, 2023
37d0252
add some more logging
aktech Aug 27, 2023
94aac9e
log cirun response
aktech Aug 27, 2023
c2db57e
remove commented out code and cirun file json
aktech Aug 27, 2023
63c566a
remove .cirun.yml template reference
aktech Aug 27, 2023
ce81aa2
pre-commit fixes
aktech Aug 28, 2023
829201e
undo changes to github actions template
aktech Aug 28, 2023
c68e3b0
undo changes to configure feedstock
aktech Aug 28, 2023
03b5cea
remove stub travis func
aktech Aug 28, 2023
d4a594c
undo mode change
aktech Aug 28, 2023
dee3843
Undo removal of github actions template
jaimergp Sep 8, 2023
c66aa34
Merge branch 'main' into gha-self-hosted-cirun
aktech Sep 8, 2023
655aec2
pre-commit fixes
aktech Sep 8, 2023
cf9e9bf
not available for travis
aktech Sep 8, 2023
589fb8a
add a note about getting installation id
aktech Sep 8, 2023
117fc1e
cache cirun client
aktech Sep 8, 2023
c02cbdd
add type hints, docs
aktech Sep 8, 2023
e7257d7
set policy args to None by default
aktech Sep 8, 2023
3876bbf
set default value for cirun_resources to iterable
aktech Sep 8, 2023
85390fa
make polcy args as optional
aktech Sep 8, 2023
2204933
precommit fixes
aktech Sep 8, 2023
357e594
Update conda_smithy/cirun_utils.py
aktech Sep 8, 2023
97456f4
remove reference to .cirun.yml template
aktech Sep 8, 2023
f42717c
add link to client docs
aktech Sep 8, 2023
ab740b8
remove cirun_runners from forge config
aktech Sep 8, 2023
36e986d
Merge branch 'main' of github.com:conda-forge/conda-smithy into gha-s…
jaimergp Oct 4, 2023
3472ee5
fix ppc64le name
jaimergp Oct 4, 2023
84cc27f
add self hosted timeout minutes
jaimergp Oct 4, 2023
af967c9
add more platform labels
jaimergp Oct 6, 2023
9405540
move labels config to configure_feedstock
jaimergp Oct 17, 2023
4c4c898
pre-commit
jaimergp Oct 17, 2023
98eb99c
Use recipe/conda_build_config.yaml for labels for more fine-grained c…
isuruf Oct 20, 2023
f480723
Merge branch 'main' of github.com:conda-forge/conda-smithy into gha-s…
isuruf Oct 20, 2023
3c07948
Keep github_actions_labels only if self-hosted is turned on
isuruf Oct 20, 2023
c29e9cd
Bring back free_disk_space
isuruf Oct 20, 2023
41d65b2
Support other orgs for cirun
isuruf Oct 23, 2023
d631fbd
pre-commit
isuruf Oct 23, 2023
549d7b9
Move import to top-level
isuruf Oct 31, 2023
83079c4
fix cirun installation id
isuruf Oct 31, 2023
fc4a0b0
formatting
isuruf Oct 31, 2023
87f7004
Add news entry
isuruf Oct 31, 2023
a72ca1e
Fix default values
isuruf Oct 31, 2023
20252d3
fix news
isuruf Oct 31, 2023
e50cf3a
list of lists for zipping
isuruf Oct 31, 2023
9ac2df7
Merge branch 'main' of github.com:conda-forge/conda-smithy into gha-s…
isuruf Nov 6, 2023
2bbcd4d
change default value of cancel_in_progress
isuruf Nov 6, 2023
64e0e49
Merge branch 'main' of github.com:conda-forge/conda-smithy into gha-s…
isuruf Nov 6, 2023
62d2a4b
pre-commit
isuruf Nov 6, 2023
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
82 changes: 82 additions & 0 deletions conda_smithy/cirun_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
See http://py.cirun.io/api.html for cirun client docs
"""
import os
from functools import lru_cache
from typing import List, Dict, Any, Optional

from cirun import Cirun
from .github import gh_token, Github


@lru_cache
def get_cirun_installation_id(owner: str) -> int:
# This ID needs a token with admin: org privileges.
# Hard-code instead for easier use.
if owner == "conda-forge":
return 18453316
else:
gh = Github(gh_token)
user = gh.get_user()
if user.login == owner:
user_or_org = user
else:
user_or_org = gh.get_organization(owner)
for inst in user_or_org.get_installations:
if inst.raw_data["app_slug"] == "cirun-application":
return inst.app_id
raise ValueError(f"cirun not found for owner {owner}")


def enable_cirun_for_project(owner: str, repo: str) -> Dict[str, Any]:
"""Enable the cirun.io Github Application for a particular repository."""
print(f"Enabling cirun for {owner}/{repo} ...")
cirun = _get_cirun_client()
return cirun.set_repo(
f"{owner}/{repo}", installation_id=get_cirun_installation_id(owner)
)


def add_repo_to_cirun_resource(
owner: str,
repo: str,
resource: str,
cirun_policy_args: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""Grant access to a cirun resource to a particular repository, with a particular policy."""
cirun = _get_cirun_client()
policy_args: Optional[Dict[str, Any]] = None
if cirun_policy_args and "pull_request" in cirun_policy_args:
policy_args = {"pull_request": True}
print(
f"Adding repo {owner}/{repo} to resource {resource} with policy_args: {policy_args}"
)
response = cirun.add_repo_to_resources(
owner,
repo,
resources=[resource],
teams=[repo],
policy_args=policy_args,
)
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
print(f"response: {response} | {response.json().keys()}")
return response


def remove_repo_from_cirun_resource(owner: str, repo: str, resource: str):
"""Revoke access to a cirun resource to a particular repository, with a particular policy."""
cirun = _get_cirun_client()
print(f"Removing repo {owner}/{repo} from resource {resource}.")
response = cirun.remove_repo_from_resources(owner, repo, [resource])
print(f"response: {response} | {response.json().keys()}")
return response


@lru_cache
def _get_cirun_client() -> Cirun:
xhochy marked this conversation as resolved.
Show resolved Hide resolved
try:
return Cirun()
except KeyError:
raise RuntimeError(
"You must have CIRUN_API_KEY defined to do Cirun CI registration. "
"This requirement can be overriden by specifying `--without-cirun`"
)
61 changes: 59 additions & 2 deletions conda_smithy/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import conda # noqa
import conda_build.api
from conda_build.metadata import MetaData

import conda_smithy.cirun_utils
from conda_smithy.utils import get_feedstock_name_from_meta, merge_dict
from ruamel.yaml import YAML

Expand Down Expand Up @@ -208,7 +210,7 @@ def __init__(self, parser):
# conda-smithy register-ci ./
super(RegisterCI, self).__init__(
parser,
"Register a feedstock at the CI " "services which do the builds.",
"Register a feedstock at the CI services which do the builds.",
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
)
scp = self.subcommand_parser
scp.add_argument(
Expand Down Expand Up @@ -238,6 +240,7 @@ def __init__(self, parser):
"Appveyor",
"Drone",
"Webservice",
"Cirun",
]:
scp.add_argument(
"--without-{}".format(ci.lower()),
Expand All @@ -257,6 +260,23 @@ def __init__(self, parser):
action="append",
help="drone server URL to register this repo. multiple values allowed",
)
scp.add_argument(
"--cirun-resources",
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
default=[],
action="append",
help="cirun resources to enable for this repo. multiple values allowed",
)
scp.add_argument(
"--cirun-policy-args",
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
action="append",
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
help="extra arguments for cirun policy to create for this repo. multiple values allowed",
)
scp.add_argument(
"--remove",
action="store_true",
help="Revoke access to the configured CI services. "
"Only available for Cirun for now",
)

def __call__(self, args):
from conda_smithy import ci_register
Expand All @@ -278,7 +298,19 @@ def __call__(self, args):
)

print("CI Summary for {}/{} (can take ~30s):".format(owner, repo))

if args.remove and any(
[
args.azure,
args.circle,
args.appveyor,
args.drone,
args.webservice,
args.anaconda_token,
]
):
raise RuntimeError(
"The --remove flag is only supported for Cirun for now"
)
if not args.anaconda_token:
print(
"Warning: By not registering an Anaconda/Binstar token"
Expand Down Expand Up @@ -345,6 +377,31 @@ def __call__(self, args):
else:
print("Drone registration disabled.")

if args.cirun:
print("Cirun Registration")
if args.remove:
if args.cirun_resources:
to_remove = args.cirun_resources
else:
to_remove = ["*"]

print(f"Cirun Registration: resources to remove: {to_remove}")
for resource in to_remove:
conda_smithy.cirun_utils.remove_repo_from_cirun_resource(
owner, repo, resource
)
else:
print(
f"Cirun Registration: resources to add to: {owner}/{repo}"
)
conda_smithy.cirun_utils.enable_cirun_for_project(owner, repo)
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
for resource in args.cirun_resources:
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
conda_smithy.cirun_utils.add_repo_to_cirun_resource(
owner, repo, resource, args.cirun_policy_args
)
else:
print("Cirun registration disabled.")

if args.webservice:
ci_register.add_conda_forge_webservice_hooks(owner, repo)
else:
Expand Down
102 changes: 96 additions & 6 deletions conda_smithy/configure_feedstock.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ def _collapse_subpackage_variants(
if not is_noarch:
always_keep_keys.add("target_platform")

if forge_config["github_actions"]["self_hosted"]:
always_keep_keys.add("github_actions_labels")

all_used_vars.update(always_keep_keys)
all_used_vars.update(top_level_vars)

Expand Down Expand Up @@ -695,7 +698,6 @@ def _render_ci_provider(
channel_target.startswith("conda-forge ")
and provider_name == "github_actions"
and not forge_config["github_actions"]["self_hosted"]
and os.path.basename(forge_dir) not in SERVICE_FEEDSTOCKS
Copy link
Member

Choose a reason for hiding this comment

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

@jaimergp why was this line removed?

Copy link
Member

Choose a reason for hiding this comment

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

I think we want it to be self-hosted or in service

):
raise RuntimeError(
"Using github_actions as the CI provider inside "
Expand Down Expand Up @@ -1266,6 +1268,83 @@ def render_appveyor(jinja_env, forge_config, forge_dir, return_metadata=False):
def _github_actions_specific_setup(
jinja_env, forge_config, forge_dir, platform
):

# Handle GH-hosted and self-hosted runners runs-on config
# Do it before the deepcopy below so these changes can be used by the
# .github/worfkflows/conda-build.yml template
runs_on = {
"osx-64": {
"os": "macos",
"self_hosted_labels": ("macOS", "x64"),
},
"osx-arm64": {
"os": "macos",
"self_hosted_labels": ("macOS", "arm64"),
},
"linux-64": {
"os": "ubuntu",
"self_hosted_labels": ("linux", "x64"),
},
"linux-aarch64": {
"os": "ubuntu",
"self_hosted_labels": ("linux", "ARM64"),
},
"win-64": {
"os": "windows",
"self_hosted_labels": ("windows", "x64"),
},
"win-arm64": {
"os": "windows",
"self_hosted_labels": ("windows", "ARM64"),
},
}
for data in forge_config["configs"]:
if not data["build_platform"].startswith(platform):
continue
# This Github Actions specific configs are prefixed with "gha_"
# because we are not deepcopying the data dict intentionally
# so it can be used in the general "render_github_actions" function
# This avoid potential collisions with other CI providers :crossed_fingers:
data["gha_os"] = runs_on[data["build_platform"]]["os"]
data["gha_with_gpu"] = False

self_hosted_default = list(
runs_on[data["build_platform"]]["self_hosted_labels"]
)
self_hosted_default += ["self-hosted"]
hosted_default = [data["gha_os"] + "-latest"]

labels_default = (
["hosted"]
if forge_config["github_actions"]["self_hosted"]
else ["self-hosted"]
)
labels = conda_build.utils.ensure_list(
data["config"].get("github_actions_labels", [labels_default])[0]
)

if len(labels) == 1 and labels[0] == "hosted":
labels = hosted_default
elif len(labels) == 1 and labels[0] in "self-hosted":
labels = self_hosted_default
else:
# Prepend the required ones
labels += self_hosted_default

if forge_config["github_actions"]["self_hosted"]:
data["gha_runs_on"] = []
# labels provided in conda-forge.yml
for label in labels:
if label.startswith("cirun-"):
label += (
"--${{ github.run_id }}-" + data["short_config_name"]
)
if "gpu" in label.lower():
data["gha_with_gpu"] = True
data["gha_runs_on"].append(label)
else:
data["gha_runs_on"] = hosted_default

build_setup = _get_build_setup_line(forge_dir, platform, forge_config)

if platform == "linux":
Expand All @@ -1288,11 +1367,13 @@ def _github_actions_specific_setup(
".scripts/run_win_build.bat",
],
}
if forge_config["github_actions"]["store_build_artifacts"]:
for tmpls in platform_templates.values():
tmpls.append(".scripts/create_conda_build_artifacts.sh")

template_files = platform_templates.get(platform, [])

# Templates for all platforms
if forge_config["github_actions"]["store_build_artifacts"]:
template_files.append(".scripts/create_conda_build_artifacts.sh")

_render_template_exe_files(
forge_config=forge_config,
jinja_env=jinja_env,
Expand All @@ -1307,7 +1388,7 @@ def render_github_actions(
target_path = os.path.join(
forge_dir, ".github", "workflows", "conda-build.yml"
)
template_filename = "github-actions.tmpl"
template_filename = "github-actions.yml.tmpl"
fast_finish_text = ""

(
Expand All @@ -1317,7 +1398,7 @@ def render_github_actions(
upload_packages,
) = _get_platforms_of_provider("github_actions", forge_config)

logger.debug("github platforms retreived")
logger.debug("github platforms retrieved")

remove_file_or_dir(target_path)
return _render_ci_provider(
Expand Down Expand Up @@ -1841,6 +1922,9 @@ def _load_forge_config(forge_dir, exclusive_config_file, forge_yml=None):
},
"github_actions": {
"self_hosted": False,
"triggers": [],
"timeout_minutes": 360,
"cancel_in_progress": True,
# Set maximum parallel jobs
"max_parallel": None,
# Toggle creating artifacts for conda build_artifacts dir
Expand Down Expand Up @@ -2001,6 +2085,12 @@ def _load_forge_config(forge_dir, exclusive_config_file, forge_yml=None):
if config["test"] is None:
config["test"] = "all"

if not config["github_actions"]["triggers"]:
self_hosted = config["github_actions"]["self_hosted"]
config["github_actions"]["triggers"] = (
["push"] if self_hosted else ["push", "pull_request"]
)

# An older conda-smithy used to have some files which should no longer exist,
# remove those now.
old_files = [
Expand Down
Loading