2025-01-24T00:45:51.6345932Z Current runner version: '2.321.0' 2025-01-24T00:45:51.6369424Z ##[group]Operating System 2025-01-24T00:45:51.6370135Z Ubuntu 2025-01-24T00:45:51.6370738Z 24.04.1 2025-01-24T00:45:51.6371240Z LTS 2025-01-24T00:45:51.6371675Z ##[endgroup] 2025-01-24T00:45:51.6372313Z ##[group]Runner Image 2025-01-24T00:45:51.6372879Z Image: ubuntu-24.04 2025-01-24T00:45:51.6373347Z Version: 20250120.5.0 2025-01-24T00:45:51.6374607Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250120.5/images/ubuntu/Ubuntu2404-Readme.md 2025-01-24T00:45:51.6375977Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250120.5 2025-01-24T00:45:51.6376807Z ##[endgroup] 2025-01-24T00:45:51.6377407Z ##[group]Runner Image Provisioner 2025-01-24T00:45:51.6377992Z 2.0.417.1 2025-01-24T00:45:51.6378407Z ##[endgroup] 2025-01-24T00:45:51.6379510Z ##[group]GITHUB_TOKEN Permissions 2025-01-24T00:45:51.6381476Z Contents: read 2025-01-24T00:45:51.6382119Z Metadata: read 2025-01-24T00:45:51.6382833Z Packages: read 2025-01-24T00:45:51.6383454Z ##[endgroup] 2025-01-24T00:45:51.6386780Z Secret source: Actions 2025-01-24T00:45:51.6387513Z Prepare workflow directory 2025-01-24T00:45:51.6870797Z Prepare all required actions 2025-01-24T00:45:51.6925186Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (b2c89bc115123aea8e075e882ee121537ec92f89) 2025-01-24T00:45:51.6929949Z ##[group] Inputs 2025-01-24T00:45:51.6930544Z check_experiments: 2025-01-24T00:45:51.6931236Z triggering_actor: pytorch-bot[bot] 2025-01-24T00:45:51.6931851Z issue_owner: 2025-01-24T00:45:51.6932359Z curr_branch: ciflow/trunk/145539 2025-01-24T00:45:51.6933051Z curr_ref_type: tag 2025-01-24T00:45:51.6933537Z issue_number: 5132 2025-01-24T00:45:51.6934061Z ##[endgroup] 2025-01-24T00:45:51.6934920Z Complete job name: get-label-type / runner-determinator 2025-01-24T00:45:51.7639700Z ##[group]Run cat < runner_determinator.py 2025-01-24T00:45:51.7641255Z cat < runner_determinator.py 2025-01-24T00:45:51.7641842Z # flake8: noqa: G004 2025-01-24T00:45:51.7642325Z  2025-01-24T00:45:51.7642995Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-01-24T00:45:51.7643981Z # must be kept in sync. You can do it easily by running the following command: 2025-01-24T00:45:51.7645049Z # python .github/scripts/update_runner_determinator.py 2025-01-24T00:45:51.7645668Z  2025-01-24T00:45:51.7646012Z """ 2025-01-24T00:45:51.7646627Z This runner determinator is used to determine which set of runners to run a 2025-01-24T00:45:51.7647534Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-01-24T00:45:51.7648524Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-01-24T00:45:51.7649377Z of which runners should be used to run which job. 2025-01-24T00:45:51.7649951Z  2025-01-24T00:45:51.7650541Z The configuration has two parts, the settings and a list of opted-in users, 2025-01-24T00:45:51.7651473Z separated by a line containing "---". If the line is not present, the 2025-01-24T00:45:51.7652376Z settings are considered to be empty with only the second part, the user 2025-01-24T00:45:51.7653093Z list, defined. 2025-01-24T00:45:51.7653507Z  2025-01-24T00:45:51.7654077Z The first part is a YAML block that defines the rollout settings. This can be 2025-01-24T00:45:51.7655153Z used to define any settings that are needed to determine which runners to use. 2025-01-24T00:45:51.7656047Z It's fields are defined by the RolloutSettings class below. 2025-01-24T00:45:51.7656665Z  2025-01-24T00:45:51.7657260Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-01-24T00:45:51.7658166Z The user list is also a comma separated list of additional features or 2025-01-24T00:45:51.7659254Z experiments which the user could be opted in to. 2025-01-24T00:45:51.7659834Z  2025-01-24T00:45:51.7660244Z The user list has the following rules: 2025-01-24T00:45:51.7660759Z  2025-01-24T00:45:51.7661291Z - Users are GitHub usernames, which must start with the @ prefix 2025-01-24T00:45:51.7662165Z - Each user is also a comma-separated list of features/experiments to enable 2025-01-24T00:45:51.7662980Z - A "#" prefix opts the user out of all experiments 2025-01-24T00:45:51.7663546Z  2025-01-24T00:45:51.7663909Z Example config: 2025-01-24T00:45:51.7664528Z  # A list of experiments that can be opted into. 2025-01-24T00:45:51.7665250Z  # This defines the behavior they'll induce when opted into. 2025-01-24T00:45:51.7665945Z  # Expected syntax is: 2025-01-24T00:45:51.7666644Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-01-24T00:45:51.7667673Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-01-24T00:45:51.7668483Z  2025-01-24T00:45:51.7668884Z  experiments: 2025-01-24T00:45:51.7669296Z  lf: 2025-01-24T00:45:51.7669699Z  rollout_percent: 25 2025-01-24T00:45:51.7670192Z  all_branches: false 2025-01-24T00:45:51.7670679Z  default: true 2025-01-24T00:45:51.7671149Z  --- 2025-01-24T00:45:51.7671542Z  2025-01-24T00:45:51.7671905Z  # Opt-ins: 2025-01-24T00:45:51.7672555Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-01-24T00:45:51.7673599Z  # and specifying experiments to enable in a comma-separated list. 2025-01-24T00:45:51.7674706Z  # To always opt out of an experiment, prefix it with a "-". 2025-01-24T00:45:51.7675478Z  # Experiments should be from the above list. 2025-01-24T00:45:51.7676046Z  2025-01-24T00:45:51.7676434Z  @User1,-lf,split_build 2025-01-24T00:45:51.7676987Z  @User2,lf 2025-01-24T00:45:51.7677424Z  @User3,split_build 2025-01-24T00:45:51.7677878Z """ 2025-01-24T00:45:51.7678239Z  2025-01-24T00:45:51.7678603Z import json 2025-01-24T00:45:51.7679006Z import logging 2025-01-24T00:45:51.7679430Z import os 2025-01-24T00:45:51.7679829Z import random 2025-01-24T00:45:51.7680239Z import re 2025-01-24T00:45:51.7680643Z import sys 2025-01-24T00:45:51.7681143Z from argparse import ArgumentParser 2025-01-24T00:45:51.7681712Z from functools import lru_cache 2025-01-24T00:45:51.7682247Z from logging import LogRecord 2025-01-24T00:45:51.7682990Z from typing import Any, Dict, FrozenSet, Iterable, List, NamedTuple, Set, Tuple 2025-01-24T00:45:51.7683816Z from urllib.request import Request, urlopen 2025-01-24T00:45:51.7684467Z  2025-01-24T00:45:51.7684829Z import yaml 2025-01-24T00:45:51.7685261Z from github import Auth, Github 2025-01-24T00:45:51.7685793Z from github.Issue import Issue 2025-01-24T00:45:51.7686291Z  2025-01-24T00:45:51.7686639Z  2025-01-24T00:45:51.7687073Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-01-24T00:45:51.7687797Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-01-24T00:45:51.7688695Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-01-24T00:45:51.7689402Z  2025-01-24T00:45:51.7689848Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-01-24T00:45:51.7690451Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-01-24T00:45:51.7691036Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-01-24T00:45:51.7691854Z OPT_OUT_LABEL = "no-runner-experiments" 2025-01-24T00:45:51.7692394Z  2025-01-24T00:45:51.7692796Z SETTING_EXPERIMENTS = "experiments" 2025-01-24T00:45:51.7693307Z  2025-01-24T00:45:51.7693682Z LF_FLEET_EXPERIMENT = "lf" 2025-01-24T00:45:51.7694182Z CANARY_FLEET_SUFFIX = ".c" 2025-01-24T00:45:51.7694761Z  2025-01-24T00:45:51.7695111Z  2025-01-24T00:45:51.7695489Z class Experiment(NamedTuple): 2025-01-24T00:45:51.7696013Z  rollout_perc: float = ( 2025-01-24T00:45:51.7696715Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-01-24T00:45:51.7697404Z  ) 2025-01-24T00:45:51.7697808Z  all_branches: bool = ( 2025-01-24T00:45:51.7698495Z  False # If True, the experiment is also enabled on the exception branches 2025-01-24T00:45:51.7699213Z  ) 2025-01-24T00:45:51.7699604Z  default: bool = ( 2025-01-24T00:45:51.7700231Z  True # If True, the experiment is enabled by default for all queries 2025-01-24T00:45:51.7700882Z  ) 2025-01-24T00:45:51.7701251Z  2025-01-24T00:45:51.7701640Z  # Add more fields as needed 2025-01-24T00:45:51.7702132Z  2025-01-24T00:45:51.7702488Z  2025-01-24T00:45:51.7702869Z class Settings(NamedTuple): 2025-01-24T00:45:51.7703351Z  """ 2025-01-24T00:45:51.7703871Z  Settings for the experiments that can be opted into. 2025-01-24T00:45:51.7704559Z  """ 2025-01-24T00:45:51.7704942Z  2025-01-24T00:45:51.7705357Z  experiments: Dict[str, Experiment] = {} 2025-01-24T00:45:51.7705880Z  2025-01-24T00:45:51.7706230Z  2025-01-24T00:45:51.7706789Z class ColorFormatter(logging.Formatter): 2025-01-24T00:45:51.7707480Z  """Color codes the log messages based on the log level""" 2025-01-24T00:45:51.7708105Z  2025-01-24T00:45:51.7708468Z  COLORS = { 2025-01-24T00:45:51.7708922Z  "WARNING": "\033[33m", # Yellow 2025-01-24T00:45:51.7709464Z  "ERROR": "\033[31m", # Red 2025-01-24T00:45:51.7709985Z  "CRITICAL": "\033[31m", # Red 2025-01-24T00:45:51.7710510Z  "INFO": "\033[0m", # Reset 2025-01-24T00:45:51.7711035Z  "DEBUG": "\033[0m", # Reset 2025-01-24T00:45:51.7711611Z  } 2025-01-24T00:45:51.7711986Z  2025-01-24T00:45:51.7712426Z  def format(self, record: LogRecord) -> str: 2025-01-24T00:45:51.7713217Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-01-24T00:45:51.7714015Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-01-24T00:45:51.7715142Z  return super().format(record) 2025-01-24T00:45:51.7715760Z  2025-01-24T00:45:51.7716115Z  2025-01-24T00:45:51.7716510Z handler = logging.StreamHandler() 2025-01-24T00:45:51.7717283Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-01-24T00:45:51.7718013Z  2025-01-24T00:45:51.7718482Z log = logging.getLogger(os.path.basename(__file__)) 2025-01-24T00:45:51.7719095Z log.addHandler(handler) 2025-01-24T00:45:51.7719578Z log.setLevel(logging.INFO) 2025-01-24T00:45:51.7720059Z  2025-01-24T00:45:51.7720408Z  2025-01-24T00:45:51.7720875Z def set_github_output(key: str, value: str) -> None: 2025-01-24T00:45:51.7721473Z  """ 2025-01-24T00:45:51.7722023Z  Defines outputs of the github action that invokes this script 2025-01-24T00:45:51.7722663Z  """ 2025-01-24T00:45:51.7723067Z  if not GITHUB_OUTPUT: 2025-01-24T00:45:51.7724172Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-01-24T00:45:51.7725723Z  log.warning( 2025-01-24T00:45:51.7726623Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-01-24T00:45:51.7727548Z  ) 2025-01-24T00:45:51.7728016Z  print(f"::set-output name={key}::{value}") 2025-01-24T00:45:51.7728574Z  return 2025-01-24T00:45:51.7728985Z  2025-01-24T00:45:51.7729384Z  with open(GITHUB_OUTPUT, "a") as f: 2025-01-24T00:45:51.7729984Z  log.info(f"Setting output: {key}='{value}'") 2025-01-24T00:45:51.7730577Z  f.write(f"{key}={value}\n") 2025-01-24T00:45:51.7731085Z  2025-01-24T00:45:51.7731432Z  2025-01-24T00:45:51.7731959Z def _str_comma_separated_to_set(value: str) -> FrozenSet[str]: 2025-01-24T00:45:51.7732626Z  return frozenset( 2025-01-24T00:45:51.7733292Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-01-24T00:45:51.7733982Z  ) 2025-01-24T00:45:51.7734464Z  2025-01-24T00:45:51.7734820Z  2025-01-24T00:45:51.7735197Z def parse_args() -> Any: 2025-01-24T00:45:51.7735813Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-01-24T00:45:51.7736718Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-01-24T00:45:51.7737512Z  parser.add_argument( 2025-01-24T00:45:51.7738014Z  "--github-issue-repo", 2025-01-24T00:45:51.7738526Z  type=str, 2025-01-24T00:45:51.7738974Z  required=False, 2025-01-24T00:45:51.7739597Z  default="pytorch/test-infra", 2025-01-24T00:45:51.7740177Z  help="GitHub repo to get the issue", 2025-01-24T00:45:51.7740707Z  ) 2025-01-24T00:45:51.7741101Z  parser.add_argument( 2025-01-24T00:45:51.7741576Z  "--github-repo", 2025-01-24T00:45:51.7742052Z  type=str, 2025-01-24T00:45:51.7742492Z  required=True, 2025-01-24T00:45:51.7743007Z  help="GitHub repo where CI is running", 2025-01-24T00:45:51.7743541Z  ) 2025-01-24T00:45:51.7743933Z  parser.add_argument( 2025-01-24T00:45:51.7744687Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-01-24T00:45:51.7745361Z  ) 2025-01-24T00:45:51.7745742Z  parser.add_argument( 2025-01-24T00:45:51.7746410Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-01-24T00:45:51.7747096Z  ) 2025-01-24T00:45:51.7747489Z  parser.add_argument( 2025-01-24T00:45:51.7748159Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-01-24T00:45:51.7748867Z  ) 2025-01-24T00:45:51.7749255Z  parser.add_argument( 2025-01-24T00:45:51.7749970Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-01-24T00:45:51.7750691Z  ) 2025-01-24T00:45:51.7751081Z  parser.add_argument( 2025-01-24T00:45:51.7751599Z  "--github-ref-type", 2025-01-24T00:45:51.7752081Z  type=str, 2025-01-24T00:45:51.7752550Z  required=True, 2025-01-24T00:45:51.7753109Z  help="Current GitHub ref type, branch or tag", 2025-01-24T00:45:51.7753670Z  ) 2025-01-24T00:45:51.7754046Z  parser.add_argument( 2025-01-24T00:45:51.7754819Z  "--eligible-experiments", 2025-01-24T00:45:51.7755396Z  type=_str_comma_separated_to_set, 2025-01-24T00:45:51.7755929Z  required=False, 2025-01-24T00:45:51.7756394Z  default="", 2025-01-24T00:45:51.7757429Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-01-24T00:45:51.7758375Z  ) 2025-01-24T00:45:51.7758762Z  parser.add_argument( 2025-01-24T00:45:51.7759236Z  "--pr-number", 2025-01-24T00:45:51.7759692Z  type=str, 2025-01-24T00:45:51.7760134Z  required=False, 2025-01-24T00:45:51.7760601Z  default="", 2025-01-24T00:45:51.7761161Z  help="the optional PR number where this is run", 2025-01-24T00:45:51.7761730Z  ) 2025-01-24T00:45:51.7762103Z  2025-01-24T00:45:51.7762488Z  return parser.parse_args() 2025-01-24T00:45:51.7762986Z  2025-01-24T00:45:51.7763339Z  2025-01-24T00:45:51.7763781Z def get_gh_client(github_token: str) -> Github: 2025-01-24T00:45:51.7764509Z  auth = Auth.Token(github_token) 2025-01-24T00:45:51.7765063Z  return Github(auth=auth) 2025-01-24T00:45:51.7765530Z  2025-01-24T00:45:51.7765873Z  2025-01-24T00:45:51.7766382Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: 2025-01-24T00:45:51.7767037Z  repo = gh.get_repo(repo) 2025-01-24T00:45:51.7767581Z  return repo.get_issue(number=issue_num) 2025-01-24T00:45:51.7768109Z  2025-01-24T00:45:51.7768452Z  2025-01-24T00:45:51.7768831Z def get_potential_pr_author( 2025-01-24T00:45:51.7769518Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-01-24T00:45:51.7770203Z ) -> str: 2025-01-24T00:45:51.7770762Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-01-24T00:45:51.7771708Z  # Fetch the actual username from the original PR. The PR number is 2025-01-24T00:45:51.7772496Z  # embedded in the tag name: ciflow// 2025-01-24T00:45:51.7773078Z  2025-01-24T00:45:51.7773472Z  gh = get_gh_client(github_token) 2025-01-24T00:45:51.7773975Z  2025-01-24T00:45:51.7774557Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-01-24T00:45:51.7775219Z  split_tag = ref_name.split("/") 2025-01-24T00:45:51.7775737Z  if ( 2025-01-24T00:45:51.7776149Z  len(split_tag) == 3 2025-01-24T00:45:51.7776669Z  and split_tag[0] == "ciflow" 2025-01-24T00:45:51.7777215Z  and split_tag[2].isnumeric() 2025-01-24T00:45:51.7777718Z  ): 2025-01-24T00:45:51.7778146Z  pr_number = split_tag[2] 2025-01-24T00:45:51.7778651Z  try: 2025-01-24T00:45:51.7779130Z  repository = gh.get_repo(repo) 2025-01-24T00:45:51.7779776Z  pull = repository.get_pull(number=int(pr_number)) 2025-01-24T00:45:51.7780406Z  except Exception as e: 2025-01-24T00:45:51.7780946Z  raise Exception( # noqa: TRY002 2025-01-24T00:45:51.7781644Z  f"issue with pull request {pr_number} from repo {repository}" 2025-01-24T00:45:51.7782296Z  ) from e 2025-01-24T00:45:51.7782786Z  return pull.user.login 2025-01-24T00:45:51.7783405Z  # In all other cases, return the original input username 2025-01-24T00:45:51.7784019Z  return username 2025-01-24T00:45:51.7784548Z  2025-01-24T00:45:51.7784895Z  2025-01-24T00:45:51.7785342Z def is_exception_branch(branch: str) -> bool: 2025-01-24T00:45:51.7785879Z  """ 2025-01-24T00:45:51.7786596Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-01-24T00:45:51.7787378Z  """ 2025-01-24T00:45:51.7788093Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-01-24T00:45:51.7788772Z  2025-01-24T00:45:51.7789117Z  2025-01-24T00:45:51.7789508Z def load_yaml(yaml_text: str) -> Any: 2025-01-24T00:45:51.7790026Z  try: 2025-01-24T00:45:51.7790444Z  data = yaml.safe_load(yaml_text) 2025-01-24T00:45:51.7790964Z  return data 2025-01-24T00:45:51.7791429Z  except yaml.YAMLError: 2025-01-24T00:45:51.7791946Z  log.exception("Error loading YAML") 2025-01-24T00:45:51.7792470Z  raise 2025-01-24T00:45:51.7792863Z  2025-01-24T00:45:51.7793204Z  2025-01-24T00:45:51.7793818Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> Tuple[str, str]: 2025-01-24T00:45:51.7794817Z  """ 2025-01-24T00:45:51.7795478Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-01-24T00:45:51.7796230Z  2025-01-24T00:45:51.7796771Z  If the issue body contains "---" then the text above that is the settings 2025-01-24T00:45:51.7797537Z  and the text below is the list of opted in users. 2025-01-24T00:45:51.7798101Z  2025-01-24T00:45:51.7798670Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-01-24T00:45:51.7799406Z  """ 2025-01-24T00:45:51.7799879Z  rollout_state_parts = rollout_state.split("---") 2025-01-24T00:45:51.7800482Z  if len(rollout_state_parts) >= 2: 2025-01-24T00:45:51.7801118Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-01-24T00:45:51.7801716Z  else: 2025-01-24T00:45:51.7802274Z  return "", rollout_state 2025-01-24T00:45:51.7802768Z  2025-01-24T00:45:51.7803113Z  2025-01-24T00:45:51.7803564Z class UserOptins(Dict[str, List[str]]): 2025-01-24T00:45:51.7804096Z  """ 2025-01-24T00:45:51.7804757Z  Dictionary of users with a list of features they have opted into 2025-01-24T00:45:51.7805402Z  """ 2025-01-24T00:45:51.7805772Z  2025-01-24T00:45:51.7806108Z  2025-01-24T00:45:51.7806646Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-01-24T00:45:51.7807309Z  """ 2025-01-24T00:45:51.7808044Z  Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-01-24T00:45:51.7808865Z  2025-01-24T00:45:51.7809672Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-01-24T00:45:51.7810687Z  - Example line: "@User1,lf,split_build" 2025-01-24T00:45:51.7811394Z  - A "#" prefix indicates the user is opted out of all experiments 2025-01-24T00:45:51.7812023Z  2025-01-24T00:45:51.7812367Z  2025-01-24T00:45:51.7812700Z  """ 2025-01-24T00:45:51.7813093Z  optins = UserOptins() 2025-01-24T00:45:51.7813625Z  for user in user_optin_text.split("\n"): 2025-01-24T00:45:51.7814205Z  user = user.strip("\r\n\t -") 2025-01-24T00:45:51.7814904Z  if not user or not user.startswith("@"): 2025-01-24T00:45:51.7815481Z  # Not a valid user. Skip 2025-01-24T00:45:51.7815999Z  continue 2025-01-24T00:45:51.7816421Z  2025-01-24T00:45:51.7816775Z  if user: 2025-01-24T00:45:51.7817256Z  usr_name = user.split(",")[0].strip("@") 2025-01-24T00:45:51.7817964Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-01-24T00:45:51.7818599Z  2025-01-24T00:45:51.7818952Z  return optins 2025-01-24T00:45:51.7819498Z  2025-01-24T00:45:51.7819843Z  2025-01-24T00:45:51.7820355Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-01-24T00:45:51.7820974Z  """ 2025-01-24T00:45:51.7821411Z  Check if the experiment name is valid. 2025-01-24T00:45:51.7821942Z  A valid name: 2025-01-24T00:45:51.7822645Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-01-24T00:45:51.7823597Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-01-24T00:45:51.7824411Z  - Cannot contain spaces 2025-01-24T00:45:51.7824903Z  """ 2025-01-24T00:45:51.7825274Z  2025-01-24T00:45:51.7825740Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-01-24T00:45:51.7826467Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-01-24T00:45:51.7827074Z  2025-01-24T00:45:51.7827430Z  if valid: 2025-01-24T00:45:51.7827837Z  return True 2025-01-24T00:45:51.7828265Z  2025-01-24T00:45:51.7828616Z  log.error( 2025-01-24T00:45:51.7830047Z  f"Invalid experiment name: {experiment_name}. Experiment names should only contain alphanumeric characters, '_', and '-'. They cannot contain spaces, and the special characters '_' and '-' cannot be the first or last characters." 2025-01-24T00:45:51.7831544Z  ) 2025-01-24T00:45:51.7831916Z  return False 2025-01-24T00:45:51.7832320Z  2025-01-24T00:45:51.7832658Z  2025-01-24T00:45:51.7833177Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-01-24T00:45:51.7833808Z  """ 2025-01-24T00:45:51.7834633Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-01-24T00:45:51.7835363Z  """ 2025-01-24T00:45:51.7835725Z  try: 2025-01-24T00:45:51.7836120Z  if settings_text: 2025-01-24T00:45:51.7836877Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-01-24T00:45:51.7837676Z  # for easy reading 2025-01-24T00:45:51.7838498Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-01-24T00:45:51.7839398Z  # the backtick character in shell commands. 2025-01-24T00:45:51.7840025Z  backtick = chr(96) # backtick character 2025-01-24T00:45:51.7840718Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-01-24T00:45:51.7841418Z  settings = load_yaml(settings_text) 2025-01-24T00:45:51.7841945Z  2025-01-24T00:45:51.7842548Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-01-24T00:45:51.7843305Z  experiments = {} 2025-01-24T00:45:51.7843774Z  2025-01-24T00:45:51.7844425Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-01-24T00:45:51.7845211Z  if not is_valid_experiment_name(exp_name): 2025-01-24T00:45:51.7846321Z  # Exclude invalid experiments from the list. We log an error, but don't raise an exception so that other experiments can still be processed. 2025-01-24T00:45:51.7847359Z  continue 2025-01-24T00:45:51.7847841Z  2025-01-24T00:45:51.7848219Z  valid_settings = {} 2025-01-24T00:45:51.7848765Z  for setting in exp_settings: 2025-01-24T00:45:51.7849351Z  if setting not in Experiment._fields: 2025-01-24T00:45:51.7849920Z  log.warning( 2025-01-24T00:45:51.7850645Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-01-24T00:45:51.7851467Z  ) 2025-01-24T00:45:51.7851920Z  else: 2025-01-24T00:45:51.7852477Z  valid_settings[setting] = exp_settings[setting] 2025-01-24T00:45:51.7853046Z  2025-01-24T00:45:51.7853507Z  experiments[exp_name] = Experiment(**valid_settings) 2025-01-24T00:45:51.7854159Z  return Settings(experiments) 2025-01-24T00:45:51.7854768Z  2025-01-24T00:45:51.7855124Z  except Exception: 2025-01-24T00:45:51.7855646Z  log.exception("Failed to parse settings") 2025-01-24T00:45:51.7856190Z  2025-01-24T00:45:51.7856547Z  return Settings() 2025-01-24T00:45:51.7856984Z  2025-01-24T00:45:51.7857312Z  2025-01-24T00:45:51.7857772Z def parse_settings(rollout_state: str) -> Settings: 2025-01-24T00:45:51.7858358Z  """ 2025-01-24T00:45:51.7858821Z  Parse settings, if any, from the rollout state. 2025-01-24T00:45:51.7859370Z  2025-01-24T00:45:51.7859913Z  If the issue body contains "---" then the text above that is the settings 2025-01-24T00:45:51.7860680Z  and the text below is the list of opted in users. 2025-01-24T00:45:51.7861237Z  2025-01-24T00:45:51.7861837Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-01-24T00:45:51.7862565Z  """ 2025-01-24T00:45:51.7863137Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:45:51.7863928Z  return parse_settings_from_text(settings_text) 2025-01-24T00:45:51.7864579Z  2025-01-24T00:45:51.7865026Z  2025-01-24T00:45:51.7865491Z def parse_users(rollout_state: str) -> UserOptins: 2025-01-24T00:45:51.7866064Z  """ 2025-01-24T00:45:51.7866487Z  Parse users from the rollout state. 2025-01-24T00:45:51.7867006Z  2025-01-24T00:45:51.7867345Z  """ 2025-01-24T00:45:51.7867902Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:45:51.7868672Z  return parse_user_opt_in_from_text(users_text) 2025-01-24T00:45:51.7869228Z  2025-01-24T00:45:51.7869563Z  2025-01-24T00:45:51.7870186Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:45:51.7870941Z  """ 2025-01-24T00:45:51.7871384Z  Check if a user is opted into an experiment 2025-01-24T00:45:51.7871925Z  """ 2025-01-24T00:45:51.7872424Z  return experiment_name in user_optins.get(user, []) 2025-01-24T00:45:51.7872998Z  2025-01-24T00:45:51.7873342Z  2025-01-24T00:45:51.7873969Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:45:51.7874829Z  """ 2025-01-24T00:45:51.7875329Z  Check if a user explicitly opted out of an experiment 2025-01-24T00:45:51.7875916Z  """ 2025-01-24T00:45:51.7876455Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-01-24T00:45:51.7877166Z  experiment_optout = "-" + experiment_name 2025-01-24T00:45:51.7877830Z  if experiment_optout not in user_optins.get(user, []): 2025-01-24T00:45:51.7878429Z  return False 2025-01-24T00:45:51.7878862Z  2025-01-24T00:45:51.7879335Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-01-24T00:45:51.7879937Z  log.warning( 2025-01-24T00:45:51.7880774Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-01-24T00:45:51.7881646Z  ) 2025-01-24T00:45:51.7882173Z  2025-01-24T00:45:51.7882518Z  return True 2025-01-24T00:45:51.7882926Z  2025-01-24T00:45:51.7883256Z  2025-01-24T00:45:51.7883617Z def get_runner_prefix( 2025-01-24T00:45:51.7884087Z  rollout_state: str, 2025-01-24T00:45:51.7884685Z  workflow_requestors: Iterable[str], 2025-01-24T00:45:51.7885221Z  branch: str, 2025-01-24T00:45:51.7885764Z  eligible_experiments: FrozenSet[str] = frozenset(), 2025-01-24T00:45:51.7886380Z  is_canary: bool = False, 2025-01-24T00:45:51.7886859Z ) -> str: 2025-01-24T00:45:51.7887314Z  settings = parse_settings(rollout_state) 2025-01-24T00:45:51.7887916Z  user_optins = parse_users(rollout_state) 2025-01-24T00:45:51.7888444Z  2025-01-24T00:45:51.7888813Z  fleet_prefix = "" 2025-01-24T00:45:51.7889271Z  prefixes = [] 2025-01-24T00:45:51.7889957Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-01-24T00:45:51.7890912Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-01-24T00:45:51.7891639Z  log.info( 2025-01-24T00:45:51.7892349Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-01-24T00:45:51.7893094Z  ) 2025-01-24T00:45:51.7893507Z  continue 2025-01-24T00:45:51.7893928Z  2025-01-24T00:45:51.7894405Z  if eligible_experiments: 2025-01-24T00:45:51.7895004Z  if experiment_name not in eligible_experiments: 2025-01-24T00:45:51.7895657Z  exp_list = ", ".join(eligible_experiments) 2025-01-24T00:45:51.7896336Z  log.info( 2025-01-24T00:45:51.7897160Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-01-24T00:45:51.7897990Z  ) 2025-01-24T00:45:51.7898422Z  continue 2025-01-24T00:45:51.7898950Z  elif not experiment_settings.default: 2025-01-24T00:45:51.7899517Z  log.info( 2025-01-24T00:45:51.7900218Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-01-24T00:45:51.7900954Z  ) 2025-01-24T00:45:51.7901357Z  continue 2025-01-24T00:45:51.7901769Z  2025-01-24T00:45:51.7902241Z  # Is any workflow_requestor opted out to this experiment? 2025-01-24T00:45:51.7902860Z  opted_out_users = [ 2025-01-24T00:45:51.7903333Z  requestor 2025-01-24T00:45:51.7903838Z  for requestor in workflow_requestors 2025-01-24T00:45:51.7904619Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-01-24T00:45:51.7905257Z  ] 2025-01-24T00:45:51.7905636Z  2025-01-24T00:45:51.7906001Z  if opted_out_users: 2025-01-24T00:45:51.7906473Z  log.info( 2025-01-24T00:45:51.7907122Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-01-24T00:45:51.7907820Z  ) 2025-01-24T00:45:51.7908227Z  continue 2025-01-24T00:45:51.7908652Z  2025-01-24T00:45:51.7909117Z  # Is any workflow_requestor opted in to this experiment? 2025-01-24T00:45:51.7909728Z  opted_in_users = [ 2025-01-24T00:45:51.7910194Z  requestor 2025-01-24T00:45:51.7910692Z  for requestor in workflow_requestors 2025-01-24T00:45:51.7911368Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-01-24T00:45:51.7911992Z  ] 2025-01-24T00:45:51.7912519Z  2025-01-24T00:45:51.7912873Z  enabled = False 2025-01-24T00:45:51.7913347Z  if opted_in_users: 2025-01-24T00:45:51.7913825Z  log.info( 2025-01-24T00:45:51.7914550Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-01-24T00:45:51.7915234Z  ) 2025-01-24T00:45:51.7915652Z  enabled = True 2025-01-24T00:45:51.7916097Z  2025-01-24T00:45:51.7916514Z  elif experiment_settings.rollout_perc: 2025-01-24T00:45:51.7917347Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-01-24T00:45:51.7918288Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-01-24T00:45:51.7918931Z  log.info( 2025-01-24T00:45:51.7919831Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-01-24T00:45:51.7920741Z  ) 2025-01-24T00:45:51.7921176Z  enabled = True 2025-01-24T00:45:51.7921648Z  2025-01-24T00:45:51.7922010Z  if enabled: 2025-01-24T00:45:51.7922474Z  label = experiment_name 2025-01-24T00:45:51.7923262Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-01-24T00:45:51.7924825Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-01-24T00:45:51.7925739Z  # - If it's enabled, then we always list it's prefix first 2025-01-24T00:45:51.7926666Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-01-24T00:45:51.7927350Z  if is_canary: 2025-01-24T00:45:51.7927910Z  label += CANARY_FLEET_SUFFIX 2025-01-24T00:45:51.7928472Z  fleet_prefix = label 2025-01-24T00:45:51.7928978Z  else: 2025-01-24T00:45:51.7929474Z  prefixes.append(label) 2025-01-24T00:45:51.7929987Z  2025-01-24T00:45:51.7930354Z  if len(prefixes) > 1: 2025-01-24T00:45:51.7930822Z  log.error( 2025-01-24T00:45:51.7931870Z  f"Only a fleet and one other experiment can be enabled for a job at any time. Enabling {prefixes[0]} and ignoring the rest, which are {', '.join(prefixes[1:])}" 2025-01-24T00:45:51.7932958Z  ) 2025-01-24T00:45:51.7933367Z  prefixes = prefixes[:1] 2025-01-24T00:45:51.7933859Z  2025-01-24T00:45:51.7934222Z  # Fleet always comes first 2025-01-24T00:45:51.7934980Z  if fleet_prefix: 2025-01-24T00:45:51.7935463Z  prefixes.insert(0, fleet_prefix) 2025-01-24T00:45:51.7935986Z  2025-01-24T00:45:51.7936459Z  return ".".join(prefixes) + "." if prefixes else "" 2025-01-24T00:45:51.7937027Z  2025-01-24T00:45:51.7937368Z  2025-01-24T00:45:51.7938015Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-01-24T00:45:51.7938775Z  """ 2025-01-24T00:45:51.7939374Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-01-24T00:45:51.7940081Z  2025-01-24T00:45:51.7940648Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-01-24T00:45:51.7941357Z  """ 2025-01-24T00:45:51.7941929Z  gh = get_gh_client(github_token) 2025-01-24T00:45:51.7942500Z  issue = get_issue(gh, repo, issue_num) 2025-01-24T00:45:51.7943169Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-01-24T00:45:51.7943769Z  2025-01-24T00:45:51.7944112Z  2025-01-24T00:45:51.7945019Z def download_json(url: str, headers: Dict[str, str], num_retries: int = 3) -> Any: 2025-01-24T00:45:51.7945774Z  for _ in range(num_retries): 2025-01-24T00:45:51.7946254Z  try: 2025-01-24T00:45:51.7946734Z  req = Request(url=url, headers=headers) 2025-01-24T00:45:51.7947437Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-01-24T00:45:51.7948121Z  return json.loads(content) 2025-01-24T00:45:51.7948665Z  except Exception as e: 2025-01-24T00:45:51.7949238Z  log.warning(f"Could not download {url}: {e}") 2025-01-24T00:45:51.7949802Z  2025-01-24T00:45:51.7950675Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-01-24T00:45:51.7951440Z  return {} 2025-01-24T00:45:51.7951867Z  2025-01-24T00:45:51.7952204Z  2025-01-24T00:45:51.7952769Z @lru_cache(maxsize=None) 2025-01-24T00:45:51.7953949Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> Dict[str, Any]: 2025-01-24T00:45:51.7955465Z  """ 2025-01-24T00:45:51.7956233Z  Dynamically get PR information 2025-01-24T00:45:51.7957093Z  """ 2025-01-24T00:45:51.7957857Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-01-24T00:45:51.7958791Z  headers = { 2025-01-24T00:45:51.7959530Z  "Accept": "application/vnd.github.v3+json", 2025-01-24T00:45:51.7960440Z  "Authorization": f"token {github_token}", 2025-01-24T00:45:51.7961235Z  } 2025-01-24T00:45:51.7961918Z  json_response: Dict[str, Any] = download_json( 2025-01-24T00:45:51.7963124Z  url=f"{github_api}/issues/{pr_number}", 2025-01-24T00:45:51.7963836Z  headers=headers, 2025-01-24T00:45:51.7964287Z  ) 2025-01-24T00:45:51.7964835Z  2025-01-24T00:45:51.7965198Z  if not json_response: 2025-01-24T00:45:51.7965807Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-01-24T00:45:51.7966498Z  return {} 2025-01-24T00:45:51.7966912Z  2025-01-24T00:45:51.7967289Z  return json_response 2025-01-24T00:45:51.7967736Z  2025-01-24T00:45:51.7968076Z  2025-01-24T00:45:51.7968677Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> Set[str]: 2025-01-24T00:45:51.7969383Z  """ 2025-01-24T00:45:51.7969935Z  Dynamically get the latest list of labels from the pull request 2025-01-24T00:45:51.7970584Z  """ 2025-01-24T00:45:51.7971087Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-01-24T00:45:51.7971705Z  return { 2025-01-24T00:45:51.7972314Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-01-24T00:45:51.7972997Z  } 2025-01-24T00:45:51.7973351Z  2025-01-24T00:45:51.7973682Z  2025-01-24T00:45:51.7974029Z def main() -> None: 2025-01-24T00:45:51.7974677Z  args = parse_args() 2025-01-24T00:45:51.7975174Z  2025-01-24T00:45:51.7975585Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-01-24T00:45:51.7976120Z  2025-01-24T00:45:51.7976500Z  # Check if the PR is opt-out 2025-01-24T00:45:51.7976997Z  if args.pr_number: 2025-01-24T00:45:51.7977718Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-01-24T00:45:51.7978468Z  if OPT_OUT_LABEL in labels: 2025-01-24T00:45:51.7978973Z  log.info( 2025-01-24T00:45:51.7979694Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-01-24T00:45:51.7980607Z  ) 2025-01-24T00:45:51.7981221Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:45:51.7981889Z  sys.exit() 2025-01-24T00:45:51.7982336Z  2025-01-24T00:45:51.7982669Z  try: 2025-01-24T00:45:51.7983136Z  rollout_state = get_rollout_state_from_issue( 2025-01-24T00:45:51.7983863Z  args.github_token, args.github_issue_repo, args.github_issue 2025-01-24T00:45:51.7984690Z  ) 2025-01-24T00:45:51.7985062Z  2025-01-24T00:45:51.7985466Z  username = get_potential_pr_author( 2025-01-24T00:45:51.7986012Z  args.github_token, 2025-01-24T00:45:51.7986503Z  args.github_repo, 2025-01-24T00:45:51.7987011Z  args.github_actor, 2025-01-24T00:45:51.7987522Z  args.github_ref_type, 2025-01-24T00:45:51.7988027Z  args.github_branch, 2025-01-24T00:45:51.7988515Z  ) 2025-01-24T00:45:51.7988892Z  2025-01-24T00:45:51.7989362Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-01-24T00:45:51.7989968Z  2025-01-24T00:45:51.7990377Z  runner_label_prefix = get_runner_prefix( 2025-01-24T00:45:51.7990917Z  rollout_state, 2025-01-24T00:45:51.7991625Z  (args.github_issue_owner, username), 2025-01-24T00:45:51.7992195Z  args.github_branch, 2025-01-24T00:45:51.7992732Z  args.eligible_experiments, 2025-01-24T00:45:51.7993246Z  is_canary, 2025-01-24T00:45:51.7993837Z  ) 2025-01-24T00:45:51.7994618Z  2025-01-24T00:45:51.7995242Z  except Exception as e: 2025-01-24T00:45:51.7996038Z  log.error( 2025-01-24T00:45:51.7997235Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-01-24T00:45:51.7998203Z  ) 2025-01-24T00:45:51.7998588Z  2025-01-24T00:45:51.7999124Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:45:51.8000055Z  2025-01-24T00:45:51.8000477Z  2025-01-24T00:45:51.8000846Z if __name__ == "__main__": 2025-01-24T00:45:51.8001342Z  main() 2025-01-24T00:45:51.8001771Z  2025-01-24T00:45:51.8002173Z EOF 2025-01-24T00:45:51.8002566Z  2025-01-24T00:45:51.8002935Z cat runner_determinator.py 2025-01-24T00:45:51.8202211Z shell: /usr/bin/bash -e {0} 2025-01-24T00:45:51.8202982Z env: 2025-01-24T00:45:51.8203593Z GITHUB_TOKEN: *** 2025-01-24T00:45:51.8203998Z ISSUE_NUMBER: 5132 2025-01-24T00:45:51.8204648Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:45:51.8205141Z ISSUE_OWNER: 2025-01-24T00:45:51.8205531Z CHECK_EXPERIMENTS: 2025-01-24T00:45:51.8205941Z PR_NUMBER: 2025-01-24T00:45:51.8206312Z ##[endgroup] 2025-01-24T00:45:51.8412149Z # flake8: noqa: G004 2025-01-24T00:45:51.8412465Z 2025-01-24T00:45:51.8412891Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-01-24T00:45:51.8413811Z # must be kept in sync. You can do it easily by running the following command: 2025-01-24T00:45:51.8415083Z # python .github/scripts/update_runner_determinator.py 2025-01-24T00:45:51.8415520Z 2025-01-24T00:45:51.8415669Z """ 2025-01-24T00:45:51.8416222Z This runner determinator is used to determine which set of runners to run a 2025-01-24T00:45:51.8417073Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-01-24T00:45:51.8417943Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-01-24T00:45:51.8419219Z of which runners should be used to run which job. 2025-01-24T00:45:51.8419919Z 2025-01-24T00:45:51.8420659Z The configuration has two parts, the settings and a list of opted-in users, 2025-01-24T00:45:51.8422381Z separated by a line containing "---". If the line is not present, the 2025-01-24T00:45:51.8423718Z settings are considered to be empty with only the second part, the user 2025-01-24T00:45:51.8424650Z list, defined. 2025-01-24T00:45:51.8424916Z 2025-01-24T00:45:51.8483342Z The first part is a YAML block that defines the rollout settings. This can be 2025-01-24T00:45:51.8484672Z used to define any settings that are needed to determine which runners to use. 2025-01-24T00:45:51.8485597Z It's fields are defined by the RolloutSettings class below. 2025-01-24T00:45:51.8486044Z 2025-01-24T00:45:51.8486418Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-01-24T00:45:51.8487285Z The user list is also a comma separated list of additional features or 2025-01-24T00:45:51.8488017Z experiments which the user could be opted in to. 2025-01-24T00:45:51.8488445Z 2025-01-24T00:45:51.8488635Z The user list has the following rules: 2025-01-24T00:45:51.8488976Z 2025-01-24T00:45:51.8489274Z - Users are GitHub usernames, which must start with the @ prefix 2025-01-24T00:45:51.8490095Z - Each user is also a comma-separated list of features/experiments to enable 2025-01-24T00:45:51.8490827Z - A "#" prefix opts the user out of all experiments 2025-01-24T00:45:51.8491207Z 2025-01-24T00:45:51.8491367Z Example config: 2025-01-24T00:45:51.8491808Z # A list of experiments that can be opted into. 2025-01-24T00:45:51.8492442Z # This defines the behavior they'll induce when opted into. 2025-01-24T00:45:51.8493025Z # Expected syntax is: 2025-01-24T00:45:51.8493632Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-01-24T00:45:51.8494760Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-01-24T00:45:51.8495565Z 2025-01-24T00:45:51.8495745Z experiments: 2025-01-24T00:45:51.8496114Z lf: 2025-01-24T00:45:51.8496475Z rollout_percent: 25 2025-01-24T00:45:51.8496926Z all_branches: false 2025-01-24T00:45:51.8497349Z default: true 2025-01-24T00:45:51.8497739Z --- 2025-01-24T00:45:51.8497928Z 2025-01-24T00:45:51.8498085Z # Opt-ins: 2025-01-24T00:45:51.8498641Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-01-24T00:45:51.8499466Z # and specifying experiments to enable in a comma-separated list. 2025-01-24T00:45:51.8500207Z # To always opt out of an experiment, prefix it with a "-". 2025-01-24T00:45:51.8500839Z # Experiments should be from the above list. 2025-01-24T00:45:51.8501212Z 2025-01-24T00:45:51.8501380Z @User1,-lf,split_build 2025-01-24T00:45:51.8501799Z @User2,lf 2025-01-24T00:45:51.8502164Z @User3,split_build 2025-01-24T00:45:51.8502559Z """ 2025-01-24T00:45:51.8502740Z 2025-01-24T00:45:51.8502908Z import json 2025-01-24T00:45:51.8503255Z import logging 2025-01-24T00:45:51.8503618Z import os 2025-01-24T00:45:51.8503962Z import random 2025-01-24T00:45:51.8504531Z import re 2025-01-24T00:45:51.8504957Z import sys 2025-01-24T00:45:51.8505344Z from argparse import ArgumentParser 2025-01-24T00:45:51.8505850Z from functools import lru_cache 2025-01-24T00:45:51.8506320Z from logging import LogRecord 2025-01-24T00:45:51.8506965Z from typing import Any, Dict, FrozenSet, Iterable, List, NamedTuple, Set, Tuple 2025-01-24T00:45:51.8507693Z from urllib.request import Request, urlopen 2025-01-24T00:45:51.8508044Z 2025-01-24T00:45:51.8508204Z import yaml 2025-01-24T00:45:51.8508574Z from github import Auth, Github 2025-01-24T00:45:51.8509044Z from github.Issue import Issue 2025-01-24T00:45:51.8509330Z 2025-01-24T00:45:51.8509337Z 2025-01-24T00:45:51.8509547Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-01-24T00:45:51.8510198Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-01-24T00:45:51.8511025Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-01-24T00:45:51.8511579Z 2025-01-24T00:45:51.8511942Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-01-24T00:45:51.8512493Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-01-24T00:45:51.8512974Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-01-24T00:45:51.8513500Z OPT_OUT_LABEL = "no-runner-experiments" 2025-01-24T00:45:51.8513851Z 2025-01-24T00:45:51.8514039Z SETTING_EXPERIMENTS = "experiments" 2025-01-24T00:45:51.8514893Z 2025-01-24T00:45:51.8515090Z LF_FLEET_EXPERIMENT = "lf" 2025-01-24T00:45:51.8515537Z CANARY_FLEET_SUFFIX = ".c" 2025-01-24T00:45:51.8515815Z 2025-01-24T00:45:51.8515821Z 2025-01-24T00:45:51.8516003Z class Experiment(NamedTuple): 2025-01-24T00:45:51.8516463Z rollout_perc: float = ( 2025-01-24T00:45:51.8517075Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-01-24T00:45:51.8517726Z ) 2025-01-24T00:45:51.8518071Z all_branches: bool = ( 2025-01-24T00:45:51.8518673Z False # If True, the experiment is also enabled on the exception branches 2025-01-24T00:45:51.8519323Z ) 2025-01-24T00:45:51.8519664Z default: bool = ( 2025-01-24T00:45:51.8520207Z True # If True, the experiment is enabled by default for all queries 2025-01-24T00:45:51.8520822Z ) 2025-01-24T00:45:51.8521001Z 2025-01-24T00:45:51.8521174Z # Add more fields as needed 2025-01-24T00:45:51.8521463Z 2025-01-24T00:45:51.8521468Z 2025-01-24T00:45:51.8521644Z class Settings(NamedTuple): 2025-01-24T00:45:51.8522062Z """ 2025-01-24T00:45:51.8522485Z Settings for the experiments that can be opted into. 2025-01-24T00:45:51.8523036Z """ 2025-01-24T00:45:51.8523223Z 2025-01-24T00:45:51.8523423Z experiments: Dict[str, Experiment] = {} 2025-01-24T00:45:51.8523770Z 2025-01-24T00:45:51.8523776Z 2025-01-24T00:45:51.8523970Z class ColorFormatter(logging.Formatter): 2025-01-24T00:45:51.8524679Z """Color codes the log messages based on the log level""" 2025-01-24T00:45:51.8525235Z 2025-01-24T00:45:51.8525405Z COLORS = { 2025-01-24T00:45:51.8525782Z "WARNING": "\033[33m", # Yellow 2025-01-24T00:45:51.8526270Z "ERROR": "\033[31m", # Red 2025-01-24T00:45:51.8526730Z "CRITICAL": "\033[31m", # Red 2025-01-24T00:45:51.8527207Z "INFO": "\033[0m", # Reset 2025-01-24T00:45:51.8527661Z "DEBUG": "\033[0m", # Reset 2025-01-24T00:45:51.8528110Z } 2025-01-24T00:45:51.8528297Z 2025-01-24T00:45:51.8528500Z def format(self, record: LogRecord) -> str: 2025-01-24T00:45:51.8529769Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-01-24T00:45:51.8531041Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-01-24T00:45:51.8531823Z return super().format(record) 2025-01-24T00:45:51.8532154Z 2025-01-24T00:45:51.8532161Z 2025-01-24T00:45:51.8532347Z handler = logging.StreamHandler() 2025-01-24T00:45:51.8533039Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-01-24T00:45:51.8533574Z 2025-01-24T00:45:51.8533811Z log = logging.getLogger(os.path.basename(__file__)) 2025-01-24T00:45:51.8534514Z log.addHandler(handler) 2025-01-24T00:45:51.8534944Z log.setLevel(logging.INFO) 2025-01-24T00:45:51.8535219Z 2025-01-24T00:45:51.8535225Z 2025-01-24T00:45:51.8535464Z def set_github_output(key: str, value: str) -> None: 2025-01-24T00:45:51.8536000Z """ 2025-01-24T00:45:51.8536490Z Defines outputs of the github action that invokes this script 2025-01-24T00:45:51.8537088Z """ 2025-01-24T00:45:51.8537430Z if not GITHUB_OUTPUT: 2025-01-24T00:45:51.8538643Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-01-24T00:45:51.8540451Z log.warning( 2025-01-24T00:45:51.8541829Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-01-24T00:45:51.8543302Z ) 2025-01-24T00:45:51.8553897Z print(f"::set-output name={key}::{value}") 2025-01-24T00:45:51.8555103Z return 2025-01-24T00:45:51.8555744Z 2025-01-24T00:45:51.8556040Z with open(GITHUB_OUTPUT, "a") as f: 2025-01-24T00:45:51.8556784Z log.info(f"Setting output: {key}='{value}'") 2025-01-24T00:45:51.8557333Z f.write(f"{key}={value}\n") 2025-01-24T00:45:51.8557653Z 2025-01-24T00:45:51.8557659Z 2025-01-24T00:45:51.8557944Z def _str_comma_separated_to_set(value: str) -> FrozenSet[str]: 2025-01-24T00:45:51.8558539Z return frozenset( 2025-01-24T00:45:51.8559137Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-01-24T00:45:51.8559792Z ) 2025-01-24T00:45:51.8559981Z 2025-01-24T00:45:51.8559988Z 2025-01-24T00:45:51.8560166Z def parse_args() -> Any: 2025-01-24T00:45:51.8560734Z parser = ArgumentParser("Get dynamic rollout settings") 2025-01-24T00:45:51.8561578Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-01-24T00:45:51.8562306Z parser.add_argument( 2025-01-24T00:45:51.8562725Z "--github-issue-repo", 2025-01-24T00:45:51.8563161Z type=str, 2025-01-24T00:45:51.8563533Z required=False, 2025-01-24T00:45:51.8563945Z default="pytorch/test-infra", 2025-01-24T00:45:51.8564662Z help="GitHub repo to get the issue", 2025-01-24T00:45:51.8565165Z ) 2025-01-24T00:45:51.8565505Z parser.add_argument( 2025-01-24T00:45:51.8565920Z "--github-repo", 2025-01-24T00:45:51.8566315Z type=str, 2025-01-24T00:45:51.8566687Z required=True, 2025-01-24T00:45:51.8567121Z help="GitHub repo where CI is running", 2025-01-24T00:45:51.8567611Z ) 2025-01-24T00:45:51.8567944Z parser.add_argument( 2025-01-24T00:45:51.8568516Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-01-24T00:45:51.8569140Z ) 2025-01-24T00:45:51.8569476Z parser.add_argument( 2025-01-24T00:45:51.8570258Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-01-24T00:45:51.8570908Z ) 2025-01-24T00:45:51.8571238Z parser.add_argument( 2025-01-24T00:45:51.8571845Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-01-24T00:45:51.8572495Z ) 2025-01-24T00:45:51.8572826Z parser.add_argument( 2025-01-24T00:45:51.8573433Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-01-24T00:45:51.8574101Z ) 2025-01-24T00:45:51.8574665Z parser.add_argument( 2025-01-24T00:45:51.8575097Z "--github-ref-type", 2025-01-24T00:45:51.8575516Z type=str, 2025-01-24T00:45:51.8575885Z required=True, 2025-01-24T00:45:51.8576350Z help="Current GitHub ref type, branch or tag", 2025-01-24T00:45:51.8576864Z ) 2025-01-24T00:45:51.8577210Z parser.add_argument( 2025-01-24T00:45:51.8577631Z "--eligible-experiments", 2025-01-24T00:45:51.8578113Z type=_str_comma_separated_to_set, 2025-01-24T00:45:51.8578597Z required=False, 2025-01-24T00:45:51.8578991Z default="", 2025-01-24T00:45:51.8579791Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-01-24T00:45:51.8580683Z ) 2025-01-24T00:45:51.8581019Z parser.add_argument( 2025-01-24T00:45:51.8581416Z "--pr-number", 2025-01-24T00:45:51.8581804Z type=str, 2025-01-24T00:45:51.8582187Z required=False, 2025-01-24T00:45:51.8582569Z default="", 2025-01-24T00:45:51.8583000Z help="the optional PR number where this is run", 2025-01-24T00:45:51.8583530Z ) 2025-01-24T00:45:51.8583709Z 2025-01-24T00:45:51.8583885Z return parser.parse_args() 2025-01-24T00:45:51.8584177Z 2025-01-24T00:45:51.8584184Z 2025-01-24T00:45:51.8584609Z def get_gh_client(github_token: str) -> Github: 2025-01-24T00:45:51.8585179Z auth = Auth.Token(github_token) 2025-01-24T00:45:51.8585647Z return Github(auth=auth) 2025-01-24T00:45:51.8585937Z 2025-01-24T00:45:51.8585944Z 2025-01-24T00:45:51.8586223Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: 2025-01-24T00:45:51.8586988Z repo = gh.get_repo(repo) 2025-01-24T00:45:51.8587455Z return repo.get_issue(number=issue_num) 2025-01-24T00:45:51.8587803Z 2025-01-24T00:45:51.8587810Z 2025-01-24T00:45:51.8587990Z def get_potential_pr_author( 2025-01-24T00:45:51.8588630Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-01-24T00:45:51.8589273Z ) -> str: 2025-01-24T00:45:51.8589752Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-01-24T00:45:51.8590509Z # Fetch the actual username from the original PR. The PR number is 2025-01-24T00:45:51.8591779Z # embedded in the tag name: ciflow// 2025-01-24T00:45:51.8592437Z 2025-01-24T00:45:51.8592731Z gh = get_gh_client(github_token) 2025-01-24T00:45:51.8593106Z 2025-01-24T00:45:51.8593375Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-01-24T00:45:51.8593960Z split_tag = ref_name.split("/") 2025-01-24T00:45:51.8594558Z if ( 2025-01-24T00:45:51.8594936Z len(split_tag) == 3 2025-01-24T00:45:51.8595408Z and split_tag[0] == "ciflow" 2025-01-24T00:45:51.8595899Z and split_tag[2].isnumeric() 2025-01-24T00:45:51.8596362Z ): 2025-01-24T00:45:51.8596718Z pr_number = split_tag[2] 2025-01-24T00:45:51.8597162Z try: 2025-01-24T00:45:51.8597578Z repository = gh.get_repo(repo) 2025-01-24T00:45:51.8598309Z pull = repository.get_pull(number=int(pr_number)) 2025-01-24T00:45:51.8599320Z except Exception as e: 2025-01-24T00:45:51.8600150Z raise Exception( # noqa: TRY002 2025-01-24T00:45:51.8601219Z f"issue with pull request {pr_number} from repo {repository}" 2025-01-24T00:45:51.8601992Z ) from e 2025-01-24T00:45:51.8602611Z return pull.user.login 2025-01-24T00:45:51.8603174Z # In all other cases, return the original input username 2025-01-24T00:45:51.8603736Z return username 2025-01-24T00:45:51.8603978Z 2025-01-24T00:45:51.8603985Z 2025-01-24T00:45:51.8604194Z def is_exception_branch(branch: str) -> bool: 2025-01-24T00:45:51.8604857Z """ 2025-01-24T00:45:51.8605462Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-01-24T00:45:51.8606201Z """ 2025-01-24T00:45:51.8606728Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-01-24T00:45:51.8607222Z 2025-01-24T00:45:51.8607229Z 2025-01-24T00:45:51.8607438Z def load_yaml(yaml_text: str) -> Any: 2025-01-24T00:45:51.8607930Z try: 2025-01-24T00:45:51.8608297Z data = yaml.safe_load(yaml_text) 2025-01-24T00:45:51.8608804Z return data 2025-01-24T00:45:51.8609187Z except yaml.YAMLError: 2025-01-24T00:45:51.8609644Z log.exception("Error loading YAML") 2025-01-24T00:45:51.8610146Z raise 2025-01-24T00:45:51.8610348Z 2025-01-24T00:45:51.8610354Z 2025-01-24T00:45:51.8610750Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> Tuple[str, str]: 2025-01-24T00:45:51.8611464Z """ 2025-01-24T00:45:51.8612049Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-01-24T00:45:51.8612618Z 2025-01-24T00:45:51.8612953Z If the issue body contains "---" then the text above that is the settings 2025-01-24T00:45:51.8613656Z and the text below is the list of opted in users. 2025-01-24T00:45:51.8614043Z 2025-01-24T00:45:51.8614605Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-01-24T00:45:51.8615297Z """ 2025-01-24T00:45:51.8615702Z rollout_state_parts = rollout_state.split("---") 2025-01-24T00:45:51.8616267Z if len(rollout_state_parts) >= 2: 2025-01-24T00:45:51.8616837Z return rollout_state_parts[0], rollout_state_parts[1] 2025-01-24T00:45:51.8617397Z else: 2025-01-24T00:45:51.8617754Z return "", rollout_state 2025-01-24T00:45:51.8618042Z 2025-01-24T00:45:51.8618048Z 2025-01-24T00:45:51.8618384Z class UserOptins(Dict[str, List[str]]): 2025-01-24T00:45:51.8618851Z """ 2025-01-24T00:45:51.8619340Z Dictionary of users with a list of features they have opted into 2025-01-24T00:45:51.8619949Z """ 2025-01-24T00:45:51.8620137Z 2025-01-24T00:45:51.8620143Z 2025-01-24T00:45:51.8620462Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-01-24T00:45:51.8621077Z """ 2025-01-24T00:45:51.8621746Z Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-01-24T00:45:51.8622420Z 2025-01-24T00:45:51.8623021Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-01-24T00:45:51.8623969Z - Example line: "@User1,lf,split_build" 2025-01-24T00:45:51.8624866Z - A "#" prefix indicates the user is opted out of all experiments 2025-01-24T00:45:51.8625324Z 2025-01-24T00:45:51.8625331Z 2025-01-24T00:45:51.8625482Z """ 2025-01-24T00:45:51.8625828Z optins = UserOptins() 2025-01-24T00:45:51.8626279Z for user in user_optin_text.split("\n"): 2025-01-24T00:45:51.8626795Z user = user.strip("\r\n\t -") 2025-01-24T00:45:51.8627292Z if not user or not user.startswith("@"): 2025-01-24T00:45:51.8627812Z # Not a valid user. Skip 2025-01-24T00:45:51.8628264Z continue 2025-01-24T00:45:51.8628491Z 2025-01-24T00:45:51.8628638Z if user: 2025-01-24T00:45:51.8629044Z usr_name = user.split(",")[0].strip("@") 2025-01-24T00:45:51.8629699Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-01-24T00:45:51.8630163Z 2025-01-24T00:45:51.8630318Z return optins 2025-01-24T00:45:51.8630542Z 2025-01-24T00:45:51.8630549Z 2025-01-24T00:45:51.8630817Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-01-24T00:45:51.8631521Z """ 2025-01-24T00:45:51.8631900Z Check if the experiment name is valid. 2025-01-24T00:45:51.8632398Z A valid name: 2025-01-24T00:45:51.8633003Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-01-24T00:45:51.8633895Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-01-24T00:45:51.8634715Z - Cannot contain spaces 2025-01-24T00:45:51.8635144Z """ 2025-01-24T00:45:51.8635327Z 2025-01-24T00:45:51.8635569Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-01-24T00:45:51.8636219Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-01-24T00:45:51.8636635Z 2025-01-24T00:45:51.8636792Z if valid: 2025-01-24T00:45:51.8637145Z return True 2025-01-24T00:45:51.8637373Z 2025-01-24T00:45:51.8637521Z log.error( 2025-01-24T00:45:51.8638903Z f"Invalid experiment name: {experiment_name}. Experiment names should only contain alphanumeric characters, '_', and '-'. They cannot contain spaces, and the special characters '_' and '-' cannot be the first or last characters." 2025-01-24T00:45:51.8640381Z ) 2025-01-24T00:45:51.8640703Z return False 2025-01-24T00:45:51.8640922Z 2025-01-24T00:45:51.8640928Z 2025-01-24T00:45:51.8641214Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-01-24T00:45:51.8641799Z """ 2025-01-24T00:45:51.8642336Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-01-24T00:45:51.8643335Z """ 2025-01-24T00:45:51.8643814Z try: 2025-01-24T00:45:51.8644158Z if settings_text: 2025-01-24T00:45:51.8645031Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-01-24T00:45:51.8645782Z # for easy reading 2025-01-24T00:45:51.8646524Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-01-24T00:45:51.8647382Z # the backtick character in shell commands. 2025-01-24T00:45:51.8647950Z backtick = chr(96) # backtick character 2025-01-24T00:45:51.8648767Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-01-24T00:45:51.8649389Z settings = load_yaml(settings_text) 2025-01-24T00:45:51.8649729Z 2025-01-24T00:45:51.8650118Z # For now we just load experiments. We can expand this if/when we add more settings 2025-01-24T00:45:51.8650819Z experiments = {} 2025-01-24T00:45:51.8651101Z 2025-01-24T00:45:51.8651438Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-01-24T00:45:51.8652139Z if not is_valid_experiment_name(exp_name): 2025-01-24T00:45:51.8653191Z # Exclude invalid experiments from the list. We log an error, but don't raise an exception so that other experiments can still be processed. 2025-01-24T00:45:51.8654167Z continue 2025-01-24T00:45:51.8654689Z 2025-01-24T00:45:51.8654952Z valid_settings = {} 2025-01-24T00:45:51.8655453Z for setting in exp_settings: 2025-01-24T00:45:51.8656000Z if setting not in Experiment._fields: 2025-01-24T00:45:51.8656522Z log.warning( 2025-01-24T00:45:51.8657184Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-01-24T00:45:51.8657860Z ) 2025-01-24T00:45:51.8658254Z else: 2025-01-24T00:45:51.8658733Z valid_settings[setting] = exp_settings[setting] 2025-01-24T00:45:51.8659132Z 2025-01-24T00:45:51.8659383Z experiments[exp_name] = Experiment(**valid_settings) 2025-01-24T00:45:51.8659970Z return Settings(experiments) 2025-01-24T00:45:51.8660296Z 2025-01-24T00:45:51.8660465Z except Exception: 2025-01-24T00:45:51.8660903Z log.exception("Failed to parse settings") 2025-01-24T00:45:51.8661263Z 2025-01-24T00:45:51.8661565Z return Settings() 2025-01-24T00:45:51.8661813Z 2025-01-24T00:45:51.8661824Z 2025-01-24T00:45:51.8662051Z def parse_settings(rollout_state: str) -> Settings: 2025-01-24T00:45:51.8662595Z """ 2025-01-24T00:45:51.8662989Z Parse settings, if any, from the rollout state. 2025-01-24T00:45:51.8663367Z 2025-01-24T00:45:51.8663694Z If the issue body contains "---" then the text above that is the settings 2025-01-24T00:45:51.8664627Z and the text below is the list of opted in users. 2025-01-24T00:45:51.8665030Z 2025-01-24T00:45:51.8665416Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-01-24T00:45:51.8666099Z """ 2025-01-24T00:45:51.8666636Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:45:51.8667335Z return parse_settings_from_text(settings_text) 2025-01-24T00:45:51.8667713Z 2025-01-24T00:45:51.8667720Z 2025-01-24T00:45:51.8667954Z def parse_users(rollout_state: str) -> UserOptins: 2025-01-24T00:45:51.8668485Z """ 2025-01-24T00:45:51.8668842Z Parse users from the rollout state. 2025-01-24T00:45:51.8669181Z 2025-01-24T00:45:51.8669326Z """ 2025-01-24T00:45:51.8669811Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:45:51.8670488Z return parse_user_opt_in_from_text(users_text) 2025-01-24T00:45:51.8670867Z 2025-01-24T00:45:51.8670874Z 2025-01-24T00:45:51.8671264Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:45:51.8671956Z """ 2025-01-24T00:45:51.8672321Z Check if a user is opted into an experiment 2025-01-24T00:45:51.8672821Z """ 2025-01-24T00:45:51.8673227Z return experiment_name in user_optins.get(user, []) 2025-01-24T00:45:51.8673613Z 2025-01-24T00:45:51.8673619Z 2025-01-24T00:45:51.8674015Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:45:51.8674830Z """ 2025-01-24T00:45:51.8675259Z Check if a user explicitly opted out of an experiment 2025-01-24T00:45:51.8675808Z """ 2025-01-24T00:45:51.8676267Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-01-24T00:45:51.8677144Z experiment_optout = "-" + experiment_name 2025-01-24T00:45:51.8677745Z if experiment_optout not in user_optins.get(user, []): 2025-01-24T00:45:51.8678298Z return False 2025-01-24T00:45:51.8678537Z 2025-01-24T00:45:51.8678787Z if is_user_opted_in(user, user_optins, experiment_name): 2025-01-24T00:45:51.8679349Z log.warning( 2025-01-24T00:45:51.8680095Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-01-24T00:45:51.8680922Z ) 2025-01-24T00:45:51.8681114Z 2025-01-24T00:45:51.8681277Z return True 2025-01-24T00:45:51.8681487Z 2025-01-24T00:45:51.8681493Z 2025-01-24T00:45:51.8681651Z def get_runner_prefix( 2025-01-24T00:45:51.8682053Z rollout_state: str, 2025-01-24T00:45:51.8682484Z workflow_requestors: Iterable[str], 2025-01-24T00:45:51.8682955Z branch: str, 2025-01-24T00:45:51.8683402Z eligible_experiments: FrozenSet[str] = frozenset(), 2025-01-24T00:45:51.8683963Z is_canary: bool = False, 2025-01-24T00:45:51.8684483Z ) -> str: 2025-01-24T00:45:51.8684870Z settings = parse_settings(rollout_state) 2025-01-24T00:45:51.8685408Z user_optins = parse_users(rollout_state) 2025-01-24T00:45:51.8685747Z 2025-01-24T00:45:51.8685902Z fleet_prefix = "" 2025-01-24T00:45:51.8686292Z prefixes = [] 2025-01-24T00:45:51.8686874Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-01-24T00:45:51.8687750Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-01-24T00:45:51.8688447Z log.info( 2025-01-24T00:45:51.8689077Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-01-24T00:45:51.8689779Z ) 2025-01-24T00:45:51.8690243Z continue 2025-01-24T00:45:51.8690478Z 2025-01-24T00:45:51.8690650Z if eligible_experiments: 2025-01-24T00:45:51.8691167Z if experiment_name not in eligible_experiments: 2025-01-24T00:45:51.8691748Z exp_list = ", ".join(eligible_experiments) 2025-01-24T00:45:51.8692266Z log.info( 2025-01-24T00:45:51.8692990Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-01-24T00:45:51.8693772Z ) 2025-01-24T00:45:51.8694128Z continue 2025-01-24T00:45:51.8694767Z elif not experiment_settings.default: 2025-01-24T00:45:51.8695270Z log.info( 2025-01-24T00:45:51.8695900Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-01-24T00:45:51.8696584Z ) 2025-01-24T00:45:51.8696934Z continue 2025-01-24T00:45:51.8697156Z 2025-01-24T00:45:51.8697429Z # Is any workflow_requestor opted out to this experiment? 2025-01-24T00:45:51.8697998Z opted_out_users = [ 2025-01-24T00:45:51.8698420Z requestor 2025-01-24T00:45:51.8698837Z for requestor in workflow_requestors 2025-01-24T00:45:51.8699463Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-01-24T00:45:51.8700052Z ] 2025-01-24T00:45:51.8700241Z 2025-01-24T00:45:51.8700410Z if opted_out_users: 2025-01-24T00:45:51.8700816Z log.info( 2025-01-24T00:45:51.8701398Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-01-24T00:45:51.8702054Z ) 2025-01-24T00:45:51.8702393Z continue 2025-01-24T00:45:51.8702621Z 2025-01-24T00:45:51.8702870Z # Is any workflow_requestor opted in to this experiment? 2025-01-24T00:45:51.8703442Z opted_in_users = [ 2025-01-24T00:45:51.8703852Z requestor 2025-01-24T00:45:51.8704274Z for requestor in workflow_requestors 2025-01-24T00:45:51.8705122Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-01-24T00:45:51.8705716Z ] 2025-01-24T00:45:51.8706058Z 2025-01-24T00:45:51.8706228Z enabled = False 2025-01-24T00:45:51.8706624Z if opted_in_users: 2025-01-24T00:45:51.8707039Z log.info( 2025-01-24T00:45:51.8707609Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-01-24T00:45:51.8708257Z ) 2025-01-24T00:45:51.8708620Z enabled = True 2025-01-24T00:45:51.8708991Z 2025-01-24T00:45:51.8709354Z elif experiment_settings.rollout_perc: 2025-01-24T00:45:51.8710306Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-01-24T00:45:51.8711197Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-01-24T00:45:51.8711804Z log.info( 2025-01-24T00:45:51.8712624Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-01-24T00:45:51.8713597Z ) 2025-01-24T00:45:51.8713995Z enabled = True 2025-01-24T00:45:51.8714272Z 2025-01-24T00:45:51.8714562Z if enabled: 2025-01-24T00:45:51.8714956Z label = experiment_name 2025-01-24T00:45:51.8715471Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-01-24T00:45:51.8716242Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-01-24T00:45:51.8717071Z # - If it's enabled, then we always list it's prefix first 2025-01-24T00:45:51.8717788Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-01-24T00:45:51.8718396Z if is_canary: 2025-01-24T00:45:51.8718845Z label += CANARY_FLEET_SUFFIX 2025-01-24T00:45:51.8719355Z fleet_prefix = label 2025-01-24T00:45:51.8719974Z else: 2025-01-24T00:45:51.8720376Z prefixes.append(label) 2025-01-24T00:45:51.8720699Z 2025-01-24T00:45:51.8720878Z if len(prefixes) > 1: 2025-01-24T00:45:51.8721282Z log.error( 2025-01-24T00:45:51.8722255Z f"Only a fleet and one other experiment can be enabled for a job at any time. Enabling {prefixes[0]} and ignoring the rest, which are {', '.join(prefixes[1:])}" 2025-01-24T00:45:51.8723312Z ) 2025-01-24T00:45:51.8723656Z prefixes = prefixes[:1] 2025-01-24T00:45:51.8723945Z 2025-01-24T00:45:51.8724113Z # Fleet always comes first 2025-01-24T00:45:51.8724739Z if fleet_prefix: 2025-01-24T00:45:51.8725219Z prefixes.insert(0, fleet_prefix) 2025-01-24T00:45:51.8725731Z 2025-01-24T00:45:51.8726088Z return ".".join(prefixes) + "." if prefixes else "" 2025-01-24T00:45:51.8726501Z 2025-01-24T00:45:51.8726507Z 2025-01-24T00:45:51.8726932Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-01-24T00:45:51.8727772Z """ 2025-01-24T00:45:51.8728305Z Gets the first comment of the issue, which contains the desired rollout state. 2025-01-24T00:45:51.8728840Z 2025-01-24T00:45:51.8729203Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-01-24T00:45:51.8729862Z """ 2025-01-24T00:45:51.8730217Z gh = get_gh_client(github_token) 2025-01-24T00:45:51.8730717Z issue = get_issue(gh, repo, issue_num) 2025-01-24T00:45:51.8731303Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-01-24T00:45:51.8731721Z 2025-01-24T00:45:51.8731728Z 2025-01-24T00:45:51.8732102Z def download_json(url: str, headers: Dict[str, str], num_retries: int = 3) -> Any: 2025-01-24T00:45:51.8732810Z for _ in range(num_retries): 2025-01-24T00:45:51.8733254Z try: 2025-01-24T00:45:51.8733641Z req = Request(url=url, headers=headers) 2025-01-24T00:45:51.8734266Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-01-24T00:45:51.8735116Z return json.loads(content) 2025-01-24T00:45:51.8735600Z except Exception as e: 2025-01-24T00:45:51.8736102Z log.warning(f"Could not download {url}: {e}") 2025-01-24T00:45:51.8736632Z 2025-01-24T00:45:51.8736995Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-01-24T00:45:51.8737668Z return {} 2025-01-24T00:45:51.8737886Z 2025-01-24T00:45:51.8737893Z 2025-01-24T00:45:51.8738060Z @lru_cache(maxsize=None) 2025-01-24T00:45:51.8738713Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> Dict[str, Any]: 2025-01-24T00:45:51.8739414Z """ 2025-01-24T00:45:51.8739779Z Dynamically get PR information 2025-01-24T00:45:51.8740235Z """ 2025-01-24T00:45:51.8740680Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-01-24T00:45:51.8741262Z headers = { 2025-01-24T00:45:51.8741677Z "Accept": "application/vnd.github.v3+json", 2025-01-24T00:45:51.8742246Z "Authorization": f"token {github_token}", 2025-01-24T00:45:51.8742739Z } 2025-01-24T00:45:51.8743127Z json_response: Dict[str, Any] = download_json( 2025-01-24T00:45:51.8743689Z url=f"{github_api}/issues/{pr_number}", 2025-01-24T00:45:51.8744197Z headers=headers, 2025-01-24T00:45:51.8744711Z ) 2025-01-24T00:45:51.8744894Z 2025-01-24T00:45:51.8745061Z if not json_response: 2025-01-24T00:45:51.8745581Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-01-24T00:45:51.8746157Z return {} 2025-01-24T00:45:51.8746369Z 2025-01-24T00:45:51.8746532Z return json_response 2025-01-24T00:45:51.8746791Z 2025-01-24T00:45:51.8746798Z 2025-01-24T00:45:51.8747166Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> Set[str]: 2025-01-24T00:45:51.8747830Z """ 2025-01-24T00:45:51.8748301Z Dynamically get the latest list of labels from the pull request 2025-01-24T00:45:51.8748905Z """ 2025-01-24T00:45:51.8749458Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-01-24T00:45:51.8750030Z return { 2025-01-24T00:45:51.8750572Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-01-24T00:45:51.8751227Z } 2025-01-24T00:45:51.8751405Z 2025-01-24T00:45:51.8751411Z 2025-01-24T00:45:51.8751576Z def main() -> None: 2025-01-24T00:45:51.8751955Z args = parse_args() 2025-01-24T00:45:51.8752203Z 2025-01-24T00:45:51.8752406Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-01-24T00:45:51.8752759Z 2025-01-24T00:45:51.8752936Z # Check if the PR is opt-out 2025-01-24T00:45:51.8753382Z if args.pr_number: 2025-01-24T00:45:51.8753990Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-01-24T00:45:51.8754801Z if OPT_OUT_LABEL in labels: 2025-01-24T00:45:51.8755250Z log.info( 2025-01-24T00:45:51.8755882Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-01-24T00:45:51.8756607Z ) 2025-01-24T00:45:51.8757106Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:45:51.8757739Z sys.exit() 2025-01-24T00:45:51.8757975Z 2025-01-24T00:45:51.8758124Z try: 2025-01-24T00:45:51.8758518Z rollout_state = get_rollout_state_from_issue( 2025-01-24T00:45:51.8759195Z args.github_token, args.github_issue_repo, args.github_issue 2025-01-24T00:45:51.8759792Z ) 2025-01-24T00:45:51.8759976Z 2025-01-24T00:45:51.8760158Z username = get_potential_pr_author( 2025-01-24T00:45:51.8760658Z args.github_token, 2025-01-24T00:45:51.8761125Z args.github_repo, 2025-01-24T00:45:51.8761553Z args.github_actor, 2025-01-24T00:45:51.8761996Z args.github_ref_type, 2025-01-24T00:45:51.8762446Z args.github_branch, 2025-01-24T00:45:51.8762858Z ) 2025-01-24T00:45:51.8763050Z 2025-01-24T00:45:51.8763313Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-01-24T00:45:51.8763729Z 2025-01-24T00:45:51.8763934Z runner_label_prefix = get_runner_prefix( 2025-01-24T00:45:51.8764536Z rollout_state, 2025-01-24T00:45:51.8765135Z (args.github_issue_owner, username), 2025-01-24T00:45:51.8765648Z args.github_branch, 2025-01-24T00:45:51.8766103Z args.eligible_experiments, 2025-01-24T00:45:51.8766574Z is_canary, 2025-01-24T00:45:51.8766950Z ) 2025-01-24T00:45:51.8767134Z 2025-01-24T00:45:51.8767303Z except Exception as e: 2025-01-24T00:45:51.8767711Z log.error( 2025-01-24T00:45:51.8768332Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-01-24T00:45:51.8769036Z ) 2025-01-24T00:45:51.8769228Z 2025-01-24T00:45:51.8769539Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:45:51.8770028Z 2025-01-24T00:45:51.8770034Z 2025-01-24T00:45:51.8770228Z if __name__ == "__main__": 2025-01-24T00:45:51.8770628Z main() 2025-01-24T00:45:51.8770824Z 2025-01-24T00:45:51.8856957Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-01-24T00:45:51.8857800Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-01-24T00:45:51.8885776Z shell: /usr/bin/bash -e {0} 2025-01-24T00:45:51.8886235Z env: 2025-01-24T00:45:51.8886778Z GITHUB_TOKEN: *** 2025-01-24T00:45:51.8887179Z ISSUE_NUMBER: 5132 2025-01-24T00:45:51.8887605Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:45:51.8888079Z ISSUE_OWNER: 2025-01-24T00:45:51.8888492Z CHECK_EXPERIMENTS: 2025-01-24T00:45:51.8888890Z PR_NUMBER: 2025-01-24T00:45:51.8889248Z ##[endgroup] 2025-01-24T00:45:52.2358811Z Defaulting to user installation because normal site-packages is not writeable 2025-01-24T00:45:52.5431779Z Collecting urllib3==1.26.18 2025-01-24T00:45:52.5880841Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-01-24T00:45:52.6107955Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 4.0 MB/s eta 0:00:00 2025-01-24T00:45:52.6447900Z Collecting PyGithub==2.3.0 2025-01-24T00:45:52.6535941Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-01-24T00:45:52.7095298Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-01-24T00:45:52.7167519Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl.metadata (8.6 kB) 2025-01-24T00:45:52.7208859Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-01-24T00:45:52.7226998Z Requirement already satisfied: pyjwt>=2.4.0 in /usr/lib/python3/dist-packages (from pyjwt[crypto]>=2.4.0->PyGithub==2.3.0) (2.7.0) 2025-01-24T00:45:52.7243488Z Requirement already satisfied: typing-extensions>=4.0.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (4.10.0) 2025-01-24T00:45:52.7584900Z Collecting Deprecated (from PyGithub==2.3.0) 2025-01-24T00:45:52.7752596Z Downloading Deprecated-1.2.15-py2.py3-none-any.whl.metadata (5.5 kB) 2025-01-24T00:45:52.7995343Z Requirement already satisfied: cryptography>=3.4.0 in /usr/lib/python3/dist-packages (from pyjwt[crypto]>=2.4.0->PyGithub==2.3.0) (41.0.7) 2025-01-24T00:45:52.9192545Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-01-24T00:45:52.9264027Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-01-24T00:45:53.0331332Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-01-24T00:45:53.0402456Z Downloading wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.4 kB) 2025-01-24T00:45:53.0693825Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-01-24T00:45:53.0766884Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-01-24T00:45:53.1147565Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-01-24T00:45:53.1262578Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 13.9 MB/s eta 0:00:00 2025-01-24T00:45:53.1335293Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-01-24T00:45:53.1585327Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 14.7 MB/s eta 0:00:00 2025-01-24T00:45:53.1683285Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (856 kB) 2025-01-24T00:45:53.2125774Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 19.8 MB/s eta 0:00:00 2025-01-24T00:45:53.2212578Z Downloading Deprecated-1.2.15-py2.py3-none-any.whl (9.9 kB) 2025-01-24T00:45:53.2330836Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-01-24T00:45:53.2539949Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 24.2 MB/s eta 0:00:00 2025-01-24T00:45:53.2630271Z Downloading wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (89 kB) 2025-01-24T00:45:53.2670411Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 29.3 MB/s eta 0:00:00 2025-01-24T00:45:53.2751768Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-01-24T00:45:53.2807695Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 28.4 MB/s eta 0:00:00 2025-01-24T00:45:53.5833992Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-01-24T00:45:54.1033266Z Successfully installed Deprecated-1.2.15 PyGithub-2.3.0 cffi-1.17.1 pycparser-2.22 pynacl-1.5.0 urllib3-1.26.18 wrapt-1.17.2 2025-01-24T00:45:54.1812344Z ##[group]Run curr_branch="ciflow/trunk/145539" 2025-01-24T00:45:54.1812776Z curr_branch="ciflow/trunk/145539" 2025-01-24T00:45:54.1813085Z curr_ref_type="tag" 2025-01-24T00:45:54.1813375Z echo "Current branch is '$curr_branch'" 2025-01-24T00:45:54.1813672Z  2025-01-24T00:45:54.1813906Z python3 runner_determinator.py \ 2025-01-24T00:45:54.1814228Z  --github-token "$GITHUB_TOKEN" \ 2025-01-24T00:45:54.1814785Z  --github-issue "$ISSUE_NUMBER" \ 2025-01-24T00:45:54.1815097Z  --github-branch "$curr_branch" \ 2025-01-24T00:45:54.1815448Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-01-24T00:45:54.1815769Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-01-24T00:45:54.1816091Z  --github-ref-type "$curr_ref_type" \ 2025-01-24T00:45:54.1816407Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-01-24T00:45:54.1816821Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-01-24T00:45:54.1817169Z  --pr-number "${PR_NUMBER}" 2025-01-24T00:45:54.1846089Z shell: /usr/bin/bash -e {0} 2025-01-24T00:45:54.1846378Z env: 2025-01-24T00:45:54.1847017Z GITHUB_TOKEN: *** 2025-01-24T00:45:54.1847263Z ISSUE_NUMBER: 5132 2025-01-24T00:45:54.1847513Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:45:54.1847798Z ISSUE_OWNER: 2025-01-24T00:45:54.1848031Z CHECK_EXPERIMENTS: 2025-01-24T00:45:54.1848257Z PR_NUMBER: 2025-01-24T00:45:54.1848469Z ##[endgroup] 2025-01-24T00:45:54.1896638Z Current branch is 'ciflow/trunk/145539' 2025-01-24T00:45:56.2630750Z INFO : Based on rollout percentage of 55%, enabling experiment lf. 2025-01-24T00:45:56.2631668Z INFO : Skipping experiment 'awsa100', as it is not a default experiment 2025-01-24T00:45:56.2632208Z INFO : Setting output: label-type='lf.' 2025-01-24T00:45:56.2931702Z Evaluate and set job outputs 2025-01-24T00:45:56.2938624Z Set output 'label-type' 2025-01-24T00:45:56.2940548Z Cleaning up orphan processes