2025-01-24T00:45:41.7031298Z Current runner version: '2.321.0' 2025-01-24T00:45:41.7055078Z ##[group]Operating System 2025-01-24T00:45:41.7055973Z Ubuntu 2025-01-24T00:45:41.7056525Z 24.04.1 2025-01-24T00:45:41.7057030Z LTS 2025-01-24T00:45:41.7057498Z ##[endgroup] 2025-01-24T00:45:41.7058055Z ##[group]Runner Image 2025-01-24T00:45:41.7058636Z Image: ubuntu-24.04 2025-01-24T00:45:41.7059136Z Version: 20250120.5.0 2025-01-24T00:45:41.7060197Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250120.5/images/ubuntu/Ubuntu2404-Readme.md 2025-01-24T00:45:41.7061610Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250120.5 2025-01-24T00:45:41.7062484Z ##[endgroup] 2025-01-24T00:45:41.7063040Z ##[group]Runner Image Provisioner 2025-01-24T00:45:41.7063625Z 2.0.417.1 2025-01-24T00:45:41.7064073Z ##[endgroup] 2025-01-24T00:45:41.7065182Z ##[group]GITHUB_TOKEN Permissions 2025-01-24T00:45:41.7067818Z Contents: read 2025-01-24T00:45:41.7068670Z Metadata: read 2025-01-24T00:45:41.7069799Z Packages: read 2025-01-24T00:45:41.7070778Z ##[endgroup] 2025-01-24T00:45:41.7074482Z Secret source: Actions 2025-01-24T00:45:41.7075770Z Prepare workflow directory 2025-01-24T00:45:41.7563676Z Prepare all required actions 2025-01-24T00:45:41.7619967Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (b2c89bc115123aea8e075e882ee121537ec92f89) 2025-01-24T00:45:41.7625045Z ##[group] Inputs 2025-01-24T00:45:41.7625913Z check_experiments: 2025-01-24T00:45:41.7626626Z triggering_actor: pytorch-bot[bot] 2025-01-24T00:45:41.7627242Z issue_owner: 2025-01-24T00:45:41.7627712Z curr_branch: ciflow/trunk/145539 2025-01-24T00:45:41.7628399Z curr_ref_type: tag 2025-01-24T00:45:41.7628928Z issue_number: 5132 2025-01-24T00:45:41.7629408Z ##[endgroup] 2025-01-24T00:45:41.7630080Z Complete job name: get-label-type / runner-determinator 2025-01-24T00:45:41.8337212Z ##[group]Run cat < runner_determinator.py 2025-01-24T00:45:41.8338856Z cat < runner_determinator.py 2025-01-24T00:45:41.8339447Z # flake8: noqa: G004 2025-01-24T00:45:41.8339891Z  2025-01-24T00:45:41.8340534Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-01-24T00:45:41.8341492Z # must be kept in sync. You can do it easily by running the following command: 2025-01-24T00:45:41.8342311Z # python .github/scripts/update_runner_determinator.py 2025-01-24T00:45:41.8342926Z  2025-01-24T00:45:41.8343277Z """ 2025-01-24T00:45:41.8343875Z This runner determinator is used to determine which set of runners to run a 2025-01-24T00:45:41.8344781Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-01-24T00:45:41.8345986Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-01-24T00:45:41.8346860Z of which runners should be used to run which job. 2025-01-24T00:45:41.8347439Z  2025-01-24T00:45:41.8348039Z The configuration has two parts, the settings and a list of opted-in users, 2025-01-24T00:45:41.8348969Z separated by a line containing "---". If the line is not present, the 2025-01-24T00:45:41.8349870Z settings are considered to be empty with only the second part, the user 2025-01-24T00:45:41.8350576Z list, defined. 2025-01-24T00:45:41.8350986Z  2025-01-24T00:45:41.8351559Z The first part is a YAML block that defines the rollout settings. This can be 2025-01-24T00:45:41.8352503Z used to define any settings that are needed to determine which runners to use. 2025-01-24T00:45:41.8353368Z It's fields are defined by the RolloutSettings class below. 2025-01-24T00:45:41.8353986Z  2025-01-24T00:45:41.8354564Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-01-24T00:45:41.8355564Z The user list is also a comma separated list of additional features or 2025-01-24T00:45:41.8356753Z experiments which the user could be opted in to. 2025-01-24T00:45:41.8357335Z  2025-01-24T00:45:41.8357754Z The user list has the following rules: 2025-01-24T00:45:41.8358273Z  2025-01-24T00:45:41.8358806Z - Users are GitHub usernames, which must start with the @ prefix 2025-01-24T00:45:41.8359679Z - Each user is also a comma-separated list of features/experiments to enable 2025-01-24T00:45:41.8360475Z - A "#" prefix opts the user out of all experiments 2025-01-24T00:45:41.8361042Z  2025-01-24T00:45:41.8361401Z Example config: 2025-01-24T00:45:41.8361899Z  # A list of experiments that can be opted into. 2025-01-24T00:45:41.8362604Z  # This defines the behavior they'll induce when opted into. 2025-01-24T00:45:41.8363251Z  # Expected syntax is: 2025-01-24T00:45:41.8363942Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-01-24T00:45:41.8364954Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-01-24T00:45:41.8365842Z  2025-01-24T00:45:41.8366207Z  experiments: 2025-01-24T00:45:41.8366676Z  lf: 2025-01-24T00:45:41.8367125Z  rollout_percent: 25 2025-01-24T00:45:41.8367611Z  all_branches: false 2025-01-24T00:45:41.8368105Z  default: true 2025-01-24T00:45:41.8368570Z  --- 2025-01-24T00:45:41.8368959Z  2025-01-24T00:45:41.8369329Z  # Opt-ins: 2025-01-24T00:45:41.8369983Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-01-24T00:45:41.8371034Z  # and specifying experiments to enable in a comma-separated list. 2025-01-24T00:45:41.8371857Z  # To always opt out of an experiment, prefix it with a "-". 2025-01-24T00:45:41.8372568Z  # Experiments should be from the above list. 2025-01-24T00:45:41.8373127Z  2025-01-24T00:45:41.8373503Z  @User1,-lf,split_build 2025-01-24T00:45:41.8373990Z  @User2,lf 2025-01-24T00:45:41.8374419Z  @User3,split_build 2025-01-24T00:45:41.8374864Z """ 2025-01-24T00:45:41.8375228Z  2025-01-24T00:45:41.8375678Z import json 2025-01-24T00:45:41.8376093Z import logging 2025-01-24T00:45:41.8376530Z import os 2025-01-24T00:45:41.8376928Z import random 2025-01-24T00:45:41.8377343Z import re 2025-01-24T00:45:41.8377970Z import sys 2025-01-24T00:45:41.8378513Z from argparse import ArgumentParser 2025-01-24T00:45:41.8379085Z from functools import lru_cache 2025-01-24T00:45:41.8379628Z from logging import LogRecord 2025-01-24T00:45:41.8380394Z from typing import Any, Dict, FrozenSet, Iterable, List, NamedTuple, Set, Tuple 2025-01-24T00:45:41.8381220Z from urllib.request import Request, urlopen 2025-01-24T00:45:41.8381774Z  2025-01-24T00:45:41.8382140Z import yaml 2025-01-24T00:45:41.8382578Z from github import Auth, Github 2025-01-24T00:45:41.8383124Z from github.Issue import Issue 2025-01-24T00:45:41.8383621Z  2025-01-24T00:45:41.8383970Z  2025-01-24T00:45:41.8384413Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-01-24T00:45:41.8385149Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-01-24T00:45:41.8386324Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-01-24T00:45:41.8387045Z  2025-01-24T00:45:41.8387496Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-01-24T00:45:41.8388098Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-01-24T00:45:41.8388665Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-01-24T00:45:41.8389469Z OPT_OUT_LABEL = "no-runner-experiments" 2025-01-24T00:45:41.8389995Z  2025-01-24T00:45:41.8390477Z SETTING_EXPERIMENTS = "experiments" 2025-01-24T00:45:41.8391000Z  2025-01-24T00:45:41.8391374Z LF_FLEET_EXPERIMENT = "lf" 2025-01-24T00:45:41.8391876Z CANARY_FLEET_SUFFIX = ".c" 2025-01-24T00:45:41.8392351Z  2025-01-24T00:45:41.8392696Z  2025-01-24T00:45:41.8393082Z class Experiment(NamedTuple): 2025-01-24T00:45:41.8393605Z  rollout_perc: float = ( 2025-01-24T00:45:41.8394304Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-01-24T00:45:41.8395048Z  ) 2025-01-24T00:45:41.8395563Z  all_branches: bool = ( 2025-01-24T00:45:41.8396271Z  False # If True, the experiment is also enabled on the exception branches 2025-01-24T00:45:41.8396958Z  ) 2025-01-24T00:45:41.8397349Z  default: bool = ( 2025-01-24T00:45:41.8397987Z  True # If True, the experiment is enabled by default for all queries 2025-01-24T00:45:41.8398685Z  ) 2025-01-24T00:45:41.8399053Z  2025-01-24T00:45:41.8399434Z  # Add more fields as needed 2025-01-24T00:45:41.8399915Z  2025-01-24T00:45:41.8400265Z  2025-01-24T00:45:41.8400635Z class Settings(NamedTuple): 2025-01-24T00:45:41.8401115Z  """ 2025-01-24T00:45:41.8401626Z  Settings for the experiments that can be opted into. 2025-01-24T00:45:41.8402226Z  """ 2025-01-24T00:45:41.8402591Z  2025-01-24T00:45:41.8403003Z  experiments: Dict[str, Experiment] = {} 2025-01-24T00:45:41.8403535Z  2025-01-24T00:45:41.8403876Z  2025-01-24T00:45:41.8404433Z class ColorFormatter(logging.Formatter): 2025-01-24T00:45:41.8405119Z  """Color codes the log messages based on the log level""" 2025-01-24T00:45:41.8405832Z  2025-01-24T00:45:41.8406194Z  COLORS = { 2025-01-24T00:45:41.8406652Z  "WARNING": "\033[33m", # Yellow 2025-01-24T00:45:41.8407190Z  "ERROR": "\033[31m", # Red 2025-01-24T00:45:41.8407709Z  "CRITICAL": "\033[31m", # Red 2025-01-24T00:45:41.8408247Z  "INFO": "\033[0m", # Reset 2025-01-24T00:45:41.8408874Z  "DEBUG": "\033[0m", # Reset 2025-01-24T00:45:41.8409372Z  } 2025-01-24T00:45:41.8409742Z  2025-01-24T00:45:41.8410175Z  def format(self, record: LogRecord) -> str: 2025-01-24T00:45:41.8410967Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-01-24T00:45:41.8411775Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-01-24T00:45:41.8412378Z  return super().format(record) 2025-01-24T00:45:41.8412892Z  2025-01-24T00:45:41.8413245Z  2025-01-24T00:45:41.8413636Z handler = logging.StreamHandler() 2025-01-24T00:45:41.8414404Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-01-24T00:45:41.8415139Z  2025-01-24T00:45:41.8415701Z log = logging.getLogger(os.path.basename(__file__)) 2025-01-24T00:45:41.8416313Z log.addHandler(handler) 2025-01-24T00:45:41.8416804Z log.setLevel(logging.INFO) 2025-01-24T00:45:41.8417283Z  2025-01-24T00:45:41.8417626Z  2025-01-24T00:45:41.8418093Z def set_github_output(key: str, value: str) -> None: 2025-01-24T00:45:41.8418676Z  """ 2025-01-24T00:45:41.8419227Z  Defines outputs of the github action that invokes this script 2025-01-24T00:45:41.8419873Z  """ 2025-01-24T00:45:41.8420271Z  if not GITHUB_OUTPUT: 2025-01-24T00:45:41.8421386Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-01-24T00:45:41.8422646Z  log.warning( 2025-01-24T00:45:41.8423563Z  "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:41.8424487Z  ) 2025-01-24T00:45:41.8424962Z  print(f"::set-output name={key}::{value}") 2025-01-24T00:45:41.8425737Z  return 2025-01-24T00:45:41.8426147Z  2025-01-24T00:45:41.8426556Z  with open(GITHUB_OUTPUT, "a") as f: 2025-01-24T00:45:41.8427165Z  log.info(f"Setting output: {key}='{value}'") 2025-01-24T00:45:41.8427751Z  f.write(f"{key}={value}\n") 2025-01-24T00:45:41.8428252Z  2025-01-24T00:45:41.8428609Z  2025-01-24T00:45:41.8429130Z def _str_comma_separated_to_set(value: str) -> FrozenSet[str]: 2025-01-24T00:45:41.8429796Z  return frozenset( 2025-01-24T00:45:41.8430466Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-01-24T00:45:41.8431154Z  ) 2025-01-24T00:45:41.8431521Z  2025-01-24T00:45:41.8431858Z  2025-01-24T00:45:41.8432239Z def parse_args() -> Any: 2025-01-24T00:45:41.8432859Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-01-24T00:45:41.8433759Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-01-24T00:45:41.8434539Z  parser.add_argument( 2025-01-24T00:45:41.8435041Z  "--github-issue-repo", 2025-01-24T00:45:41.8435645Z  type=str, 2025-01-24T00:45:41.8436090Z  required=False, 2025-01-24T00:45:41.8436736Z  default="pytorch/test-infra", 2025-01-24T00:45:41.8437317Z  help="GitHub repo to get the issue", 2025-01-24T00:45:41.8437843Z  ) 2025-01-24T00:45:41.8438242Z  parser.add_argument( 2025-01-24T00:45:41.8438728Z  "--github-repo", 2025-01-24T00:45:41.8439195Z  type=str, 2025-01-24T00:45:41.8439638Z  required=True, 2025-01-24T00:45:41.8440153Z  help="GitHub repo where CI is running", 2025-01-24T00:45:41.8440682Z  ) 2025-01-24T00:45:41.8441072Z  parser.add_argument( 2025-01-24T00:45:41.8441736Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-01-24T00:45:41.8442413Z  ) 2025-01-24T00:45:41.8442803Z  parser.add_argument( 2025-01-24T00:45:41.8443495Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-01-24T00:45:41.8444189Z  ) 2025-01-24T00:45:41.8444584Z  parser.add_argument( 2025-01-24T00:45:41.8445277Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-01-24T00:45:41.8446086Z  ) 2025-01-24T00:45:41.8446471Z  parser.add_argument( 2025-01-24T00:45:41.8447186Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-01-24T00:45:41.8447915Z  ) 2025-01-24T00:45:41.8448363Z  parser.add_argument( 2025-01-24T00:45:41.8448852Z  "--github-ref-type", 2025-01-24T00:45:41.8449347Z  type=str, 2025-01-24T00:45:41.8449794Z  required=True, 2025-01-24T00:45:41.8450347Z  help="Current GitHub ref type, branch or tag", 2025-01-24T00:45:41.8450918Z  ) 2025-01-24T00:45:41.8451308Z  parser.add_argument( 2025-01-24T00:45:41.8451810Z  "--eligible-experiments", 2025-01-24T00:45:41.8452371Z  type=_str_comma_separated_to_set, 2025-01-24T00:45:41.8452908Z  required=False, 2025-01-24T00:45:41.8453615Z  default="", 2025-01-24T00:45:41.8454703Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-01-24T00:45:41.8455745Z  ) 2025-01-24T00:45:41.8456142Z  parser.add_argument( 2025-01-24T00:45:41.8456621Z  "--pr-number", 2025-01-24T00:45:41.8457088Z  type=str, 2025-01-24T00:45:41.8457534Z  required=False, 2025-01-24T00:45:41.8457996Z  default="", 2025-01-24T00:45:41.8458528Z  help="the optional PR number where this is run", 2025-01-24T00:45:41.8459111Z  ) 2025-01-24T00:45:41.8459472Z  2025-01-24T00:45:41.8459861Z  return parser.parse_args() 2025-01-24T00:45:41.8460356Z  2025-01-24T00:45:41.8460707Z  2025-01-24T00:45:41.8461150Z def get_gh_client(github_token: str) -> Github: 2025-01-24T00:45:41.8461756Z  auth = Auth.Token(github_token) 2025-01-24T00:45:41.8462290Z  return Github(auth=auth) 2025-01-24T00:45:41.8462818Z  2025-01-24T00:45:41.8463216Z  2025-01-24T00:45:41.8463767Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: 2025-01-24T00:45:41.8464482Z  repo = gh.get_repo(repo) 2025-01-24T00:45:41.8465077Z  return repo.get_issue(number=issue_num) 2025-01-24T00:45:41.8465739Z  2025-01-24T00:45:41.8466137Z  2025-01-24T00:45:41.8466561Z def get_potential_pr_author( 2025-01-24T00:45:41.8467292Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-01-24T00:45:41.8468008Z ) -> str: 2025-01-24T00:45:41.8468574Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-01-24T00:45:41.8469525Z  # Fetch the actual username from the original PR. The PR number is 2025-01-24T00:45:41.8470312Z  # embedded in the tag name: ciflow// 2025-01-24T00:45:41.8471110Z  2025-01-24T00:45:41.8471690Z  gh = get_gh_client(github_token) 2025-01-24T00:45:41.8472436Z  2025-01-24T00:45:41.8473144Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-01-24T00:45:41.8474118Z  split_tag = ref_name.split("/") 2025-01-24T00:45:41.8474937Z  if ( 2025-01-24T00:45:41.8475866Z  len(split_tag) == 3 2025-01-24T00:45:41.8476685Z  and split_tag[0] == "ciflow" 2025-01-24T00:45:41.8477584Z  and split_tag[2].isnumeric() 2025-01-24T00:45:41.8478438Z  ): 2025-01-24T00:45:41.8479102Z  pr_number = split_tag[2] 2025-01-24T00:45:41.8479946Z  try: 2025-01-24T00:45:41.8480768Z  repository = gh.get_repo(repo) 2025-01-24T00:45:41.8481786Z  pull = repository.get_pull(number=int(pr_number)) 2025-01-24T00:45:41.8482837Z  except Exception as e: 2025-01-24T00:45:41.8483750Z  raise Exception( # noqa: TRY002 2025-01-24T00:45:41.8484469Z  f"issue with pull request {pr_number} from repo {repository}" 2025-01-24T00:45:41.8485143Z  ) from e 2025-01-24T00:45:41.8485903Z  return pull.user.login 2025-01-24T00:45:41.8486538Z  # In all other cases, return the original input username 2025-01-24T00:45:41.8487151Z  return username 2025-01-24T00:45:41.8487592Z  2025-01-24T00:45:41.8487939Z  2025-01-24T00:45:41.8488369Z def is_exception_branch(branch: str) -> bool: 2025-01-24T00:45:41.8488921Z  """ 2025-01-24T00:45:41.8489629Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-01-24T00:45:41.8490414Z  """ 2025-01-24T00:45:41.8491199Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-01-24T00:45:41.8491889Z  2025-01-24T00:45:41.8492248Z  2025-01-24T00:45:41.8492652Z def load_yaml(yaml_text: str) -> Any: 2025-01-24T00:45:41.8493174Z  try: 2025-01-24T00:45:41.8493596Z  data = yaml.safe_load(yaml_text) 2025-01-24T00:45:41.8494122Z  return data 2025-01-24T00:45:41.8494588Z  except yaml.YAMLError: 2025-01-24T00:45:41.8495163Z  log.exception("Error loading YAML") 2025-01-24T00:45:41.8495869Z  raise 2025-01-24T00:45:41.8496277Z  2025-01-24T00:45:41.8496617Z  2025-01-24T00:45:41.8497247Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> Tuple[str, str]: 2025-01-24T00:45:41.8498007Z  """ 2025-01-24T00:45:41.8498666Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-01-24T00:45:41.8499427Z  2025-01-24T00:45:41.8499982Z  If the issue body contains "---" then the text above that is the settings 2025-01-24T00:45:41.8500771Z  and the text below is the list of opted in users. 2025-01-24T00:45:41.8501342Z  2025-01-24T00:45:41.8501926Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-01-24T00:45:41.8502632Z  """ 2025-01-24T00:45:41.8503118Z  rollout_state_parts = rollout_state.split("---") 2025-01-24T00:45:41.8503726Z  if len(rollout_state_parts) >= 2: 2025-01-24T00:45:41.8504362Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-01-24T00:45:41.8504973Z  else: 2025-01-24T00:45:41.8505696Z  return "", rollout_state 2025-01-24T00:45:41.8506213Z  2025-01-24T00:45:41.8506564Z  2025-01-24T00:45:41.8506974Z class UserOptins(Dict[str, List[str]]): 2025-01-24T00:45:41.8507508Z  """ 2025-01-24T00:45:41.8508069Z  Dictionary of users with a list of features they have opted into 2025-01-24T00:45:41.8508719Z  """ 2025-01-24T00:45:41.8509084Z  2025-01-24T00:45:41.8509429Z  2025-01-24T00:45:41.8509975Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-01-24T00:45:41.8510639Z  """ 2025-01-24T00:45:41.8511395Z  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:41.8512233Z  2025-01-24T00:45:41.8513056Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-01-24T00:45:41.8514068Z  - Example line: "@User1,lf,split_build" 2025-01-24T00:45:41.8514774Z  - A "#" prefix indicates the user is opted out of all experiments 2025-01-24T00:45:41.8515618Z  2025-01-24T00:45:41.8515965Z  2025-01-24T00:45:41.8516306Z  """ 2025-01-24T00:45:41.8516710Z  optins = UserOptins() 2025-01-24T00:45:41.8517244Z  for user in user_optin_text.split("\n"): 2025-01-24T00:45:41.8517826Z  user = user.strip("\r\n\t -") 2025-01-24T00:45:41.8518421Z  if not user or not user.startswith("@"): 2025-01-24T00:45:41.8518993Z  # Not a valid user. Skip 2025-01-24T00:45:41.8519508Z  continue 2025-01-24T00:45:41.8519939Z  2025-01-24T00:45:41.8520284Z  if user: 2025-01-24T00:45:41.8520774Z  usr_name = user.split(",")[0].strip("@") 2025-01-24T00:45:41.8521495Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-01-24T00:45:41.8522137Z  2025-01-24T00:45:41.8522494Z  return optins 2025-01-24T00:45:41.8523051Z  2025-01-24T00:45:41.8523390Z  2025-01-24T00:45:41.8523898Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-01-24T00:45:41.8524528Z  """ 2025-01-24T00:45:41.8524964Z  Check if the experiment name is valid. 2025-01-24T00:45:41.8525670Z  A valid name: 2025-01-24T00:45:41.8526377Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-01-24T00:45:41.8527334Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-01-24T00:45:41.8528060Z  - Cannot contain spaces 2025-01-24T00:45:41.8528551Z  """ 2025-01-24T00:45:41.8528916Z  2025-01-24T00:45:41.8529390Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-01-24T00:45:41.8530125Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-01-24T00:45:41.8530726Z  2025-01-24T00:45:41.8531084Z  if valid: 2025-01-24T00:45:41.8531509Z  return True 2025-01-24T00:45:41.8531938Z  2025-01-24T00:45:41.8532293Z  log.error( 2025-01-24T00:45:41.8533747Z  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:41.8535285Z  ) 2025-01-24T00:45:41.8535853Z  return False 2025-01-24T00:45:41.8536272Z  2025-01-24T00:45:41.8536610Z  2025-01-24T00:45:41.8537125Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-01-24T00:45:41.8537770Z  """ 2025-01-24T00:45:41.8538528Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-01-24T00:45:41.8539269Z  """ 2025-01-24T00:45:41.8539639Z  try: 2025-01-24T00:45:41.8540034Z  if settings_text: 2025-01-24T00:45:41.8540804Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-01-24T00:45:41.8541608Z  # for easy reading 2025-01-24T00:45:41.8542441Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-01-24T00:45:41.8543348Z  # the backtick character in shell commands. 2025-01-24T00:45:41.8543981Z  backtick = chr(96) # backtick character 2025-01-24T00:45:41.8544684Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-01-24T00:45:41.8545576Z  settings = load_yaml(settings_text) 2025-01-24T00:45:41.8546110Z  2025-01-24T00:45:41.8546730Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-01-24T00:45:41.8547654Z  experiments = {} 2025-01-24T00:45:41.8548127Z  2025-01-24T00:45:41.8548752Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-01-24T00:45:41.8549531Z  if not is_valid_experiment_name(exp_name): 2025-01-24T00:45:41.8550635Z  # 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:41.8551681Z  continue 2025-01-24T00:45:41.8552146Z  2025-01-24T00:45:41.8552520Z  valid_settings = {} 2025-01-24T00:45:41.8553061Z  for setting in exp_settings: 2025-01-24T00:45:41.8553666Z  if setting not in Experiment._fields: 2025-01-24T00:45:41.8554237Z  log.warning( 2025-01-24T00:45:41.8554974Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-01-24T00:45:41.8555952Z  ) 2025-01-24T00:45:41.8556410Z  else: 2025-01-24T00:45:41.8556960Z  valid_settings[setting] = exp_settings[setting] 2025-01-24T00:45:41.8557546Z  2025-01-24T00:45:41.8558033Z  experiments[exp_name] = Experiment(**valid_settings) 2025-01-24T00:45:41.8558688Z  return Settings(experiments) 2025-01-24T00:45:41.8559212Z  2025-01-24T00:45:41.8559572Z  except Exception: 2025-01-24T00:45:41.8560094Z  log.exception("Failed to parse settings") 2025-01-24T00:45:41.8560642Z  2025-01-24T00:45:41.8561006Z  return Settings() 2025-01-24T00:45:41.8561441Z  2025-01-24T00:45:41.8561783Z  2025-01-24T00:45:41.8562246Z def parse_settings(rollout_state: str) -> Settings: 2025-01-24T00:45:41.8562832Z  """ 2025-01-24T00:45:41.8563314Z  Parse settings, if any, from the rollout state. 2025-01-24T00:45:41.8563880Z  2025-01-24T00:45:41.8564419Z  If the issue body contains "---" then the text above that is the settings 2025-01-24T00:45:41.8565212Z  and the text below is the list of opted in users. 2025-01-24T00:45:41.8565872Z  2025-01-24T00:45:41.8566471Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-01-24T00:45:41.8567204Z  """ 2025-01-24T00:45:41.8567804Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:45:41.8568596Z  return parse_settings_from_text(settings_text) 2025-01-24T00:45:41.8569162Z  2025-01-24T00:45:41.8569624Z  2025-01-24T00:45:41.8570085Z def parse_users(rollout_state: str) -> UserOptins: 2025-01-24T00:45:41.8570664Z  """ 2025-01-24T00:45:41.8571098Z  Parse users from the rollout state. 2025-01-24T00:45:41.8571614Z  2025-01-24T00:45:41.8571958Z  """ 2025-01-24T00:45:41.8572527Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:45:41.8573296Z  return parse_user_opt_in_from_text(users_text) 2025-01-24T00:45:41.8573853Z  2025-01-24T00:45:41.8574202Z  2025-01-24T00:45:41.8574842Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:45:41.8575701Z  """ 2025-01-24T00:45:41.8576160Z  Check if a user is opted into an experiment 2025-01-24T00:45:41.8576710Z  """ 2025-01-24T00:45:41.8577216Z  return experiment_name in user_optins.get(user, []) 2025-01-24T00:45:41.8577817Z  2025-01-24T00:45:41.8578149Z  2025-01-24T00:45:41.8578783Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:45:41.8579554Z  """ 2025-01-24T00:45:41.8580044Z  Check if a user explicitly opted out of an experiment 2025-01-24T00:45:41.8580638Z  """ 2025-01-24T00:45:41.8581181Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-01-24T00:45:41.8581906Z  experiment_optout = "-" + experiment_name 2025-01-24T00:45:41.8582576Z  if experiment_optout not in user_optins.get(user, []): 2025-01-24T00:45:41.8583186Z  return False 2025-01-24T00:45:41.8583621Z  2025-01-24T00:45:41.8584083Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-01-24T00:45:41.8584706Z  log.warning( 2025-01-24T00:45:41.8585647Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-01-24T00:45:41.8586515Z  ) 2025-01-24T00:45:41.8587020Z  2025-01-24T00:45:41.8587372Z  return True 2025-01-24T00:45:41.8587776Z  2025-01-24T00:45:41.8588109Z  2025-01-24T00:45:41.8588464Z def get_runner_prefix( 2025-01-24T00:45:41.8588929Z  rollout_state: str, 2025-01-24T00:45:41.8589431Z  workflow_requestors: Iterable[str], 2025-01-24T00:45:41.8589957Z  branch: str, 2025-01-24T00:45:41.8590494Z  eligible_experiments: FrozenSet[str] = frozenset(), 2025-01-24T00:45:41.8591117Z  is_canary: bool = False, 2025-01-24T00:45:41.8591599Z ) -> str: 2025-01-24T00:45:41.8592054Z  settings = parse_settings(rollout_state) 2025-01-24T00:45:41.8592656Z  user_optins = parse_users(rollout_state) 2025-01-24T00:45:41.8593188Z  2025-01-24T00:45:41.8593546Z  fleet_prefix = "" 2025-01-24T00:45:41.8594003Z  prefixes = [] 2025-01-24T00:45:41.8594688Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-01-24T00:45:41.8595780Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-01-24T00:45:41.8596502Z  log.info( 2025-01-24T00:45:41.8597228Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-01-24T00:45:41.8597978Z  ) 2025-01-24T00:45:41.8598390Z  continue 2025-01-24T00:45:41.8598815Z  2025-01-24T00:45:41.8599193Z  if eligible_experiments: 2025-01-24T00:45:41.8599799Z  if experiment_name not in eligible_experiments: 2025-01-24T00:45:41.8600504Z  exp_list = ", ".join(eligible_experiments) 2025-01-24T00:45:41.8601228Z  log.info( 2025-01-24T00:45:41.8602098Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-01-24T00:45:41.8602988Z  ) 2025-01-24T00:45:41.8603470Z  continue 2025-01-24T00:45:41.8604038Z  elif not experiment_settings.default: 2025-01-24T00:45:41.8604630Z  log.info( 2025-01-24T00:45:41.8605496Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-01-24T00:45:41.8606290Z  ) 2025-01-24T00:45:41.8606733Z  continue 2025-01-24T00:45:41.8607158Z  2025-01-24T00:45:41.8607663Z  # Is any workflow_requestor opted out to this experiment? 2025-01-24T00:45:41.8608421Z  opted_out_users = [ 2025-01-24T00:45:41.8608903Z  requestor 2025-01-24T00:45:41.8609409Z  for requestor in workflow_requestors 2025-01-24T00:45:41.8610105Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-01-24T00:45:41.8610746Z  ] 2025-01-24T00:45:41.8611118Z  2025-01-24T00:45:41.8611482Z  if opted_out_users: 2025-01-24T00:45:41.8611967Z  log.info( 2025-01-24T00:45:41.8612622Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-01-24T00:45:41.8613329Z  ) 2025-01-24T00:45:41.8613740Z  continue 2025-01-24T00:45:41.8614187Z  2025-01-24T00:45:41.8614654Z  # Is any workflow_requestor opted in to this experiment? 2025-01-24T00:45:41.8615276Z  opted_in_users = [ 2025-01-24T00:45:41.8616017Z  requestor 2025-01-24T00:45:41.8616524Z  for requestor in workflow_requestors 2025-01-24T00:45:41.8617219Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-01-24T00:45:41.8617846Z  ] 2025-01-24T00:45:41.8618382Z  2025-01-24T00:45:41.8618739Z  enabled = False 2025-01-24T00:45:41.8619202Z  if opted_in_users: 2025-01-24T00:45:41.8619671Z  log.info( 2025-01-24T00:45:41.8620353Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-01-24T00:45:41.8621031Z  ) 2025-01-24T00:45:41.8621440Z  enabled = True 2025-01-24T00:45:41.8621889Z  2025-01-24T00:45:41.8622305Z  elif experiment_settings.rollout_perc: 2025-01-24T00:45:41.8623144Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-01-24T00:45:41.8624091Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-01-24T00:45:41.8624743Z  log.info( 2025-01-24T00:45:41.8625759Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-01-24T00:45:41.8626680Z  ) 2025-01-24T00:45:41.8627130Z  enabled = True 2025-01-24T00:45:41.8627608Z  2025-01-24T00:45:41.8627967Z  if enabled: 2025-01-24T00:45:41.8628445Z  label = experiment_name 2025-01-24T00:45:41.8629017Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-01-24T00:45:41.8629858Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-01-24T00:45:41.8630773Z  # - If it's enabled, then we always list it's prefix first 2025-01-24T00:45:41.8631761Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-01-24T00:45:41.8632463Z  if is_canary: 2025-01-24T00:45:41.8632993Z  label += CANARY_FLEET_SUFFIX 2025-01-24T00:45:41.8633559Z  fleet_prefix = label 2025-01-24T00:45:41.8634129Z  else: 2025-01-24T00:45:41.8634978Z  prefixes.append(label) 2025-01-24T00:45:41.8635759Z  2025-01-24T00:45:41.8636121Z  if len(prefixes) > 1: 2025-01-24T00:45:41.8636592Z  log.error( 2025-01-24T00:45:41.8637649Z  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:41.8638737Z  ) 2025-01-24T00:45:41.8639152Z  prefixes = prefixes[:1] 2025-01-24T00:45:41.8639643Z  2025-01-24T00:45:41.8640009Z  # Fleet always comes first 2025-01-24T00:45:41.8640523Z  if fleet_prefix: 2025-01-24T00:45:41.8641008Z  prefixes.insert(0, fleet_prefix) 2025-01-24T00:45:41.8641511Z  2025-01-24T00:45:41.8641974Z  return ".".join(prefixes) + "." if prefixes else "" 2025-01-24T00:45:41.8642542Z  2025-01-24T00:45:41.8642899Z  2025-01-24T00:45:41.8643541Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-01-24T00:45:41.8644307Z  """ 2025-01-24T00:45:41.8644919Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-01-24T00:45:41.8645748Z  2025-01-24T00:45:41.8646338Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-01-24T00:45:41.8647039Z  """ 2025-01-24T00:45:41.8647465Z  gh = get_gh_client(github_token) 2025-01-24T00:45:41.8648041Z  issue = get_issue(gh, repo, issue_num) 2025-01-24T00:45:41.8648703Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-01-24T00:45:41.8649292Z  2025-01-24T00:45:41.8649629Z  2025-01-24T00:45:41.8650412Z def download_json(url: str, headers: Dict[str, str], num_retries: int = 3) -> Any: 2025-01-24T00:45:41.8651174Z  for _ in range(num_retries): 2025-01-24T00:45:41.8651667Z  try: 2025-01-24T00:45:41.8652135Z  req = Request(url=url, headers=headers) 2025-01-24T00:45:41.8652836Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-01-24T00:45:41.8653491Z  return json.loads(content) 2025-01-24T00:45:41.8654028Z  except Exception as e: 2025-01-24T00:45:41.8654614Z  log.warning(f"Could not download {url}: {e}") 2025-01-24T00:45:41.8655182Z  2025-01-24T00:45:41.8656086Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-01-24T00:45:41.8656821Z  return {} 2025-01-24T00:45:41.8657229Z  2025-01-24T00:45:41.8657591Z  2025-01-24T00:45:41.8657958Z @lru_cache(maxsize=None) 2025-01-24T00:45:41.8658700Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> Dict[str, Any]: 2025-01-24T00:45:41.8659457Z  """ 2025-01-24T00:45:41.8659869Z  Dynamically get PR information 2025-01-24T00:45:41.8660368Z  """ 2025-01-24T00:45:41.8660895Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-01-24T00:45:41.8661524Z  headers = { 2025-01-24T00:45:41.8662017Z  "Accept": "application/vnd.github.v3+json", 2025-01-24T00:45:41.8662642Z  "Authorization": f"token {github_token}", 2025-01-24T00:45:41.8663175Z  } 2025-01-24T00:45:41.8663628Z  json_response: Dict[str, Any] = download_json( 2025-01-24T00:45:41.8664412Z  url=f"{github_api}/issues/{pr_number}", 2025-01-24T00:45:41.8664968Z  headers=headers, 2025-01-24T00:45:41.8665655Z  ) 2025-01-24T00:45:41.8666040Z  2025-01-24T00:45:41.8666402Z  if not json_response: 2025-01-24T00:45:41.8667038Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-01-24T00:45:41.8667668Z  return {} 2025-01-24T00:45:41.8668085Z  2025-01-24T00:45:41.8668446Z  return json_response 2025-01-24T00:45:41.8668897Z  2025-01-24T00:45:41.8669237Z  2025-01-24T00:45:41.8669834Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> Set[str]: 2025-01-24T00:45:41.8670550Z  """ 2025-01-24T00:45:41.8671099Z  Dynamically get the latest list of labels from the pull request 2025-01-24T00:45:41.8671744Z  """ 2025-01-24T00:45:41.8672267Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-01-24T00:45:41.8672872Z  return { 2025-01-24T00:45:41.8673486Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-01-24T00:45:41.8674172Z  } 2025-01-24T00:45:41.8674533Z  2025-01-24T00:45:41.8674897Z  2025-01-24T00:45:41.8675316Z def main() -> None: 2025-01-24T00:45:41.8675903Z  args = parse_args() 2025-01-24T00:45:41.8676367Z  2025-01-24T00:45:41.8676799Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-01-24T00:45:41.8677339Z  2025-01-24T00:45:41.8677803Z  # Check if the PR is opt-out 2025-01-24T00:45:41.8678599Z  if args.pr_number: 2025-01-24T00:45:41.8679759Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-01-24T00:45:41.8680961Z  if OPT_OUT_LABEL in labels: 2025-01-24T00:45:41.8681794Z  log.info( 2025-01-24T00:45:41.8683063Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-01-24T00:45:41.8684079Z  ) 2025-01-24T00:45:41.8684675Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:45:41.8685588Z  sys.exit() 2025-01-24T00:45:41.8686052Z  2025-01-24T00:45:41.8686393Z  try: 2025-01-24T00:45:41.8686870Z  rollout_state = get_rollout_state_from_issue( 2025-01-24T00:45:41.8687615Z  args.github_token, args.github_issue_repo, args.github_issue 2025-01-24T00:45:41.8688251Z  ) 2025-01-24T00:45:41.8688627Z  2025-01-24T00:45:41.8689025Z  username = get_potential_pr_author( 2025-01-24T00:45:41.8689573Z  args.github_token, 2025-01-24T00:45:41.8690081Z  args.github_repo, 2025-01-24T00:45:41.8690588Z  args.github_actor, 2025-01-24T00:45:41.8691091Z  args.github_ref_type, 2025-01-24T00:45:41.8691605Z  args.github_branch, 2025-01-24T00:45:41.8692099Z  ) 2025-01-24T00:45:41.8692467Z  2025-01-24T00:45:41.8692954Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-01-24T00:45:41.8693573Z  2025-01-24T00:45:41.8693984Z  runner_label_prefix = get_runner_prefix( 2025-01-24T00:45:41.8694538Z  rollout_state, 2025-01-24T00:45:41.8695092Z  (args.github_issue_owner, username), 2025-01-24T00:45:41.8695916Z  args.github_branch, 2025-01-24T00:45:41.8696464Z  args.eligible_experiments, 2025-01-24T00:45:41.8696990Z  is_canary, 2025-01-24T00:45:41.8697788Z  ) 2025-01-24T00:45:41.8698427Z  2025-01-24T00:45:41.8699063Z  except Exception as e: 2025-01-24T00:45:41.8699938Z  log.error( 2025-01-24T00:45:41.8700685Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-01-24T00:45:41.8701758Z  ) 2025-01-24T00:45:41.8702416Z  2025-01-24T00:45:41.8703022Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:45:41.8703686Z  2025-01-24T00:45:41.8704019Z  2025-01-24T00:45:41.8704381Z if __name__ == "__main__": 2025-01-24T00:45:41.8704855Z  main() 2025-01-24T00:45:41.8705237Z  2025-01-24T00:45:41.8705883Z EOF 2025-01-24T00:45:41.8706251Z  2025-01-24T00:45:41.8706620Z cat runner_determinator.py 2025-01-24T00:45:41.9074881Z shell: /usr/bin/bash -e {0} 2025-01-24T00:45:41.9076083Z env: 2025-01-24T00:45:41.9076781Z GITHUB_TOKEN: *** 2025-01-24T00:45:41.9077177Z ISSUE_NUMBER: 5132 2025-01-24T00:45:41.9077627Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:45:41.9078124Z ISSUE_OWNER: 2025-01-24T00:45:41.9078496Z CHECK_EXPERIMENTS: 2025-01-24T00:45:41.9078900Z PR_NUMBER: 2025-01-24T00:45:41.9079270Z ##[endgroup] 2025-01-24T00:45:41.9307293Z # flake8: noqa: G004 2025-01-24T00:45:41.9307699Z 2025-01-24T00:45:41.9308163Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-01-24T00:45:41.9369223Z # must be kept in sync. You can do it easily by running the following command: 2025-01-24T00:45:41.9370603Z # python .github/scripts/update_runner_determinator.py 2025-01-24T00:45:41.9371088Z 2025-01-24T00:45:41.9371238Z """ 2025-01-24T00:45:41.9371797Z This runner determinator is used to determine which set of runners to run a 2025-01-24T00:45:41.9372664Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-01-24T00:45:41.9373542Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-01-24T00:45:41.9374345Z of which runners should be used to run which job. 2025-01-24T00:45:41.9374727Z 2025-01-24T00:45:41.9375103Z The configuration has two parts, the settings and a list of opted-in users, 2025-01-24T00:45:41.9376485Z separated by a line containing "---". If the line is not present, the 2025-01-24T00:45:41.9377372Z settings are considered to be empty with only the second part, the user 2025-01-24T00:45:41.9378043Z list, defined. 2025-01-24T00:45:41.9378261Z 2025-01-24T00:45:41.9378609Z The first part is a YAML block that defines the rollout settings. This can be 2025-01-24T00:45:41.9379510Z used to define any settings that are needed to determine which runners to use. 2025-01-24T00:45:41.9380313Z It's fields are defined by the RolloutSettings class below. 2025-01-24T00:45:41.9380741Z 2025-01-24T00:45:41.9381091Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-01-24T00:45:41.9381931Z The user list is also a comma separated list of additional features or 2025-01-24T00:45:41.9382631Z experiments which the user could be opted in to. 2025-01-24T00:45:41.9383013Z 2025-01-24T00:45:41.9383200Z The user list has the following rules: 2025-01-24T00:45:41.9383536Z 2025-01-24T00:45:41.9383832Z - Users are GitHub usernames, which must start with the @ prefix 2025-01-24T00:45:41.9384659Z - Each user is also a comma-separated list of features/experiments to enable 2025-01-24T00:45:41.9385991Z - A "#" prefix opts the user out of all experiments 2025-01-24T00:45:41.9386433Z 2025-01-24T00:45:41.9386602Z Example config: 2025-01-24T00:45:41.9387040Z # A list of experiments that can be opted into. 2025-01-24T00:45:41.9387680Z # This defines the behavior they'll induce when opted into. 2025-01-24T00:45:41.9388271Z # Expected syntax is: 2025-01-24T00:45:41.9388891Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-01-24T00:45:41.9389833Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-01-24T00:45:41.9390581Z 2025-01-24T00:45:41.9390755Z experiments: 2025-01-24T00:45:41.9391128Z lf: 2025-01-24T00:45:41.9391482Z rollout_percent: 25 2025-01-24T00:45:41.9391918Z all_branches: false 2025-01-24T00:45:41.9392344Z default: true 2025-01-24T00:45:41.9392734Z --- 2025-01-24T00:45:41.9392926Z 2025-01-24T00:45:41.9393081Z # Opt-ins: 2025-01-24T00:45:41.9393634Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-01-24T00:45:41.9394466Z # and specifying experiments to enable in a comma-separated list. 2025-01-24T00:45:41.9395203Z # To always opt out of an experiment, prefix it with a "-". 2025-01-24T00:45:41.9395970Z # Experiments should be from the above list. 2025-01-24T00:45:41.9396341Z 2025-01-24T00:45:41.9396517Z @User1,-lf,split_build 2025-01-24T00:45:41.9396930Z @User2,lf 2025-01-24T00:45:41.9397301Z @User3,split_build 2025-01-24T00:45:41.9397700Z """ 2025-01-24T00:45:41.9397879Z 2025-01-24T00:45:41.9398040Z import json 2025-01-24T00:45:41.9398397Z import logging 2025-01-24T00:45:41.9398758Z import os 2025-01-24T00:45:41.9399104Z import random 2025-01-24T00:45:41.9399467Z import re 2025-01-24T00:45:41.9399796Z import sys 2025-01-24T00:45:41.9400187Z from argparse import ArgumentParser 2025-01-24T00:45:41.9400683Z from functools import lru_cache 2025-01-24T00:45:41.9401145Z from logging import LogRecord 2025-01-24T00:45:41.9401796Z from typing import Any, Dict, FrozenSet, Iterable, List, NamedTuple, Set, Tuple 2025-01-24T00:45:41.9402542Z from urllib.request import Request, urlopen 2025-01-24T00:45:41.9402890Z 2025-01-24T00:45:41.9403050Z import yaml 2025-01-24T00:45:41.9403423Z from github import Auth, Github 2025-01-24T00:45:41.9403885Z from github.Issue import Issue 2025-01-24T00:45:41.9404173Z 2025-01-24T00:45:41.9404179Z 2025-01-24T00:45:41.9404384Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-01-24T00:45:41.9405046Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-01-24T00:45:41.9405969Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-01-24T00:45:41.9406501Z 2025-01-24T00:45:41.9406856Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-01-24T00:45:41.9407395Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-01-24T00:45:41.9407888Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-01-24T00:45:41.9408411Z OPT_OUT_LABEL = "no-runner-experiments" 2025-01-24T00:45:41.9408750Z 2025-01-24T00:45:41.9408936Z SETTING_EXPERIMENTS = "experiments" 2025-01-24T00:45:41.9409254Z 2025-01-24T00:45:41.9409428Z LF_FLEET_EXPERIMENT = "lf" 2025-01-24T00:45:41.9409866Z CANARY_FLEET_SUFFIX = ".c" 2025-01-24T00:45:41.9410133Z 2025-01-24T00:45:41.9410139Z 2025-01-24T00:45:41.9410320Z class Experiment(NamedTuple): 2025-01-24T00:45:41.9410784Z rollout_perc: float = ( 2025-01-24T00:45:41.9411398Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-01-24T00:45:41.9412042Z ) 2025-01-24T00:45:41.9412392Z all_branches: bool = ( 2025-01-24T00:45:41.9412988Z False # If True, the experiment is also enabled on the exception branches 2025-01-24T00:45:41.9413626Z ) 2025-01-24T00:45:41.9413973Z default: bool = ( 2025-01-24T00:45:41.9414518Z True # If True, the experiment is enabled by default for all queries 2025-01-24T00:45:41.9415128Z ) 2025-01-24T00:45:41.9415313Z 2025-01-24T00:45:41.9415579Z # Add more fields as needed 2025-01-24T00:45:41.9415871Z 2025-01-24T00:45:41.9415876Z 2025-01-24T00:45:41.9416054Z class Settings(NamedTuple): 2025-01-24T00:45:41.9416478Z """ 2025-01-24T00:45:41.9416915Z Settings for the experiments that can be opted into. 2025-01-24T00:45:41.9417464Z """ 2025-01-24T00:45:41.9417650Z 2025-01-24T00:45:41.9417845Z experiments: Dict[str, Experiment] = {} 2025-01-24T00:45:41.9418196Z 2025-01-24T00:45:41.9418202Z 2025-01-24T00:45:41.9418399Z class ColorFormatter(logging.Formatter): 2025-01-24T00:45:41.9419000Z """Color codes the log messages based on the log level""" 2025-01-24T00:45:41.9419535Z 2025-01-24T00:45:41.9419699Z COLORS = { 2025-01-24T00:45:41.9420081Z "WARNING": "\033[33m", # Yellow 2025-01-24T00:45:41.9420576Z "ERROR": "\033[31m", # Red 2025-01-24T00:45:41.9421031Z "CRITICAL": "\033[31m", # Red 2025-01-24T00:45:41.9421506Z "INFO": "\033[0m", # Reset 2025-01-24T00:45:41.9421964Z "DEBUG": "\033[0m", # Reset 2025-01-24T00:45:41.9422413Z } 2025-01-24T00:45:41.9422598Z 2025-01-24T00:45:41.9422802Z def format(self, record: LogRecord) -> str: 2025-01-24T00:45:41.9423523Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-01-24T00:45:41.9424264Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-01-24T00:45:41.9424819Z return super().format(record) 2025-01-24T00:45:41.9425140Z 2025-01-24T00:45:41.9425146Z 2025-01-24T00:45:41.9425456Z handler = logging.StreamHandler() 2025-01-24T00:45:41.9426130Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-01-24T00:45:41.9426655Z 2025-01-24T00:45:41.9426890Z log = logging.getLogger(os.path.basename(__file__)) 2025-01-24T00:45:41.9427455Z log.addHandler(handler) 2025-01-24T00:45:41.9427878Z log.setLevel(logging.INFO) 2025-01-24T00:45:41.9428152Z 2025-01-24T00:45:41.9428158Z 2025-01-24T00:45:41.9428398Z def set_github_output(key: str, value: str) -> None: 2025-01-24T00:45:41.9428932Z """ 2025-01-24T00:45:41.9429412Z Defines outputs of the github action that invokes this script 2025-01-24T00:45:41.9430000Z """ 2025-01-24T00:45:41.9430341Z if not GITHUB_OUTPUT: 2025-01-24T00:45:41.9431370Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-01-24T00:45:41.9432434Z log.warning( 2025-01-24T00:45:41.9433240Z "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:41.9434115Z ) 2025-01-24T00:45:41.9445049Z print(f"::set-output name={key}::{value}") 2025-01-24T00:45:41.9445858Z return 2025-01-24T00:45:41.9446284Z 2025-01-24T00:45:41.9446475Z with open(GITHUB_OUTPUT, "a") as f: 2025-01-24T00:45:41.9447016Z log.info(f"Setting output: {key}='{value}'") 2025-01-24T00:45:41.9447547Z f.write(f"{key}={value}\n") 2025-01-24T00:45:41.9447862Z 2025-01-24T00:45:41.9447868Z 2025-01-24T00:45:41.9448165Z def _str_comma_separated_to_set(value: str) -> FrozenSet[str]: 2025-01-24T00:45:41.9448767Z return frozenset( 2025-01-24T00:45:41.9449350Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-01-24T00:45:41.9450020Z ) 2025-01-24T00:45:41.9450199Z 2025-01-24T00:45:41.9450206Z 2025-01-24T00:45:41.9450375Z def parse_args() -> Any: 2025-01-24T00:45:41.9450909Z parser = ArgumentParser("Get dynamic rollout settings") 2025-01-24T00:45:41.9451739Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-01-24T00:45:41.9452471Z parser.add_argument( 2025-01-24T00:45:41.9453001Z "--github-issue-repo", 2025-01-24T00:45:41.9453602Z type=str, 2025-01-24T00:45:41.9453975Z required=False, 2025-01-24T00:45:41.9454479Z default="pytorch/test-infra", 2025-01-24T00:45:41.9455052Z help="GitHub repo to get the issue", 2025-01-24T00:45:41.9455652Z ) 2025-01-24T00:45:41.9456005Z parser.add_argument( 2025-01-24T00:45:41.9456430Z "--github-repo", 2025-01-24T00:45:41.9456839Z type=str, 2025-01-24T00:45:41.9457212Z required=True, 2025-01-24T00:45:41.9457647Z help="GitHub repo where CI is running", 2025-01-24T00:45:41.9458144Z ) 2025-01-24T00:45:41.9458485Z parser.add_argument( 2025-01-24T00:45:41.9459069Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-01-24T00:45:41.9459688Z ) 2025-01-24T00:45:41.9460036Z parser.add_argument( 2025-01-24T00:45:41.9460773Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-01-24T00:45:41.9461427Z ) 2025-01-24T00:45:41.9461770Z parser.add_argument( 2025-01-24T00:45:41.9462377Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-01-24T00:45:41.9463030Z ) 2025-01-24T00:45:41.9463454Z parser.add_argument( 2025-01-24T00:45:41.9464277Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-01-24T00:45:41.9464965Z ) 2025-01-24T00:45:41.9465311Z parser.add_argument( 2025-01-24T00:45:41.9465844Z "--github-ref-type", 2025-01-24T00:45:41.9466367Z type=str, 2025-01-24T00:45:41.9466870Z required=True, 2025-01-24T00:45:41.9467471Z help="Current GitHub ref type, branch or tag", 2025-01-24T00:45:41.9467995Z ) 2025-01-24T00:45:41.9468344Z parser.add_argument( 2025-01-24T00:45:41.9468772Z "--eligible-experiments", 2025-01-24T00:45:41.9469347Z type=_str_comma_separated_to_set, 2025-01-24T00:45:41.9469840Z required=False, 2025-01-24T00:45:41.9470237Z default="", 2025-01-24T00:45:41.9471041Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-01-24T00:45:41.9471952Z ) 2025-01-24T00:45:41.9472294Z parser.add_argument( 2025-01-24T00:45:41.9472705Z "--pr-number", 2025-01-24T00:45:41.9473091Z type=str, 2025-01-24T00:45:41.9473458Z required=False, 2025-01-24T00:45:41.9473857Z default="", 2025-01-24T00:45:41.9474300Z help="the optional PR number where this is run", 2025-01-24T00:45:41.9474822Z ) 2025-01-24T00:45:41.9475009Z 2025-01-24T00:45:41.9475184Z return parser.parse_args() 2025-01-24T00:45:41.9475578Z 2025-01-24T00:45:41.9475584Z 2025-01-24T00:45:41.9475806Z def get_gh_client(github_token: str) -> Github: 2025-01-24T00:45:41.9476345Z auth = Auth.Token(github_token) 2025-01-24T00:45:41.9476823Z return Github(auth=auth) 2025-01-24T00:45:41.9477108Z 2025-01-24T00:45:41.9477115Z 2025-01-24T00:45:41.9477405Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: 2025-01-24T00:45:41.9478160Z repo = gh.get_repo(repo) 2025-01-24T00:45:41.9478634Z return repo.get_issue(number=issue_num) 2025-01-24T00:45:41.9478975Z 2025-01-24T00:45:41.9478981Z 2025-01-24T00:45:41.9479163Z def get_potential_pr_author( 2025-01-24T00:45:41.9479765Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-01-24T00:45:41.9480405Z ) -> str: 2025-01-24T00:45:41.9480888Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-01-24T00:45:41.9481654Z # Fetch the actual username from the original PR. The PR number is 2025-01-24T00:45:41.9482342Z # embedded in the tag name: ciflow// 2025-01-24T00:45:41.9482749Z 2025-01-24T00:45:41.9482923Z gh = get_gh_client(github_token) 2025-01-24T00:45:41.9483240Z 2025-01-24T00:45:41.9483506Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-01-24T00:45:41.9484088Z split_tag = ref_name.split("/") 2025-01-24T00:45:41.9484577Z if ( 2025-01-24T00:45:41.9484944Z len(split_tag) == 3 2025-01-24T00:45:41.9485493Z and split_tag[0] == "ciflow" 2025-01-24T00:45:41.9485991Z and split_tag[2].isnumeric() 2025-01-24T00:45:41.9486466Z ): 2025-01-24T00:45:41.9486821Z pr_number = split_tag[2] 2025-01-24T00:45:41.9487276Z try: 2025-01-24T00:45:41.9487684Z repository = gh.get_repo(repo) 2025-01-24T00:45:41.9488265Z pull = repository.get_pull(number=int(pr_number)) 2025-01-24T00:45:41.9488828Z except Exception as e: 2025-01-24T00:45:41.9489318Z raise Exception( # noqa: TRY002 2025-01-24T00:45:41.9489942Z f"issue with pull request {pr_number} from repo {repository}" 2025-01-24T00:45:41.9490554Z ) from e 2025-01-24T00:45:41.9491085Z return pull.user.login 2025-01-24T00:45:41.9491626Z # In all other cases, return the original input username 2025-01-24T00:45:41.9492188Z return username 2025-01-24T00:45:41.9492419Z 2025-01-24T00:45:41.9492425Z 2025-01-24T00:45:41.9492643Z def is_exception_branch(branch: str) -> bool: 2025-01-24T00:45:41.9493140Z """ 2025-01-24T00:45:41.9493737Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-01-24T00:45:41.9494482Z """ 2025-01-24T00:45:41.9494983Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-01-24T00:45:41.9495605Z 2025-01-24T00:45:41.9495611Z 2025-01-24T00:45:41.9495796Z def load_yaml(yaml_text: str) -> Any: 2025-01-24T00:45:41.9496263Z try: 2025-01-24T00:45:41.9496622Z data = yaml.safe_load(yaml_text) 2025-01-24T00:45:41.9497101Z return data 2025-01-24T00:45:41.9497491Z except yaml.YAMLError: 2025-01-24T00:45:41.9497941Z log.exception("Error loading YAML") 2025-01-24T00:45:41.9498436Z raise 2025-01-24T00:45:41.9498640Z 2025-01-24T00:45:41.9498646Z 2025-01-24T00:45:41.9499052Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> Tuple[str, str]: 2025-01-24T00:45:41.9499761Z """ 2025-01-24T00:45:41.9500350Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-01-24T00:45:41.9500921Z 2025-01-24T00:45:41.9501254Z If the issue body contains "---" then the text above that is the settings 2025-01-24T00:45:41.9501966Z and the text below is the list of opted in users. 2025-01-24T00:45:41.9502356Z 2025-01-24T00:45:41.9502709Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-01-24T00:45:41.9503384Z """ 2025-01-24T00:45:41.9503789Z rollout_state_parts = rollout_state.split("---") 2025-01-24T00:45:41.9504357Z if len(rollout_state_parts) >= 2: 2025-01-24T00:45:41.9504926Z return rollout_state_parts[0], rollout_state_parts[1] 2025-01-24T00:45:41.9505583Z else: 2025-01-24T00:45:41.9505941Z return "", rollout_state 2025-01-24T00:45:41.9506226Z 2025-01-24T00:45:41.9506238Z 2025-01-24T00:45:41.9506548Z class UserOptins(Dict[str, List[str]]): 2025-01-24T00:45:41.9507034Z """ 2025-01-24T00:45:41.9507521Z Dictionary of users with a list of features they have opted into 2025-01-24T00:45:41.9508134Z """ 2025-01-24T00:45:41.9508316Z 2025-01-24T00:45:41.9508323Z 2025-01-24T00:45:41.9508644Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-01-24T00:45:41.9509261Z """ 2025-01-24T00:45:41.9509925Z 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:41.9510576Z 2025-01-24T00:45:41.9511176Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-01-24T00:45:41.9512123Z - Example line: "@User1,lf,split_build" 2025-01-24T00:45:41.9512780Z - A "#" prefix indicates the user is opted out of all experiments 2025-01-24T00:45:41.9513241Z 2025-01-24T00:45:41.9513246Z 2025-01-24T00:45:41.9513404Z """ 2025-01-24T00:45:41.9513749Z optins = UserOptins() 2025-01-24T00:45:41.9514215Z for user in user_optin_text.split("\n"): 2025-01-24T00:45:41.9514730Z user = user.strip("\r\n\t -") 2025-01-24T00:45:41.9515235Z if not user or not user.startswith("@"): 2025-01-24T00:45:41.9515856Z # Not a valid user. Skip 2025-01-24T00:45:41.9516319Z continue 2025-01-24T00:45:41.9516562Z 2025-01-24T00:45:41.9516712Z if user: 2025-01-24T00:45:41.9517128Z usr_name = user.split(",")[0].strip("@") 2025-01-24T00:45:41.9517789Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-01-24T00:45:41.9518260Z 2025-01-24T00:45:41.9518417Z return optins 2025-01-24T00:45:41.9518645Z 2025-01-24T00:45:41.9518651Z 2025-01-24T00:45:41.9518921Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-01-24T00:45:41.9519610Z """ 2025-01-24T00:45:41.9519982Z Check if the experiment name is valid. 2025-01-24T00:45:41.9520468Z A valid name: 2025-01-24T00:45:41.9521073Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-01-24T00:45:41.9521961Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-01-24T00:45:41.9522632Z - Cannot contain spaces 2025-01-24T00:45:41.9523067Z """ 2025-01-24T00:45:41.9523248Z 2025-01-24T00:45:41.9523495Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-01-24T00:45:41.9524142Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-01-24T00:45:41.9524560Z 2025-01-24T00:45:41.9524713Z if valid: 2025-01-24T00:45:41.9525068Z return True 2025-01-24T00:45:41.9525286Z 2025-01-24T00:45:41.9525533Z log.error( 2025-01-24T00:45:41.9526922Z 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:41.9528458Z ) 2025-01-24T00:45:41.9528796Z return False 2025-01-24T00:45:41.9529007Z 2025-01-24T00:45:41.9529013Z 2025-01-24T00:45:41.9529298Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-01-24T00:45:41.9529885Z """ 2025-01-24T00:45:41.9530434Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-01-24T00:45:41.9531106Z """ 2025-01-24T00:45:41.9531434Z try: 2025-01-24T00:45:41.9531782Z if settings_text: 2025-01-24T00:45:41.9532462Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-01-24T00:45:41.9533218Z # for easy reading 2025-01-24T00:45:41.9533964Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-01-24T00:45:41.9534807Z # the backtick character in shell commands. 2025-01-24T00:45:41.9535464Z backtick = chr(96) # backtick character 2025-01-24T00:45:41.9536269Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-01-24T00:45:41.9536896Z settings = load_yaml(settings_text) 2025-01-24T00:45:41.9537247Z 2025-01-24T00:45:41.9537631Z # For now we just load experiments. We can expand this if/when we add more settings 2025-01-24T00:45:41.9538347Z experiments = {} 2025-01-24T00:45:41.9538624Z 2025-01-24T00:45:41.9538958Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-01-24T00:45:41.9539675Z if not is_valid_experiment_name(exp_name): 2025-01-24T00:45:41.9540727Z # 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:41.9541725Z continue 2025-01-24T00:45:41.9542004Z 2025-01-24T00:45:41.9542175Z valid_settings = {} 2025-01-24T00:45:41.9542667Z for setting in exp_settings: 2025-01-24T00:45:41.9543211Z if setting not in Experiment._fields: 2025-01-24T00:45:41.9543726Z log.warning( 2025-01-24T00:45:41.9544390Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-01-24T00:45:41.9545272Z ) 2025-01-24T00:45:41.9545776Z else: 2025-01-24T00:45:41.9546262Z valid_settings[setting] = exp_settings[setting] 2025-01-24T00:45:41.9546663Z 2025-01-24T00:45:41.9546935Z experiments[exp_name] = Experiment(**valid_settings) 2025-01-24T00:45:41.9547523Z return Settings(experiments) 2025-01-24T00:45:41.9547856Z 2025-01-24T00:45:41.9548026Z except Exception: 2025-01-24T00:45:41.9548471Z log.exception("Failed to parse settings") 2025-01-24T00:45:41.9548833Z 2025-01-24T00:45:41.9549131Z return Settings() 2025-01-24T00:45:41.9549385Z 2025-01-24T00:45:41.9549391Z 2025-01-24T00:45:41.9549623Z def parse_settings(rollout_state: str) -> Settings: 2025-01-24T00:45:41.9550174Z """ 2025-01-24T00:45:41.9550569Z Parse settings, if any, from the rollout state. 2025-01-24T00:45:41.9550952Z 2025-01-24T00:45:41.9551281Z If the issue body contains "---" then the text above that is the settings 2025-01-24T00:45:41.9551988Z and the text below is the list of opted in users. 2025-01-24T00:45:41.9552361Z 2025-01-24T00:45:41.9552750Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-01-24T00:45:41.9553432Z """ 2025-01-24T00:45:41.9553970Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:45:41.9554675Z return parse_settings_from_text(settings_text) 2025-01-24T00:45:41.9555045Z 2025-01-24T00:45:41.9555051Z 2025-01-24T00:45:41.9555287Z def parse_users(rollout_state: str) -> UserOptins: 2025-01-24T00:45:41.9555919Z """ 2025-01-24T00:45:41.9556285Z Parse users from the rollout state. 2025-01-24T00:45:41.9556618Z 2025-01-24T00:45:41.9556767Z """ 2025-01-24T00:45:41.9557256Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:45:41.9557946Z return parse_user_opt_in_from_text(users_text) 2025-01-24T00:45:41.9558320Z 2025-01-24T00:45:41.9558326Z 2025-01-24T00:45:41.9558719Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:45:41.9559422Z """ 2025-01-24T00:45:41.9559811Z Check if a user is opted into an experiment 2025-01-24T00:45:41.9560307Z """ 2025-01-24T00:45:41.9560728Z return experiment_name in user_optins.get(user, []) 2025-01-24T00:45:41.9561122Z 2025-01-24T00:45:41.9561129Z 2025-01-24T00:45:41.9561524Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:45:41.9562226Z """ 2025-01-24T00:45:41.9562662Z Check if a user explicitly opted out of an experiment 2025-01-24T00:45:41.9563215Z """ 2025-01-24T00:45:41.9563680Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-01-24T00:45:41.9564478Z experiment_optout = "-" + experiment_name 2025-01-24T00:45:41.9565075Z if experiment_optout not in user_optins.get(user, []): 2025-01-24T00:45:41.9565727Z return False 2025-01-24T00:45:41.9565962Z 2025-01-24T00:45:41.9566216Z if is_user_opted_in(user, user_optins, experiment_name): 2025-01-24T00:45:41.9566787Z log.warning( 2025-01-24T00:45:41.9567543Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-01-24T00:45:41.9568373Z ) 2025-01-24T00:45:41.9568557Z 2025-01-24T00:45:41.9568717Z return True 2025-01-24T00:45:41.9568930Z 2025-01-24T00:45:41.9568936Z 2025-01-24T00:45:41.9569134Z def get_runner_prefix( 2025-01-24T00:45:41.9569541Z rollout_state: str, 2025-01-24T00:45:41.9569977Z workflow_requestors: Iterable[str], 2025-01-24T00:45:41.9570455Z branch: str, 2025-01-24T00:45:41.9570911Z eligible_experiments: FrozenSet[str] = frozenset(), 2025-01-24T00:45:41.9571477Z is_canary: bool = False, 2025-01-24T00:45:41.9571902Z ) -> str: 2025-01-24T00:45:41.9572295Z settings = parse_settings(rollout_state) 2025-01-24T00:45:41.9572838Z user_optins = parse_users(rollout_state) 2025-01-24T00:45:41.9573185Z 2025-01-24T00:45:41.9573346Z fleet_prefix = "" 2025-01-24T00:45:41.9573739Z prefixes = [] 2025-01-24T00:45:41.9574326Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-01-24T00:45:41.9575199Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-01-24T00:45:41.9575966Z log.info( 2025-01-24T00:45:41.9576599Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-01-24T00:45:41.9577320Z ) 2025-01-24T00:45:41.9577799Z continue 2025-01-24T00:45:41.9578036Z 2025-01-24T00:45:41.9578216Z if eligible_experiments: 2025-01-24T00:45:41.9578740Z if experiment_name not in eligible_experiments: 2025-01-24T00:45:41.9579342Z exp_list = ", ".join(eligible_experiments) 2025-01-24T00:45:41.9579864Z log.info( 2025-01-24T00:45:41.9580598Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-01-24T00:45:41.9581405Z ) 2025-01-24T00:45:41.9581778Z continue 2025-01-24T00:45:41.9582219Z elif not experiment_settings.default: 2025-01-24T00:45:41.9582713Z log.info( 2025-01-24T00:45:41.9583349Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-01-24T00:45:41.9584042Z ) 2025-01-24T00:45:41.9584392Z continue 2025-01-24T00:45:41.9584625Z 2025-01-24T00:45:41.9584887Z # Is any workflow_requestor opted out to this experiment? 2025-01-24T00:45:41.9585561Z opted_out_users = [ 2025-01-24T00:45:41.9585981Z requestor 2025-01-24T00:45:41.9586411Z for requestor in workflow_requestors 2025-01-24T00:45:41.9587039Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-01-24T00:45:41.9587632Z ] 2025-01-24T00:45:41.9587824Z 2025-01-24T00:45:41.9587987Z if opted_out_users: 2025-01-24T00:45:41.9588400Z log.info( 2025-01-24T00:45:41.9588971Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-01-24T00:45:41.9589631Z ) 2025-01-24T00:45:41.9589980Z continue 2025-01-24T00:45:41.9590209Z 2025-01-24T00:45:41.9590459Z # Is any workflow_requestor opted in to this experiment? 2025-01-24T00:45:41.9591023Z opted_in_users = [ 2025-01-24T00:45:41.9591442Z requestor 2025-01-24T00:45:41.9591867Z for requestor in workflow_requestors 2025-01-24T00:45:41.9592491Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-01-24T00:45:41.9593076Z ] 2025-01-24T00:45:41.9593393Z 2025-01-24T00:45:41.9593551Z enabled = False 2025-01-24T00:45:41.9593954Z if opted_in_users: 2025-01-24T00:45:41.9594379Z log.info( 2025-01-24T00:45:41.9594933Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-01-24T00:45:41.9595674Z ) 2025-01-24T00:45:41.9596041Z enabled = True 2025-01-24T00:45:41.9596298Z 2025-01-24T00:45:41.9596505Z elif experiment_settings.rollout_perc: 2025-01-24T00:45:41.9597292Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-01-24T00:45:41.9598173Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-01-24T00:45:41.9598786Z log.info( 2025-01-24T00:45:41.9599605Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-01-24T00:45:41.9600481Z ) 2025-01-24T00:45:41.9600859Z enabled = True 2025-01-24T00:45:41.9601141Z 2025-01-24T00:45:41.9601300Z if enabled: 2025-01-24T00:45:41.9601689Z label = experiment_name 2025-01-24T00:45:41.9602205Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-01-24T00:45:41.9602981Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-01-24T00:45:41.9603807Z # - If it's enabled, then we always list it's prefix first 2025-01-24T00:45:41.9604511Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-01-24T00:45:41.9605139Z if is_canary: 2025-01-24T00:45:41.9605693Z label += CANARY_FLEET_SUFFIX 2025-01-24T00:45:41.9606193Z fleet_prefix = label 2025-01-24T00:45:41.9606852Z else: 2025-01-24T00:45:41.9607267Z prefixes.append(label) 2025-01-24T00:45:41.9607601Z 2025-01-24T00:45:41.9607773Z if len(prefixes) > 1: 2025-01-24T00:45:41.9608187Z log.error( 2025-01-24T00:45:41.9609168Z 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:41.9610227Z ) 2025-01-24T00:45:41.9610584Z prefixes = prefixes[:1] 2025-01-24T00:45:41.9610869Z 2025-01-24T00:45:41.9611045Z # Fleet always comes first 2025-01-24T00:45:41.9611507Z if fleet_prefix: 2025-01-24T00:45:41.9612053Z prefixes.insert(0, fleet_prefix) 2025-01-24T00:45:41.9612450Z 2025-01-24T00:45:41.9612720Z return ".".join(prefixes) + "." if prefixes else "" 2025-01-24T00:45:41.9613111Z 2025-01-24T00:45:41.9613118Z 2025-01-24T00:45:41.9613538Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-01-24T00:45:41.9614263Z """ 2025-01-24T00:45:41.9614815Z Gets the first comment of the issue, which contains the desired rollout state. 2025-01-24T00:45:41.9615470Z 2025-01-24T00:45:41.9615846Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-01-24T00:45:41.9616516Z """ 2025-01-24T00:45:41.9616874Z gh = get_gh_client(github_token) 2025-01-24T00:45:41.9617391Z issue = get_issue(gh, repo, issue_num) 2025-01-24T00:45:41.9617984Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-01-24T00:45:41.9618395Z 2025-01-24T00:45:41.9618401Z 2025-01-24T00:45:41.9618775Z def download_json(url: str, headers: Dict[str, str], num_retries: int = 3) -> Any: 2025-01-24T00:45:41.9619489Z for _ in range(num_retries): 2025-01-24T00:45:41.9619931Z try: 2025-01-24T00:45:41.9620333Z req = Request(url=url, headers=headers) 2025-01-24T00:45:41.9620958Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-01-24T00:45:41.9621559Z return json.loads(content) 2025-01-24T00:45:41.9622051Z except Exception as e: 2025-01-24T00:45:41.9622684Z log.warning(f"Could not download {url}: {e}") 2025-01-24T00:45:41.9623213Z 2025-01-24T00:45:41.9623573Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-01-24T00:45:41.9624351Z return {} 2025-01-24T00:45:41.9624557Z 2025-01-24T00:45:41.9624564Z 2025-01-24T00:45:41.9624739Z @lru_cache(maxsize=None) 2025-01-24T00:45:41.9625494Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> Dict[str, Any]: 2025-01-24T00:45:41.9626219Z """ 2025-01-24T00:45:41.9626582Z Dynamically get PR information 2025-01-24T00:45:41.9627119Z """ 2025-01-24T00:45:41.9627625Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-01-24T00:45:41.9628211Z headers = { 2025-01-24T00:45:41.9628621Z "Accept": "application/vnd.github.v3+json", 2025-01-24T00:45:41.9629198Z "Authorization": f"token {github_token}", 2025-01-24T00:45:41.9629694Z } 2025-01-24T00:45:41.9630086Z json_response: Dict[str, Any] = download_json( 2025-01-24T00:45:41.9630662Z url=f"{github_api}/issues/{pr_number}", 2025-01-24T00:45:41.9631181Z headers=headers, 2025-01-24T00:45:41.9631572Z ) 2025-01-24T00:45:41.9631758Z 2025-01-24T00:45:41.9631928Z if not json_response: 2025-01-24T00:45:41.9632453Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-01-24T00:45:41.9633025Z return {} 2025-01-24T00:45:41.9633247Z 2025-01-24T00:45:41.9633415Z return json_response 2025-01-24T00:45:41.9633667Z 2025-01-24T00:45:41.9633673Z 2025-01-24T00:45:41.9634053Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> Set[str]: 2025-01-24T00:45:41.9634731Z """ 2025-01-24T00:45:41.9635217Z Dynamically get the latest list of labels from the pull request 2025-01-24T00:45:41.9635930Z """ 2025-01-24T00:45:41.9636503Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-01-24T00:45:41.9637093Z return { 2025-01-24T00:45:41.9637632Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-01-24T00:45:41.9638280Z } 2025-01-24T00:45:41.9638466Z 2025-01-24T00:45:41.9638472Z 2025-01-24T00:45:41.9638634Z def main() -> None: 2025-01-24T00:45:41.9639018Z args = parse_args() 2025-01-24T00:45:41.9639266Z 2025-01-24T00:45:41.9639470Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-01-24T00:45:41.9639832Z 2025-01-24T00:45:41.9640006Z # Check if the PR is opt-out 2025-01-24T00:45:41.9640459Z if args.pr_number: 2025-01-24T00:45:41.9641057Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-01-24T00:45:41.9641746Z if OPT_OUT_LABEL in labels: 2025-01-24T00:45:41.9642209Z log.info( 2025-01-24T00:45:41.9642844Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-01-24T00:45:41.9643566Z ) 2025-01-24T00:45:41.9644078Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:45:41.9644722Z sys.exit() 2025-01-24T00:45:41.9644965Z 2025-01-24T00:45:41.9645111Z try: 2025-01-24T00:45:41.9645615Z rollout_state = get_rollout_state_from_issue( 2025-01-24T00:45:41.9646274Z args.github_token, args.github_issue_repo, args.github_issue 2025-01-24T00:45:41.9647217Z ) 2025-01-24T00:45:41.9647531Z 2025-01-24T00:45:41.9647770Z username = get_potential_pr_author( 2025-01-24T00:45:41.9648283Z args.github_token, 2025-01-24T00:45:41.9648721Z args.github_repo, 2025-01-24T00:45:41.9649165Z args.github_actor, 2025-01-24T00:45:41.9649610Z args.github_ref_type, 2025-01-24T00:45:41.9650064Z args.github_branch, 2025-01-24T00:45:41.9650488Z ) 2025-01-24T00:45:41.9650673Z 2025-01-24T00:45:41.9650952Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-01-24T00:45:41.9651377Z 2025-01-24T00:45:41.9651576Z runner_label_prefix = get_runner_prefix( 2025-01-24T00:45:41.9652088Z rollout_state, 2025-01-24T00:45:41.9652704Z (args.github_issue_owner, username), 2025-01-24T00:45:41.9653215Z args.github_branch, 2025-01-24T00:45:41.9653678Z args.eligible_experiments, 2025-01-24T00:45:41.9654151Z is_canary, 2025-01-24T00:45:41.9654531Z ) 2025-01-24T00:45:41.9654721Z 2025-01-24T00:45:41.9654891Z except Exception as e: 2025-01-24T00:45:41.9655317Z log.error( 2025-01-24T00:45:41.9656061Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-01-24T00:45:41.9656778Z ) 2025-01-24T00:45:41.9656965Z 2025-01-24T00:45:41.9657282Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:45:41.9657750Z 2025-01-24T00:45:41.9657756Z 2025-01-24T00:45:41.9657917Z if __name__ == "__main__": 2025-01-24T00:45:41.9658337Z main() 2025-01-24T00:45:41.9658530Z 2025-01-24T00:45:41.9746046Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-01-24T00:45:41.9746920Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-01-24T00:45:41.9774926Z shell: /usr/bin/bash -e {0} 2025-01-24T00:45:41.9775500Z env: 2025-01-24T00:45:41.9776086Z GITHUB_TOKEN: *** 2025-01-24T00:45:41.9776489Z ISSUE_NUMBER: 5132 2025-01-24T00:45:41.9776912Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:45:41.9777387Z ISSUE_OWNER: 2025-01-24T00:45:41.9777768Z CHECK_EXPERIMENTS: 2025-01-24T00:45:41.9778163Z PR_NUMBER: 2025-01-24T00:45:41.9778512Z ##[endgroup] 2025-01-24T00:45:42.3459698Z Defaulting to user installation because normal site-packages is not writeable 2025-01-24T00:45:42.6677890Z Collecting urllib3==1.26.18 2025-01-24T00:45:42.7080939Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-01-24T00:45:42.7304258Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 4.9 MB/s eta 0:00:00 2025-01-24T00:45:42.7531999Z Collecting PyGithub==2.3.0 2025-01-24T00:45:42.7574053Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-01-24T00:45:42.8026573Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-01-24T00:45:42.8069720Z 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:42.8113829Z 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:42.8130557Z 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:42.8144998Z 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:42.8393784Z Collecting Deprecated (from PyGithub==2.3.0) 2025-01-24T00:45:42.8439594Z Downloading Deprecated-1.2.15-py2.py3-none-any.whl.metadata (5.5 kB) 2025-01-24T00:45:42.8674331Z 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:42.9789433Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-01-24T00:45:42.9829895Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-01-24T00:45:43.0946437Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-01-24T00:45:43.0991254Z 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:43.1189438Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-01-24T00:45:43.1233048Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-01-24T00:45:43.1471360Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-01-24T00:45:43.1571800Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 16.2 MB/s eta 0:00:00 2025-01-24T00:45:43.1612933Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-01-24T00:45:43.1750867Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 28.5 MB/s eta 0:00:00 2025-01-24T00:45:43.1918538Z 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:43.2231502Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 28.8 MB/s eta 0:00:00 2025-01-24T00:45:43.2272915Z Downloading Deprecated-1.2.15-py2.py3-none-any.whl (9.9 kB) 2025-01-24T00:45:43.2335623Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-01-24T00:45:43.2460276Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 42.9 MB/s eta 0:00:00 2025-01-24T00:45:43.2504984Z 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:43.2541234Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 37.4 MB/s eta 0:00:00 2025-01-24T00:45:43.2580445Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-01-24T00:45:43.2623634Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 41.6 MB/s eta 0:00:00 2025-01-24T00:45:43.5474947Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-01-24T00:45:44.0495330Z 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:44.1315965Z ##[group]Run curr_branch="ciflow/trunk/145539" 2025-01-24T00:45:44.1317368Z curr_branch="ciflow/trunk/145539" 2025-01-24T00:45:44.1318603Z curr_ref_type="tag" 2025-01-24T00:45:44.1319722Z echo "Current branch is '$curr_branch'" 2025-01-24T00:45:44.1320923Z  2025-01-24T00:45:44.1321802Z python3 runner_determinator.py \ 2025-01-24T00:45:44.1323041Z  --github-token "$GITHUB_TOKEN" \ 2025-01-24T00:45:44.1324291Z  --github-issue "$ISSUE_NUMBER" \ 2025-01-24T00:45:44.1325707Z  --github-branch "$curr_branch" \ 2025-01-24T00:45:44.1326985Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-01-24T00:45:44.1328281Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-01-24T00:45:44.1329553Z  --github-ref-type "$curr_ref_type" \ 2025-01-24T00:45:44.1330814Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-01-24T00:45:44.1332265Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-01-24T00:45:44.1333695Z  --pr-number "${PR_NUMBER}" 2025-01-24T00:45:44.1362750Z shell: /usr/bin/bash -e {0} 2025-01-24T00:45:44.1363712Z env: 2025-01-24T00:45:44.1364971Z GITHUB_TOKEN: *** 2025-01-24T00:45:44.1365969Z ISSUE_NUMBER: 5132 2025-01-24T00:45:44.1366846Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:45:44.1367881Z ISSUE_OWNER: 2025-01-24T00:45:44.1368664Z CHECK_EXPERIMENTS: 2025-01-24T00:45:44.1369508Z PR_NUMBER: 2025-01-24T00:45:44.1370261Z ##[endgroup] 2025-01-24T00:45:44.1426689Z Current branch is 'ciflow/trunk/145539' 2025-01-24T00:45:46.4866379Z INFO : Based on rollout percentage of 55%, enabling experiment lf. 2025-01-24T00:45:46.4867430Z INFO : Skipping experiment 'awsa100', as it is not a default experiment 2025-01-24T00:45:46.4868670Z INFO : Setting output: label-type='lf.' 2025-01-24T00:45:46.5173034Z Evaluate and set job outputs 2025-01-24T00:45:46.5179791Z Set output 'label-type' 2025-01-24T00:45:46.5181604Z Cleaning up orphan processes