2025-03-14T03:51:40.6366412Z Current runner version: '2.322.0' 2025-03-14T03:51:40.6398990Z ##[group]Operating System 2025-03-14T03:51:40.6399765Z Ubuntu 2025-03-14T03:51:40.6400299Z 24.04.2 2025-03-14T03:51:40.6400804Z LTS 2025-03-14T03:51:40.6401267Z ##[endgroup] 2025-03-14T03:51:40.6402006Z ##[group]Runner Image 2025-03-14T03:51:40.6402581Z Image: ubuntu-24.04 2025-03-14T03:51:40.6403063Z Version: 20250309.1.0 2025-03-14T03:51:40.6404145Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250309.1/images/ubuntu/Ubuntu2404-Readme.md 2025-03-14T03:51:40.6405531Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250309.1 2025-03-14T03:51:40.6406425Z ##[endgroup] 2025-03-14T03:51:40.6407015Z ##[group]Runner Image Provisioner 2025-03-14T03:51:40.6407612Z 2.0.422.1 2025-03-14T03:51:40.6408056Z ##[endgroup] 2025-03-14T03:51:40.6409457Z ##[group]GITHUB_TOKEN Permissions 2025-03-14T03:51:40.6411231Z Contents: read 2025-03-14T03:51:40.6412138Z Metadata: read 2025-03-14T03:51:40.6412872Z Packages: read 2025-03-14T03:51:40.6413586Z ##[endgroup] 2025-03-14T03:51:40.6416823Z Secret source: Actions 2025-03-14T03:51:40.6417792Z Prepare workflow directory 2025-03-14T03:51:40.6903376Z Prepare all required actions 2025-03-14T03:51:40.6958058Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (aed0b7a742a2d7b7901790622829cbd2135049a4) 2025-03-14T03:51:40.6963445Z ##[group] Inputs 2025-03-14T03:51:40.6964079Z check_experiments: 2025-03-14T03:51:40.6964675Z triggering_actor: pytorchmergebot 2025-03-14T03:51:40.6965354Z issue_owner: 2025-03-14T03:51:40.6965865Z curr_branch: main 2025-03-14T03:51:40.6966377Z curr_ref_type: branch 2025-03-14T03:51:40.6966975Z issue_number: 5132 2025-03-14T03:51:40.6967512Z ##[endgroup] 2025-03-14T03:51:40.6968116Z Complete job name: get-label-type / runner-determinator 2025-03-14T03:51:40.7679709Z ##[group]Run cat < runner_determinator.py 2025-03-14T03:51:40.7681726Z cat < runner_determinator.py 2025-03-14T03:51:40.7682378Z # flake8: noqa: G004 2025-03-14T03:51:40.7682850Z  2025-03-14T03:51:40.7683501Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-03-14T03:51:40.7684510Z # must be kept in sync. You can do it easily by running the following command: 2025-03-14T03:51:40.7685378Z # python .github/scripts/update_runner_determinator.py 2025-03-14T03:51:40.7686012Z  2025-03-14T03:51:40.7686378Z """ 2025-03-14T03:51:40.7686998Z This runner determinator is used to determine which set of runners to run a 2025-03-14T03:51:40.7687934Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-03-14T03:51:40.7688954Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-03-14T03:51:40.7689839Z of which runners should be used to run which job. 2025-03-14T03:51:40.7690426Z  2025-03-14T03:51:40.7691040Z The configuration has two parts, the settings and a list of opted-in users, 2025-03-14T03:51:40.7692147Z separated by a line containing "---". If the line is not present, the 2025-03-14T03:51:40.7693095Z settings are considered to be empty with only the second part, the user 2025-03-14T03:51:40.7693835Z list, defined. 2025-03-14T03:51:40.7694255Z  2025-03-14T03:51:40.7694829Z The first part is a YAML block that defines the rollout settings. This can be 2025-03-14T03:51:40.7695822Z used to define any settings that are needed to determine which runners to use. 2025-03-14T03:51:40.7696768Z It's fields are defined by the RolloutSettings class below. 2025-03-14T03:51:40.7697462Z  2025-03-14T03:51:40.7698086Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-03-14T03:51:40.7699015Z The user list is also a comma separated list of additional features or 2025-03-14T03:51:40.7700211Z experiments which the user could be opted in to. 2025-03-14T03:51:40.7700808Z  2025-03-14T03:51:40.7701218Z The user list has the following rules: 2025-03-14T03:51:40.7701985Z  2025-03-14T03:51:40.7702528Z - Users are GitHub usernames, which must start with the @ prefix 2025-03-14T03:51:40.7703412Z - Each user is also a comma-separated list of features/experiments to enable 2025-03-14T03:51:40.7704245Z - A "#" prefix opts the user out of all experiments 2025-03-14T03:51:40.7704827Z  2025-03-14T03:51:40.7705207Z Example config: 2025-03-14T03:51:40.7705756Z  # A list of experiments that can be opted into. 2025-03-14T03:51:40.7706502Z  # This defines the behavior they'll induce when opted into. 2025-03-14T03:51:40.7707183Z  # Expected syntax is: 2025-03-14T03:51:40.7707907Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-03-14T03:51:40.7708942Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-03-14T03:51:40.7709750Z  2025-03-14T03:51:40.7710130Z  experiments: 2025-03-14T03:51:40.7710579Z  lf: 2025-03-14T03:51:40.7710994Z  rollout_percent: 25 2025-03-14T03:51:40.7711769Z  all_branches: false 2025-03-14T03:51:40.7712293Z  default: true 2025-03-14T03:51:40.7712754Z  --- 2025-03-14T03:51:40.7713131Z  2025-03-14T03:51:40.7713524Z  # Opt-ins: 2025-03-14T03:51:40.7714177Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-03-14T03:51:40.7715300Z  # and specifying experiments to enable in a comma-separated list. 2025-03-14T03:51:40.7716157Z  # To always opt out of an experiment, prefix it with a "-". 2025-03-14T03:51:40.7716886Z  # Experiments should be from the above list. 2025-03-14T03:51:40.7717454Z  2025-03-14T03:51:40.7717833Z  @User1,-lf,split_build 2025-03-14T03:51:40.7718336Z  @User2,lf 2025-03-14T03:51:40.7718774Z  @User3,split_build 2025-03-14T03:51:40.7719237Z """ 2025-03-14T03:51:40.7719601Z  2025-03-14T03:51:40.7719959Z import json 2025-03-14T03:51:40.7720380Z import logging 2025-03-14T03:51:40.7720810Z import os 2025-03-14T03:51:40.7721220Z import random 2025-03-14T03:51:40.7721882Z import re 2025-03-14T03:51:40.7722296Z import sys 2025-03-14T03:51:40.7722820Z from argparse import ArgumentParser 2025-03-14T03:51:40.7723416Z from collections.abc import Iterable 2025-03-14T03:51:40.7723990Z from functools import cache 2025-03-14T03:51:40.7724519Z from logging import LogRecord 2025-03-14T03:51:40.7725088Z from typing import Any, NamedTuple 2025-03-14T03:51:40.7725705Z from urllib.request import Request, urlopen 2025-03-14T03:51:40.7726268Z  2025-03-14T03:51:40.7726634Z import yaml 2025-03-14T03:51:40.7727082Z from github import Auth, Github 2025-03-14T03:51:40.7727628Z from github.Issue import Issue 2025-03-14T03:51:40.7728126Z  2025-03-14T03:51:40.7728477Z  2025-03-14T03:51:40.7728917Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-03-14T03:51:40.7729668Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-03-14T03:51:40.7730587Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-03-14T03:51:40.7731341Z  2025-03-14T03:51:40.7732218Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-03-14T03:51:40.7733244Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-03-14T03:51:40.7733959Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-03-14T03:51:40.7734777Z OPT_OUT_LABEL = "no-runner-experiments" 2025-03-14T03:51:40.7735325Z  2025-03-14T03:51:40.7735745Z SETTING_EXPERIMENTS = "experiments" 2025-03-14T03:51:40.7736276Z  2025-03-14T03:51:40.7736652Z LF_FLEET_EXPERIMENT = "lf" 2025-03-14T03:51:40.7737169Z CANARY_FLEET_SUFFIX = ".c" 2025-03-14T03:51:40.7737690Z  2025-03-14T03:51:40.7738061Z  2025-03-14T03:51:40.7738452Z class Experiment(NamedTuple): 2025-03-14T03:51:40.7738980Z  rollout_perc: float = ( 2025-03-14T03:51:40.7739698Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-03-14T03:51:40.7740426Z  ) 2025-03-14T03:51:40.7740837Z  all_branches: bool = ( 2025-03-14T03:51:40.7741769Z  False # If True, the experiment is also enabled on the exception branches 2025-03-14T03:51:40.7742511Z  ) 2025-03-14T03:51:40.7742911Z  default: bool = ( 2025-03-14T03:51:40.7743555Z  True # If True, the experiment is enabled by default for all queries 2025-03-14T03:51:40.7744232Z  ) 2025-03-14T03:51:40.7744604Z  2025-03-14T03:51:40.7744994Z  # Add more fields as needed 2025-03-14T03:51:40.7745500Z  2025-03-14T03:51:40.7745849Z  2025-03-14T03:51:40.7746234Z class Settings(NamedTuple): 2025-03-14T03:51:40.7746727Z  """ 2025-03-14T03:51:40.7747240Z  Settings for the experiments that can be opted into. 2025-03-14T03:51:40.7747856Z  """ 2025-03-14T03:51:40.7748239Z  2025-03-14T03:51:40.7748660Z  experiments: dict[str, Experiment] = {} 2025-03-14T03:51:40.7749208Z  2025-03-14T03:51:40.7749939Z  2025-03-14T03:51:40.7750607Z class ColorFormatter(logging.Formatter): 2025-03-14T03:51:40.7751303Z  """Color codes the log messages based on the log level""" 2025-03-14T03:51:40.7752035Z  2025-03-14T03:51:40.7752398Z  COLORS = { 2025-03-14T03:51:40.7752866Z  "WARNING": "\033[33m", # Yellow 2025-03-14T03:51:40.7753438Z  "ERROR": "\033[31m", # Red 2025-03-14T03:51:40.7754019Z  "CRITICAL": "\033[31m", # Red 2025-03-14T03:51:40.7754568Z  "INFO": "\033[0m", # Reset 2025-03-14T03:51:40.7755105Z  "DEBUG": "\033[0m", # Reset 2025-03-14T03:51:40.7755639Z  } 2025-03-14T03:51:40.7756005Z  2025-03-14T03:51:40.7756459Z  def format(self, record: LogRecord) -> str: 2025-03-14T03:51:40.7757267Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-03-14T03:51:40.7758294Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-03-14T03:51:40.7759129Z  return super().format(record) 2025-03-14T03:51:40.7759675Z  2025-03-14T03:51:40.7760029Z  2025-03-14T03:51:40.7760430Z handler = logging.StreamHandler() 2025-03-14T03:51:40.7761220Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-03-14T03:51:40.7762192Z  2025-03-14T03:51:40.7762677Z log = logging.getLogger(os.path.basename(__file__)) 2025-03-14T03:51:40.7763306Z log.addHandler(handler) 2025-03-14T03:51:40.7763810Z log.setLevel(logging.INFO) 2025-03-14T03:51:40.7764297Z  2025-03-14T03:51:40.7764642Z  2025-03-14T03:51:40.7765114Z def set_github_output(key: str, value: str) -> None: 2025-03-14T03:51:40.7765723Z  """ 2025-03-14T03:51:40.7766276Z  Defines outputs of the github action that invokes this script 2025-03-14T03:51:40.7766946Z  """ 2025-03-14T03:51:40.7767352Z  if not GITHUB_OUTPUT: 2025-03-14T03:51:40.7768455Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-03-14T03:51:40.7769781Z  log.warning( 2025-03-14T03:51:40.7770742Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-03-14T03:51:40.7771884Z  ) 2025-03-14T03:51:40.7772357Z  print(f"::set-output name={key}::{value}") 2025-03-14T03:51:40.7772926Z  return 2025-03-14T03:51:40.7773357Z  2025-03-14T03:51:40.7773759Z  with open(GITHUB_OUTPUT, "a") as f: 2025-03-14T03:51:40.7774391Z  log.info(f"Setting output: {key}='{value}'") 2025-03-14T03:51:40.7775000Z  f.write(f"{key}={value}\n") 2025-03-14T03:51:40.7775533Z  2025-03-14T03:51:40.7775893Z  2025-03-14T03:51:40.7776421Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-03-14T03:51:40.7777122Z  return frozenset( 2025-03-14T03:51:40.7777797Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-03-14T03:51:40.7778506Z  ) 2025-03-14T03:51:40.7778885Z  2025-03-14T03:51:40.7779236Z  2025-03-14T03:51:40.7779609Z def parse_args() -> Any: 2025-03-14T03:51:40.7780241Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-03-14T03:51:40.7781158Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-03-14T03:51:40.7782168Z  parser.add_argument( 2025-03-14T03:51:40.7782687Z  "--github-issue-repo", 2025-03-14T03:51:40.7783213Z  type=str, 2025-03-14T03:51:40.7783667Z  required=False, 2025-03-14T03:51:40.7784321Z  default="pytorch/test-infra", 2025-03-14T03:51:40.7784922Z  help="GitHub repo to get the issue", 2025-03-14T03:51:40.7785529Z  ) 2025-03-14T03:51:40.7785990Z  parser.add_argument( 2025-03-14T03:51:40.7786556Z  "--github-repo", 2025-03-14T03:51:40.7787095Z  type=str, 2025-03-14T03:51:40.7787641Z  required=True, 2025-03-14T03:51:40.7788251Z  help="GitHub repo where CI is running", 2025-03-14T03:51:40.7788859Z  ) 2025-03-14T03:51:40.7789317Z  parser.add_argument( 2025-03-14T03:51:40.7790049Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-03-14T03:51:40.7790746Z  ) 2025-03-14T03:51:40.7791145Z  parser.add_argument( 2025-03-14T03:51:40.7791947Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-03-14T03:51:40.7792662Z  ) 2025-03-14T03:51:40.7793056Z  parser.add_argument( 2025-03-14T03:51:40.7793756Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-03-14T03:51:40.7794474Z  ) 2025-03-14T03:51:40.7794876Z  parser.add_argument( 2025-03-14T03:51:40.7795589Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-03-14T03:51:40.7796339Z  ) 2025-03-14T03:51:40.7796732Z  parser.add_argument( 2025-03-14T03:51:40.7797236Z  "--github-ref-type", 2025-03-14T03:51:40.7797866Z  type=str, 2025-03-14T03:51:40.7798398Z  required=True, 2025-03-14T03:51:40.7798951Z  help="Current GitHub ref type, branch or tag", 2025-03-14T03:51:40.7799531Z  ) 2025-03-14T03:51:40.7799928Z  parser.add_argument( 2025-03-14T03:51:40.7800776Z  "--eligible-experiments", 2025-03-14T03:51:40.7801870Z  type=_str_comma_separated_to_set, 2025-03-14T03:51:40.7802790Z  required=False, 2025-03-14T03:51:40.7803485Z  default="", 2025-03-14T03:51:40.7804394Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-03-14T03:51:40.7805398Z  ) 2025-03-14T03:51:40.7805799Z  parser.add_argument( 2025-03-14T03:51:40.7806290Z  "--pr-number", 2025-03-14T03:51:40.7806840Z  type=str, 2025-03-14T03:51:40.7807307Z  required=False, 2025-03-14T03:51:40.7807793Z  default="", 2025-03-14T03:51:40.7808344Z  help="the optional PR number where this is run", 2025-03-14T03:51:40.7808936Z  ) 2025-03-14T03:51:40.7809301Z  2025-03-14T03:51:40.7809692Z  return parser.parse_args() 2025-03-14T03:51:40.7810200Z  2025-03-14T03:51:40.7810556Z  2025-03-14T03:51:40.7811186Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-03-14T03:51:40.7812229Z  auth = Auth.Token(github_token) 2025-03-14T03:51:40.7812782Z  return Github(auth=auth) 2025-03-14T03:51:40.7813277Z  2025-03-14T03:51:40.7813654Z  2025-03-14T03:51:40.7814567Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-03-14T03:51:40.7815479Z  repo = gh.get_repo(repo) 2025-03-14T03:51:40.7816044Z  return repo.get_issue(number=issue_num) 2025-03-14T03:51:40.7816592Z  2025-03-14T03:51:40.7816945Z  2025-03-14T03:51:40.7817330Z def get_potential_pr_author( 2025-03-14T03:51:40.7818042Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-03-14T03:51:40.7818756Z ) -> str: 2025-03-14T03:51:40.7819600Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-03-14T03:51:40.7820689Z  # Fetch the actual username from the original PR. The PR number is 2025-03-14T03:51:40.7821626Z  # embedded in the tag name: ciflow// 2025-03-14T03:51:40.7822234Z  2025-03-14T03:51:40.7822625Z  gh = get_gh_client(github_token) 2025-03-14T03:51:40.7823152Z  2025-03-14T03:51:40.7823632Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-03-14T03:51:40.7824359Z  split_tag = ref_name.split("/") 2025-03-14T03:51:40.7824895Z  if ( 2025-03-14T03:51:40.7825322Z  len(split_tag) == 3 2025-03-14T03:51:40.7825856Z  and split_tag[0] == "ciflow" 2025-03-14T03:51:40.7826426Z  and split_tag[2].isnumeric() 2025-03-14T03:51:40.7826951Z  ): 2025-03-14T03:51:40.7827394Z  pr_number = split_tag[2] 2025-03-14T03:51:40.7827916Z  try: 2025-03-14T03:51:40.7828402Z  repository = gh.get_repo(repo) 2025-03-14T03:51:40.7829058Z  pull = repository.get_pull(number=int(pr_number)) 2025-03-14T03:51:40.7829754Z  except Exception as e: 2025-03-14T03:51:40.7830323Z  raise Exception( # noqa: TRY002 2025-03-14T03:51:40.7831031Z  f"issue with pull request {pr_number} from repo {repository}" 2025-03-14T03:51:40.7831902Z  ) from e 2025-03-14T03:51:40.7832553Z  return pull.user.login # type: ignore[no-any-return] 2025-03-14T03:51:40.7833371Z  # In all other cases, return the original input username 2025-03-14T03:51:40.7834199Z  return username 2025-03-14T03:51:40.7834931Z  2025-03-14T03:51:40.7835523Z  2025-03-14T03:51:40.7836150Z def is_exception_branch(branch: str) -> bool: 2025-03-14T03:51:40.7836721Z  """ 2025-03-14T03:51:40.7837414Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-03-14T03:51:40.7838414Z  """ 2025-03-14T03:51:40.7838995Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-03-14T03:51:40.7839685Z  2025-03-14T03:51:40.7840025Z  2025-03-14T03:51:40.7840420Z def load_yaml(yaml_text: str) -> Any: 2025-03-14T03:51:40.7840945Z  try: 2025-03-14T03:51:40.7841520Z  data = yaml.safe_load(yaml_text) 2025-03-14T03:51:40.7842136Z  return data 2025-03-14T03:51:40.7842602Z  except yaml.YAMLError: 2025-03-14T03:51:40.7843145Z  log.exception("Error loading YAML") 2025-03-14T03:51:40.7843691Z  raise 2025-03-14T03:51:40.7844096Z  2025-03-14T03:51:40.7844436Z  2025-03-14T03:51:40.7845059Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-03-14T03:51:40.7845851Z  """ 2025-03-14T03:51:40.7846517Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-03-14T03:51:40.7847309Z  2025-03-14T03:51:40.7847860Z  If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:40.7848661Z  and the text below is the list of opted in users. 2025-03-14T03:51:40.7849242Z  2025-03-14T03:51:40.7849837Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-03-14T03:51:40.7850582Z  """ 2025-03-14T03:51:40.7851069Z  rollout_state_parts = rollout_state.split("---") 2025-03-14T03:51:40.7851874Z  if len(rollout_state_parts) >= 2: 2025-03-14T03:51:40.7852687Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-03-14T03:51:40.7853330Z  else: 2025-03-14T03:51:40.7853748Z  return "", rollout_state 2025-03-14T03:51:40.7854245Z  2025-03-14T03:51:40.7854582Z  2025-03-14T03:51:40.7854984Z class UserOptins(dict[str, list[str]]): 2025-03-14T03:51:40.7855525Z  """ 2025-03-14T03:51:40.7856081Z  Dictionary of users with a list of features they have opted into 2025-03-14T03:51:40.7856751Z  """ 2025-03-14T03:51:40.7857122Z  2025-03-14T03:51:40.7857457Z  2025-03-14T03:51:40.7857994Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-03-14T03:51:40.7858750Z  """ 2025-03-14T03:51:40.7859540Z  Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-03-14T03:51:40.7860454Z  2025-03-14T03:51:40.7861274Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-03-14T03:51:40.7862582Z  - Example line: "@User1,lf,split_build" 2025-03-14T03:51:40.7863308Z  - A "#" prefix indicates the user is opted out of all experiments 2025-03-14T03:51:40.7863977Z  2025-03-14T03:51:40.7864323Z  2025-03-14T03:51:40.7864667Z  """ 2025-03-14T03:51:40.7865067Z  optins = UserOptins() 2025-03-14T03:51:40.7865606Z  for user in user_optin_text.split("\n"): 2025-03-14T03:51:40.7866201Z  user = user.strip("\r\n\t -") 2025-03-14T03:51:40.7866793Z  if not user or not user.startswith("@"): 2025-03-14T03:51:40.7867443Z  # Not a valid user. Skip 2025-03-14T03:51:40.7868025Z  continue 2025-03-14T03:51:40.7868507Z  2025-03-14T03:51:40.7868925Z  if user: 2025-03-14T03:51:40.7869482Z  usr_name = user.split(",")[0].strip("@") 2025-03-14T03:51:40.7870271Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-03-14T03:51:40.7871136Z  2025-03-14T03:51:40.7871834Z  return optins 2025-03-14T03:51:40.7872276Z  2025-03-14T03:51:40.7872611Z  2025-03-14T03:51:40.7873125Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-03-14T03:51:40.7873776Z  """ 2025-03-14T03:51:40.7874215Z  Check if the experiment name is valid. 2025-03-14T03:51:40.7874768Z  A valid name: 2025-03-14T03:51:40.7875479Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-03-14T03:51:40.7876459Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-03-14T03:51:40.7877206Z  - Cannot contain spaces 2025-03-14T03:51:40.7877706Z  """ 2025-03-14T03:51:40.7878080Z  2025-03-14T03:51:40.7878548Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-03-14T03:51:40.7879296Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-03-14T03:51:40.7879920Z  2025-03-14T03:51:40.7880272Z  if valid: 2025-03-14T03:51:40.7880693Z  return True 2025-03-14T03:51:40.7881118Z  2025-03-14T03:51:40.7881705Z  log.error( 2025-03-14T03:51:40.7883182Z  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-03-14T03:51:40.7884796Z  ) 2025-03-14T03:51:40.7885176Z  return False 2025-03-14T03:51:40.7885591Z  2025-03-14T03:51:40.7885924Z  2025-03-14T03:51:40.7886585Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-03-14T03:51:40.7887255Z  """ 2025-03-14T03:51:40.7887884Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-03-14T03:51:40.7888634Z  """ 2025-03-14T03:51:40.7888996Z  try: 2025-03-14T03:51:40.7889383Z  if settings_text: 2025-03-14T03:51:40.7890686Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-03-14T03:51:40.7892059Z  # for easy reading 2025-03-14T03:51:40.7892918Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-03-14T03:51:40.7893870Z  # the backtick character in shell commands. 2025-03-14T03:51:40.7894561Z  backtick = chr(96) # backtick character 2025-03-14T03:51:40.7895270Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-03-14T03:51:40.7895986Z  settings = load_yaml(settings_text) 2025-03-14T03:51:40.7896582Z  2025-03-14T03:51:40.7897248Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-03-14T03:51:40.7898084Z  experiments = {} 2025-03-14T03:51:40.7898627Z  2025-03-14T03:51:40.7899260Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-03-14T03:51:40.7900129Z  if not is_valid_experiment_name(exp_name): 2025-03-14T03:51:40.7901319Z  # 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-03-14T03:51:40.7902628Z  continue 2025-03-14T03:51:40.7903098Z  2025-03-14T03:51:40.7903477Z  valid_settings = {} 2025-03-14T03:51:40.7904047Z  for setting in exp_settings: 2025-03-14T03:51:40.7904646Z  if setting not in Experiment._fields: 2025-03-14T03:51:40.7905476Z  log.warning( 2025-03-14T03:51:40.7906288Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-03-14T03:51:40.7907040Z  ) 2025-03-14T03:51:40.7907507Z  else: 2025-03-14T03:51:40.7908077Z  valid_settings[setting] = exp_settings[setting] 2025-03-14T03:51:40.7908675Z  2025-03-14T03:51:40.7909157Z  experiments[exp_name] = Experiment(**valid_settings) 2025-03-14T03:51:40.7909833Z  return Settings(experiments) 2025-03-14T03:51:40.7910358Z  2025-03-14T03:51:40.7910722Z  except Exception: 2025-03-14T03:51:40.7911263Z  log.exception("Failed to parse settings") 2025-03-14T03:51:40.7912024Z  2025-03-14T03:51:40.7912391Z  return Settings() 2025-03-14T03:51:40.7912839Z  2025-03-14T03:51:40.7913177Z  2025-03-14T03:51:40.7913674Z def parse_settings(rollout_state: str) -> Settings: 2025-03-14T03:51:40.7914265Z  """ 2025-03-14T03:51:40.7914745Z  Parse settings, if any, from the rollout state. 2025-03-14T03:51:40.7915310Z  2025-03-14T03:51:40.7915858Z  If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:40.7916678Z  and the text below is the list of opted in users. 2025-03-14T03:51:40.7917258Z  2025-03-14T03:51:40.7917872Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-03-14T03:51:40.7918632Z  """ 2025-03-14T03:51:40.7919228Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:40.7920163Z  return parse_settings_from_text(settings_text) 2025-03-14T03:51:40.7920742Z  2025-03-14T03:51:40.7921077Z  2025-03-14T03:51:40.7921652Z def parse_users(rollout_state: str) -> UserOptins: 2025-03-14T03:51:40.7922265Z  """ 2025-03-14T03:51:40.7922691Z  Parse users from the rollout state. 2025-03-14T03:51:40.7923216Z  2025-03-14T03:51:40.7923560Z  """ 2025-03-14T03:51:40.7924132Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:40.7924921Z  return parse_user_opt_in_from_text(users_text) 2025-03-14T03:51:40.7925486Z  2025-03-14T03:51:40.7925830Z  2025-03-14T03:51:40.7926454Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:40.7927228Z  """ 2025-03-14T03:51:40.7927689Z  Check if a user is opted into an experiment 2025-03-14T03:51:40.7928295Z  """ 2025-03-14T03:51:40.7928848Z  return experiment_name in user_optins.get(user, []) 2025-03-14T03:51:40.7929513Z  2025-03-14T03:51:40.7929908Z  2025-03-14T03:51:40.7930596Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:40.7931533Z  """ 2025-03-14T03:51:40.7932087Z  Check if a user explicitly opted out of an experiment 2025-03-14T03:51:40.7932754Z  """ 2025-03-14T03:51:40.7933301Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-03-14T03:51:40.7934026Z  experiment_optout = "-" + experiment_name 2025-03-14T03:51:40.7934711Z  if experiment_optout not in user_optins.get(user, []): 2025-03-14T03:51:40.7935327Z  return False 2025-03-14T03:51:40.7935767Z  2025-03-14T03:51:40.7936250Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-03-14T03:51:40.7936873Z  log.warning( 2025-03-14T03:51:40.7937704Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-03-14T03:51:40.7938720Z  ) 2025-03-14T03:51:40.7939091Z  2025-03-14T03:51:40.7939447Z  return True 2025-03-14T03:51:40.7939856Z  2025-03-14T03:51:40.7940188Z  2025-03-14T03:51:40.7940557Z def get_runner_prefix( 2025-03-14T03:51:40.7941030Z  rollout_state: str, 2025-03-14T03:51:40.7941638Z  workflow_requestors: Iterable[str], 2025-03-14T03:51:40.7942182Z  branch: str, 2025-03-14T03:51:40.7942727Z  eligible_experiments: frozenset[str] = frozenset(), 2025-03-14T03:51:40.7943352Z  is_canary: bool = False, 2025-03-14T03:51:40.7943831Z ) -> str: 2025-03-14T03:51:40.7944290Z  settings = parse_settings(rollout_state) 2025-03-14T03:51:40.7944901Z  user_optins = parse_users(rollout_state) 2025-03-14T03:51:40.7945429Z  2025-03-14T03:51:40.7945783Z  fleet_prefix = "" 2025-03-14T03:51:40.7946241Z  prefixes = [] 2025-03-14T03:51:40.7946911Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-03-14T03:51:40.7947888Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-03-14T03:51:40.7948617Z  log.info( 2025-03-14T03:51:40.7949330Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-03-14T03:51:40.7950097Z  ) 2025-03-14T03:51:40.7950924Z  continue 2025-03-14T03:51:40.7951715Z  2025-03-14T03:51:40.7952360Z  if eligible_experiments: 2025-03-14T03:51:40.7953153Z  if experiment_name not in eligible_experiments: 2025-03-14T03:51:40.7953845Z  exp_list = ", ".join(eligible_experiments) 2025-03-14T03:51:40.7954419Z  log.info( 2025-03-14T03:51:40.7955242Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-03-14T03:51:40.7956124Z  ) 2025-03-14T03:51:40.7956567Z  continue 2025-03-14T03:51:40.7957104Z  elif not experiment_settings.default: 2025-03-14T03:51:40.7957680Z  log.info( 2025-03-14T03:51:40.7958392Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-03-14T03:51:40.7959149Z  ) 2025-03-14T03:51:40.7959566Z  continue 2025-03-14T03:51:40.7960009Z  2025-03-14T03:51:40.7960489Z  # Is any workflow_requestor opted out to this experiment? 2025-03-14T03:51:40.7961149Z  opted_out_users = [ 2025-03-14T03:51:40.7961843Z  requestor 2025-03-14T03:51:40.7962356Z  for requestor in workflow_requestors 2025-03-14T03:51:40.7963063Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-03-14T03:51:40.7963715Z  ] 2025-03-14T03:51:40.7964097Z  2025-03-14T03:51:40.7964466Z  if opted_out_users: 2025-03-14T03:51:40.7964954Z  log.info( 2025-03-14T03:51:40.7965622Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-03-14T03:51:40.7966354Z  ) 2025-03-14T03:51:40.7966768Z  continue 2025-03-14T03:51:40.7967195Z  2025-03-14T03:51:40.7967665Z  # Is any workflow_requestor opted in to this experiment? 2025-03-14T03:51:40.7968307Z  opted_in_users = [ 2025-03-14T03:51:40.7968797Z  requestor 2025-03-14T03:51:40.7969369Z  for requestor in workflow_requestors 2025-03-14T03:51:40.7970067Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-03-14T03:51:40.7970854Z  ] 2025-03-14T03:51:40.7971227Z  2025-03-14T03:51:40.7971712Z  enabled = False 2025-03-14T03:51:40.7972188Z  if opted_in_users: 2025-03-14T03:51:40.7972669Z  log.info( 2025-03-14T03:51:40.7973320Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-03-14T03:51:40.7974016Z  ) 2025-03-14T03:51:40.7974444Z  enabled = True 2025-03-14T03:51:40.7974905Z  2025-03-14T03:51:40.7975324Z  elif experiment_settings.rollout_perc: 2025-03-14T03:51:40.7976172Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-03-14T03:51:40.7977141Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-03-14T03:51:40.7977820Z  log.info( 2025-03-14T03:51:40.7978746Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-03-14T03:51:40.7979693Z  ) 2025-03-14T03:51:40.7980139Z  enabled = True 2025-03-14T03:51:40.7980625Z  2025-03-14T03:51:40.7980976Z  if enabled: 2025-03-14T03:51:40.7981548Z  label = experiment_name 2025-03-14T03:51:40.7982142Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-03-14T03:51:40.7982985Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-03-14T03:51:40.7984034Z  # - If it's enabled, then we always list it's prefix first 2025-03-14T03:51:40.7984836Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-03-14T03:51:40.7985528Z  if is_canary: 2025-03-14T03:51:40.7986066Z  label += CANARY_FLEET_SUFFIX 2025-03-14T03:51:40.7986646Z  fleet_prefix = label 2025-03-14T03:51:40.7987166Z  else: 2025-03-14T03:51:40.7987641Z  prefixes.append(label) 2025-03-14T03:51:40.7988156Z  2025-03-14T03:51:40.7988517Z  if len(prefixes) > 1: 2025-03-14T03:51:40.7989009Z  log.error( 2025-03-14T03:51:40.7990063Z  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-03-14T03:51:40.7991210Z  ) 2025-03-14T03:51:40.7991734Z  prefixes = prefixes[:1] 2025-03-14T03:51:40.7992244Z  2025-03-14T03:51:40.7992614Z  # Fleet always comes first 2025-03-14T03:51:40.7993122Z  if fleet_prefix: 2025-03-14T03:51:40.7993621Z  prefixes.insert(0, fleet_prefix) 2025-03-14T03:51:40.7994144Z  2025-03-14T03:51:40.7994929Z  return ".".join(prefixes) + "." if prefixes else "" 2025-03-14T03:51:40.7995815Z  2025-03-14T03:51:40.7996164Z  2025-03-14T03:51:40.7996807Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-03-14T03:51:40.7997611Z  """ 2025-03-14T03:51:40.7998226Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-03-14T03:51:40.7998953Z  2025-03-14T03:51:40.7999552Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-03-14T03:51:40.8000281Z  """ 2025-03-14T03:51:40.8000706Z  gh = get_gh_client(github_token) 2025-03-14T03:51:40.8001285Z  issue = get_issue(gh, repo, issue_num) 2025-03-14T03:51:40.8002167Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-03-14T03:51:40.8002966Z  2025-03-14T03:51:40.8003312Z  2025-03-14T03:51:40.8003943Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-03-14T03:51:40.8004793Z  for _ in range(num_retries): 2025-03-14T03:51:40.8005307Z  try: 2025-03-14T03:51:40.8005774Z  req = Request(url=url, headers=headers) 2025-03-14T03:51:40.8006527Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-03-14T03:51:40.8007210Z  return json.loads(content) 2025-03-14T03:51:40.8007760Z  except Exception as e: 2025-03-14T03:51:40.8008356Z  log.warning(f"Could not download {url}: {e}") 2025-03-14T03:51:40.8008932Z  2025-03-14T03:51:40.8009533Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-03-14T03:51:40.8010291Z  return {} 2025-03-14T03:51:40.8010710Z  2025-03-14T03:51:40.8011049Z  2025-03-14T03:51:40.8011514Z @cache 2025-03-14T03:51:40.8012169Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-03-14T03:51:40.8012958Z  """ 2025-03-14T03:51:40.8013407Z  Dynamically get PR information 2025-03-14T03:51:40.8013935Z  """ 2025-03-14T03:51:40.8014461Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-03-14T03:51:40.8015124Z  headers = { 2025-03-14T03:51:40.8015628Z  "Accept": "application/vnd.github.v3+json", 2025-03-14T03:51:40.8016323Z  "Authorization": f"token {github_token}", 2025-03-14T03:51:40.8016875Z  } 2025-03-14T03:51:40.8017456Z  json_response: dict[str, Any] = download_json( 2025-03-14T03:51:40.8018104Z  url=f"{github_api}/issues/{pr_number}", 2025-03-14T03:51:40.8018670Z  headers=headers, 2025-03-14T03:51:40.8019135Z  ) 2025-03-14T03:51:40.8019505Z  2025-03-14T03:51:40.8019870Z  if not json_response: 2025-03-14T03:51:40.8020487Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-03-14T03:51:40.8021129Z  return {} 2025-03-14T03:51:40.8021758Z  2025-03-14T03:51:40.8022129Z  return json_response 2025-03-14T03:51:40.8022601Z  2025-03-14T03:51:40.8022937Z  2025-03-14T03:51:40.8023527Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-03-14T03:51:40.8024274Z  """ 2025-03-14T03:51:40.8024857Z  Dynamically get the latest list of labels from the pull request 2025-03-14T03:51:40.8025545Z  """ 2025-03-14T03:51:40.8026059Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-03-14T03:51:40.8026690Z  return { 2025-03-14T03:51:40.8027306Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-03-14T03:51:40.8028011Z  } 2025-03-14T03:51:40.8028373Z  2025-03-14T03:51:40.8028721Z  2025-03-14T03:51:40.8029086Z def main() -> None: 2025-03-14T03:51:40.8029545Z  args = parse_args() 2025-03-14T03:51:40.8030014Z  2025-03-14T03:51:40.8030437Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-03-14T03:51:40.8030995Z  2025-03-14T03:51:40.8031475Z  # Check if the PR is opt-out 2025-03-14T03:51:40.8031994Z  if args.pr_number: 2025-03-14T03:51:40.8032750Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-03-14T03:51:40.8033535Z  if OPT_OUT_LABEL in labels: 2025-03-14T03:51:40.8034065Z  log.info( 2025-03-14T03:51:40.8034794Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-03-14T03:51:40.8035749Z  ) 2025-03-14T03:51:40.8036339Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:40.8037045Z  sys.exit() 2025-03-14T03:51:40.8037496Z  2025-03-14T03:51:40.8037842Z  try: 2025-03-14T03:51:40.8038317Z  rollout_state = get_rollout_state_from_issue( 2025-03-14T03:51:40.8039060Z  args.github_token, args.github_issue_repo, args.github_issue 2025-03-14T03:51:40.8039724Z  ) 2025-03-14T03:51:40.8040114Z  2025-03-14T03:51:40.8040525Z  username = get_potential_pr_author( 2025-03-14T03:51:40.8041092Z  args.github_token, 2025-03-14T03:51:40.8041730Z  args.github_repo, 2025-03-14T03:51:40.8042242Z  args.github_actor, 2025-03-14T03:51:40.8042766Z  args.github_ref_type, 2025-03-14T03:51:40.8043307Z  args.github_branch, 2025-03-14T03:51:40.8043799Z  ) 2025-03-14T03:51:40.8044179Z  2025-03-14T03:51:40.8044655Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-03-14T03:51:40.8045292Z  2025-03-14T03:51:40.8045708Z  runner_label_prefix = get_runner_prefix( 2025-03-14T03:51:40.8046273Z  rollout_state, 2025-03-14T03:51:40.8046806Z  (args.github_issue_owner, username), 2025-03-14T03:51:40.8047374Z  args.github_branch, 2025-03-14T03:51:40.8047914Z  args.eligible_experiments, 2025-03-14T03:51:40.8048449Z  is_canary, 2025-03-14T03:51:40.8049018Z  ) 2025-03-14T03:51:40.8049405Z  2025-03-14T03:51:40.8049772Z  except Exception as e: 2025-03-14T03:51:40.8050265Z  log.error( 2025-03-14T03:51:40.8050983Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-03-14T03:51:40.8051866Z  ) 2025-03-14T03:51:40.8052239Z  2025-03-14T03:51:40.8052775Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:40.8053438Z  2025-03-14T03:51:40.8053781Z  2025-03-14T03:51:40.8054142Z if __name__ == "__main__": 2025-03-14T03:51:40.8054611Z  main() 2025-03-14T03:51:40.8055002Z  2025-03-14T03:51:40.8055339Z EOF 2025-03-14T03:51:40.8055698Z  2025-03-14T03:51:40.8056079Z cat runner_determinator.py 2025-03-14T03:51:40.8449465Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:40.8450270Z env: 2025-03-14T03:51:40.8450943Z GITHUB_TOKEN: *** 2025-03-14T03:51:40.8451767Z ISSUE_NUMBER: 5132 2025-03-14T03:51:40.8452526Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:40.8453122Z ISSUE_OWNER: 2025-03-14T03:51:40.8453521Z CHECK_EXPERIMENTS: 2025-03-14T03:51:40.8453922Z PR_NUMBER: 2025-03-14T03:51:40.8454288Z ##[endgroup] 2025-03-14T03:51:40.8698039Z # flake8: noqa: G004 2025-03-14T03:51:40.8698363Z 2025-03-14T03:51:40.8699239Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-03-14T03:51:40.8700288Z # must be kept in sync. You can do it easily by running the following command: 2025-03-14T03:51:40.8701075Z # python .github/scripts/update_runner_determinator.py 2025-03-14T03:51:40.8701700Z 2025-03-14T03:51:40.8701857Z """ 2025-03-14T03:51:40.8702407Z This runner determinator is used to determine which set of runners to run a 2025-03-14T03:51:40.8703237Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-03-14T03:51:40.8704139Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-03-14T03:51:40.8705294Z of which runners should be used to run which job. 2025-03-14T03:51:40.8705925Z 2025-03-14T03:51:40.8706301Z The configuration has two parts, the settings and a list of opted-in users, 2025-03-14T03:51:40.8707153Z separated by a line containing "---". If the line is not present, the 2025-03-14T03:51:40.8708186Z settings are considered to be empty with only the second part, the user 2025-03-14T03:51:40.8708845Z list, defined. 2025-03-14T03:51:40.8709068Z 2025-03-14T03:51:40.8709417Z The first part is a YAML block that defines the rollout settings. This can be 2025-03-14T03:51:40.8710291Z used to define any settings that are needed to determine which runners to use. 2025-03-14T03:51:40.8711079Z It's fields are defined by the RolloutSettings class below. 2025-03-14T03:51:40.8711705Z 2025-03-14T03:51:40.8712068Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-03-14T03:51:40.8712908Z The user list is also a comma separated list of additional features or 2025-03-14T03:51:40.8713598Z experiments which the user could be opted in to. 2025-03-14T03:51:40.8713995Z 2025-03-14T03:51:40.8714183Z The user list has the following rules: 2025-03-14T03:51:40.8714516Z 2025-03-14T03:51:40.8714806Z - Users are GitHub usernames, which must start with the @ prefix 2025-03-14T03:51:40.8715626Z - Each user is also a comma-separated list of features/experiments to enable 2025-03-14T03:51:40.8716343Z - A "#" prefix opts the user out of all experiments 2025-03-14T03:51:40.8716719Z 2025-03-14T03:51:40.8716883Z Example config: 2025-03-14T03:51:40.8717315Z # A list of experiments that can be opted into. 2025-03-14T03:51:40.8717952Z # This defines the behavior they'll induce when opted into. 2025-03-14T03:51:40.8718549Z # Expected syntax is: 2025-03-14T03:51:40.8719159Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-03-14T03:51:40.8720244Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-03-14T03:51:40.8720834Z 2025-03-14T03:51:40.8720997Z experiments: 2025-03-14T03:51:40.8721517Z lf: 2025-03-14T03:51:40.8721981Z rollout_percent: 25 2025-03-14T03:51:40.8722422Z all_branches: false 2025-03-14T03:51:40.8722848Z default: true 2025-03-14T03:51:40.8723233Z --- 2025-03-14T03:51:40.8723439Z 2025-03-14T03:51:40.8723594Z # Opt-ins: 2025-03-14T03:51:40.8724146Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-03-14T03:51:40.8724969Z # and specifying experiments to enable in a comma-separated list. 2025-03-14T03:51:40.8725702Z # To always opt out of an experiment, prefix it with a "-". 2025-03-14T03:51:40.8726324Z # Experiments should be from the above list. 2025-03-14T03:51:40.8726690Z 2025-03-14T03:51:40.8726862Z @User1,-lf,split_build 2025-03-14T03:51:40.8727277Z @User2,lf 2025-03-14T03:51:40.8727642Z @User3,split_build 2025-03-14T03:51:40.8728029Z """ 2025-03-14T03:51:40.8728206Z 2025-03-14T03:51:40.8728357Z import json 2025-03-14T03:51:40.8728702Z import logging 2025-03-14T03:51:40.8729064Z import os 2025-03-14T03:51:40.8729402Z import random 2025-03-14T03:51:40.8729755Z import re 2025-03-14T03:51:40.8730086Z import sys 2025-03-14T03:51:40.8730463Z from argparse import ArgumentParser 2025-03-14T03:51:40.8730964Z from collections.abc import Iterable 2025-03-14T03:51:40.8732159Z from functools import cache 2025-03-14T03:51:40.8732654Z from logging import LogRecord 2025-03-14T03:51:40.8733115Z from typing import Any, NamedTuple 2025-03-14T03:51:40.8733619Z from urllib.request import Request, urlopen 2025-03-14T03:51:40.8733984Z 2025-03-14T03:51:40.8734131Z import yaml 2025-03-14T03:51:40.8734503Z from github import Auth, Github 2025-03-14T03:51:40.8734968Z from github.Issue import Issue 2025-03-14T03:51:40.8735256Z 2025-03-14T03:51:40.8735263Z 2025-03-14T03:51:40.8735473Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-03-14T03:51:40.8736123Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-03-14T03:51:40.8736937Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-03-14T03:51:40.8737633Z 2025-03-14T03:51:40.8737851Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-03-14T03:51:40.8738398Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-03-14T03:51:40.8738881Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-03-14T03:51:40.8739394Z OPT_OUT_LABEL = "no-runner-experiments" 2025-03-14T03:51:40.8739728Z 2025-03-14T03:51:40.8739909Z SETTING_EXPERIMENTS = "experiments" 2025-03-14T03:51:40.8740230Z 2025-03-14T03:51:40.8740401Z LF_FLEET_EXPERIMENT = "lf" 2025-03-14T03:51:40.8740837Z CANARY_FLEET_SUFFIX = ".c" 2025-03-14T03:51:40.8741101Z 2025-03-14T03:51:40.8741107Z 2025-03-14T03:51:40.8741289Z class Experiment(NamedTuple): 2025-03-14T03:51:40.8741974Z rollout_perc: float = ( 2025-03-14T03:51:40.8742581Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-03-14T03:51:40.8743224Z ) 2025-03-14T03:51:40.8743572Z all_branches: bool = ( 2025-03-14T03:51:40.8744166Z False # If True, the experiment is also enabled on the exception branches 2025-03-14T03:51:40.8744812Z ) 2025-03-14T03:51:40.8745138Z default: bool = ( 2025-03-14T03:51:40.8745683Z True # If True, the experiment is enabled by default for all queries 2025-03-14T03:51:40.8746292Z ) 2025-03-14T03:51:40.8746472Z 2025-03-14T03:51:40.8746649Z # Add more fields as needed 2025-03-14T03:51:40.8746929Z 2025-03-14T03:51:40.8746935Z 2025-03-14T03:51:40.8747113Z class Settings(NamedTuple): 2025-03-14T03:51:40.8747524Z """ 2025-03-14T03:51:40.8747956Z Settings for the experiments that can be opted into. 2025-03-14T03:51:40.8748498Z """ 2025-03-14T03:51:40.8748678Z 2025-03-14T03:51:40.8748875Z experiments: dict[str, Experiment] = {} 2025-03-14T03:51:40.8749222Z 2025-03-14T03:51:40.8749227Z 2025-03-14T03:51:40.8749554Z class ColorFormatter(logging.Formatter): 2025-03-14T03:51:40.8750156Z """Color codes the log messages based on the log level""" 2025-03-14T03:51:40.8750565Z 2025-03-14T03:51:40.8750722Z COLORS = { 2025-03-14T03:51:40.8751096Z "WARNING": "\033[33m", # Yellow 2025-03-14T03:51:40.8751748Z "ERROR": "\033[31m", # Red 2025-03-14T03:51:40.8752217Z "CRITICAL": "\033[31m", # Red 2025-03-14T03:51:40.8752689Z "INFO": "\033[0m", # Reset 2025-03-14T03:51:40.8753136Z "DEBUG": "\033[0m", # Reset 2025-03-14T03:51:40.8753577Z } 2025-03-14T03:51:40.8753764Z 2025-03-14T03:51:40.8753968Z def format(self, record: LogRecord) -> str: 2025-03-14T03:51:40.8754682Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-03-14T03:51:40.8755415Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-03-14T03:51:40.8755960Z return super().format(record) 2025-03-14T03:51:40.8756282Z 2025-03-14T03:51:40.8756288Z 2025-03-14T03:51:40.8756477Z handler = logging.StreamHandler() 2025-03-14T03:51:40.8757150Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-03-14T03:51:40.8757684Z 2025-03-14T03:51:40.8758064Z log = logging.getLogger(os.path.basename(__file__)) 2025-03-14T03:51:40.8758789Z log.addHandler(handler) 2025-03-14T03:51:40.8759212Z log.setLevel(logging.INFO) 2025-03-14T03:51:40.8759483Z 2025-03-14T03:51:40.8759490Z 2025-03-14T03:51:40.8759729Z def set_github_output(key: str, value: str) -> None: 2025-03-14T03:51:40.8760269Z """ 2025-03-14T03:51:40.8760745Z Defines outputs of the github action that invokes this script 2025-03-14T03:51:40.8761342Z """ 2025-03-14T03:51:40.8762014Z if not GITHUB_OUTPUT: 2025-03-14T03:51:40.8763149Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-03-14T03:51:40.8764224Z log.warning( 2025-03-14T03:51:40.8765049Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-03-14T03:51:40.8765930Z ) 2025-03-14T03:51:40.8776402Z print(f"::set-output name={key}::{value}") 2025-03-14T03:51:40.8777215Z return 2025-03-14T03:51:40.8777434Z 2025-03-14T03:51:40.8777624Z with open(GITHUB_OUTPUT, "a") as f: 2025-03-14T03:51:40.8778181Z log.info(f"Setting output: {key}='{value}'") 2025-03-14T03:51:40.8778735Z f.write(f"{key}={value}\n") 2025-03-14T03:51:40.8779047Z 2025-03-14T03:51:40.8779053Z 2025-03-14T03:51:40.8779356Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-03-14T03:51:40.8779964Z return frozenset( 2025-03-14T03:51:40.8780542Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-03-14T03:51:40.8781198Z ) 2025-03-14T03:51:40.8781597Z 2025-03-14T03:51:40.8781606Z 2025-03-14T03:51:40.8781804Z def parse_args() -> Any: 2025-03-14T03:51:40.8782350Z parser = ArgumentParser("Get dynamic rollout settings") 2025-03-14T03:51:40.8783182Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-03-14T03:51:40.8783929Z parser.add_argument( 2025-03-14T03:51:40.8784355Z "--github-issue-repo", 2025-03-14T03:51:40.8784807Z type=str, 2025-03-14T03:51:40.8785186Z required=False, 2025-03-14T03:51:40.8785616Z default="pytorch/test-infra", 2025-03-14T03:51:40.8786132Z help="GitHub repo to get the issue", 2025-03-14T03:51:40.8786617Z ) 2025-03-14T03:51:40.8786965Z parser.add_argument( 2025-03-14T03:51:40.8787384Z "--github-repo", 2025-03-14T03:51:40.8787787Z type=str, 2025-03-14T03:51:40.8788157Z required=True, 2025-03-14T03:51:40.8788586Z help="GitHub repo where CI is running", 2025-03-14T03:51:40.8789083Z ) 2025-03-14T03:51:40.8789428Z parser.add_argument( 2025-03-14T03:51:40.8790000Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-03-14T03:51:40.8790759Z ) 2025-03-14T03:51:40.8791107Z parser.add_argument( 2025-03-14T03:51:40.8791874Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-03-14T03:51:40.8792526Z ) 2025-03-14T03:51:40.8792865Z parser.add_argument( 2025-03-14T03:51:40.8793468Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-03-14T03:51:40.8794112Z ) 2025-03-14T03:51:40.8794457Z parser.add_argument( 2025-03-14T03:51:40.8795073Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-03-14T03:51:40.8795738Z ) 2025-03-14T03:51:40.8796077Z parser.add_argument( 2025-03-14T03:51:40.8796497Z "--github-ref-type", 2025-03-14T03:51:40.8796923Z type=str, 2025-03-14T03:51:40.8797293Z required=True, 2025-03-14T03:51:40.8797747Z help="Current GitHub ref type, branch or tag", 2025-03-14T03:51:40.8798268Z ) 2025-03-14T03:51:40.8798606Z parser.add_argument( 2025-03-14T03:51:40.8799038Z "--eligible-experiments", 2025-03-14T03:51:40.8799523Z type=_str_comma_separated_to_set, 2025-03-14T03:51:40.8800017Z required=False, 2025-03-14T03:51:40.8800417Z default="", 2025-03-14T03:51:40.8801212Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-03-14T03:51:40.8802309Z ) 2025-03-14T03:51:40.8802657Z parser.add_argument( 2025-03-14T03:51:40.8803061Z "--pr-number", 2025-03-14T03:51:40.8803445Z type=str, 2025-03-14T03:51:40.8803813Z required=False, 2025-03-14T03:51:40.8804208Z default="", 2025-03-14T03:51:40.8804646Z help="the optional PR number where this is run", 2025-03-14T03:51:40.8805165Z ) 2025-03-14T03:51:40.8805348Z 2025-03-14T03:51:40.8805531Z return parser.parse_args() 2025-03-14T03:51:40.8805824Z 2025-03-14T03:51:40.8805830Z 2025-03-14T03:51:40.8806226Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-03-14T03:51:40.8806947Z auth = Auth.Token(github_token) 2025-03-14T03:51:40.8807427Z return Github(auth=auth) 2025-03-14T03:51:40.8807860Z 2025-03-14T03:51:40.8807866Z 2025-03-14T03:51:40.8808297Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-03-14T03:51:40.8809069Z repo = gh.get_repo(repo) 2025-03-14T03:51:40.8809540Z return repo.get_issue(number=issue_num) 2025-03-14T03:51:40.8809881Z 2025-03-14T03:51:40.8809887Z 2025-03-14T03:51:40.8810060Z def get_potential_pr_author( 2025-03-14T03:51:40.8810665Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-03-14T03:51:40.8811304Z ) -> str: 2025-03-14T03:51:40.8812039Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-03-14T03:51:40.8812815Z # Fetch the actual username from the original PR. The PR number is 2025-03-14T03:51:40.8813530Z # embedded in the tag name: ciflow// 2025-03-14T03:51:40.8813925Z 2025-03-14T03:51:40.8814102Z gh = get_gh_client(github_token) 2025-03-14T03:51:40.8814420Z 2025-03-14T03:51:40.8814667Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-03-14T03:51:40.8815262Z split_tag = ref_name.split("/") 2025-03-14T03:51:40.8815728Z if ( 2025-03-14T03:51:40.8816087Z len(split_tag) == 3 2025-03-14T03:51:40.8816534Z and split_tag[0] == "ciflow" 2025-03-14T03:51:40.8817027Z and split_tag[2].isnumeric() 2025-03-14T03:51:40.8817497Z ): 2025-03-14T03:51:40.8817863Z pr_number = split_tag[2] 2025-03-14T03:51:40.8818316Z try: 2025-03-14T03:51:40.8818707Z repository = gh.get_repo(repo) 2025-03-14T03:51:40.8819298Z pull = repository.get_pull(number=int(pr_number)) 2025-03-14T03:51:40.8819867Z except Exception as e: 2025-03-14T03:51:40.8820358Z raise Exception( # noqa: TRY002 2025-03-14T03:51:40.8821115Z f"issue with pull request {pr_number} from repo {repository}" 2025-03-14T03:51:40.8821938Z ) from e 2025-03-14T03:51:40.8822455Z return pull.user.login # type: ignore[no-any-return] 2025-03-14T03:51:40.8823116Z # In all other cases, return the original input username 2025-03-14T03:51:40.8823672Z return username 2025-03-14T03:51:40.8823901Z 2025-03-14T03:51:40.8823907Z 2025-03-14T03:51:40.8824120Z def is_exception_branch(branch: str) -> bool: 2025-03-14T03:51:40.8824622Z """ 2025-03-14T03:51:40.8825219Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-03-14T03:51:40.8825949Z """ 2025-03-14T03:51:40.8826464Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-03-14T03:51:40.8826960Z 2025-03-14T03:51:40.8826965Z 2025-03-14T03:51:40.8827155Z def load_yaml(yaml_text: str) -> Any: 2025-03-14T03:51:40.8827622Z try: 2025-03-14T03:51:40.8827987Z data = yaml.safe_load(yaml_text) 2025-03-14T03:51:40.8828466Z return data 2025-03-14T03:51:40.8828857Z except yaml.YAMLError: 2025-03-14T03:51:40.8829312Z log.exception("Error loading YAML") 2025-03-14T03:51:40.8829798Z raise 2025-03-14T03:51:40.8830001Z 2025-03-14T03:51:40.8830007Z 2025-03-14T03:51:40.8830389Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-03-14T03:51:40.8831092Z """ 2025-03-14T03:51:40.8831863Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-03-14T03:51:40.8832449Z 2025-03-14T03:51:40.8832773Z If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:40.8833486Z and the text below is the list of opted in users. 2025-03-14T03:51:40.8833865Z 2025-03-14T03:51:40.8834225Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-03-14T03:51:40.8834884Z """ 2025-03-14T03:51:40.8835298Z rollout_state_parts = rollout_state.split("---") 2025-03-14T03:51:40.8835865Z if len(rollout_state_parts) >= 2: 2025-03-14T03:51:40.8836430Z return rollout_state_parts[0], rollout_state_parts[1] 2025-03-14T03:51:40.8837128Z else: 2025-03-14T03:51:40.8837476Z return "", rollout_state 2025-03-14T03:51:40.8837772Z 2025-03-14T03:51:40.8837778Z 2025-03-14T03:51:40.8837962Z class UserOptins(dict[str, list[str]]): 2025-03-14T03:51:40.8838437Z """ 2025-03-14T03:51:40.8838926Z Dictionary of users with a list of features they have opted into 2025-03-14T03:51:40.8839534Z """ 2025-03-14T03:51:40.8839721Z 2025-03-14T03:51:40.8839727Z 2025-03-14T03:51:40.8840051Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-03-14T03:51:40.8840670Z """ 2025-03-14T03:51:40.8841337Z Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-03-14T03:51:40.8842244Z 2025-03-14T03:51:40.8842849Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-03-14T03:51:40.8843802Z - Example line: "@User1,lf,split_build" 2025-03-14T03:51:40.8844454Z - A "#" prefix indicates the user is opted out of all experiments 2025-03-14T03:51:40.8844904Z 2025-03-14T03:51:40.8844910Z 2025-03-14T03:51:40.8845062Z """ 2025-03-14T03:51:40.8845412Z optins = UserOptins() 2025-03-14T03:51:40.8845870Z for user in user_optin_text.split("\n"): 2025-03-14T03:51:40.8846396Z user = user.strip("\r\n\t -") 2025-03-14T03:51:40.8846907Z if not user or not user.startswith("@"): 2025-03-14T03:51:40.8847430Z # Not a valid user. Skip 2025-03-14T03:51:40.8847880Z continue 2025-03-14T03:51:40.8848115Z 2025-03-14T03:51:40.8848263Z if user: 2025-03-14T03:51:40.8848669Z usr_name = user.split(",")[0].strip("@") 2025-03-14T03:51:40.8849323Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-03-14T03:51:40.8849918Z 2025-03-14T03:51:40.8850086Z return optins 2025-03-14T03:51:40.8850304Z 2025-03-14T03:51:40.8850310Z 2025-03-14T03:51:40.8850589Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-03-14T03:51:40.8851154Z """ 2025-03-14T03:51:40.8851808Z Check if the experiment name is valid. 2025-03-14T03:51:40.8852312Z A valid name: 2025-03-14T03:51:40.8852910Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-03-14T03:51:40.8853803Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-03-14T03:51:40.8854473Z - Cannot contain spaces 2025-03-14T03:51:40.8854906Z """ 2025-03-14T03:51:40.8855098Z 2025-03-14T03:51:40.8855342Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-03-14T03:51:40.8856015Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-03-14T03:51:40.8856437Z 2025-03-14T03:51:40.8856592Z if valid: 2025-03-14T03:51:40.8856954Z return True 2025-03-14T03:51:40.8857178Z 2025-03-14T03:51:40.8857327Z log.error( 2025-03-14T03:51:40.8858714Z 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-03-14T03:51:40.8860189Z ) 2025-03-14T03:51:40.8860519Z return False 2025-03-14T03:51:40.8860741Z 2025-03-14T03:51:40.8860746Z 2025-03-14T03:51:40.8861041Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-03-14T03:51:40.8861819Z """ 2025-03-14T03:51:40.8862370Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-03-14T03:51:40.8863042Z """ 2025-03-14T03:51:40.8863374Z try: 2025-03-14T03:51:40.8863719Z if settings_text: 2025-03-14T03:51:40.8864403Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-03-14T03:51:40.8865162Z # for easy reading 2025-03-14T03:51:40.8865905Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-03-14T03:51:40.8866922Z # the backtick character in shell commands. 2025-03-14T03:51:40.8867491Z backtick = chr(96) # backtick character 2025-03-14T03:51:40.8868117Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-03-14T03:51:40.8868739Z settings = load_yaml(settings_text) 2025-03-14T03:51:40.8869084Z 2025-03-14T03:51:40.8869468Z # For now we just load experiments. We can expand this if/when we add more settings 2025-03-14T03:51:40.8870180Z experiments = {} 2025-03-14T03:51:40.8870462Z 2025-03-14T03:51:40.8870796Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-03-14T03:51:40.8871606Z if not is_valid_experiment_name(exp_name): 2025-03-14T03:51:40.8872662Z # 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-03-14T03:51:40.8873657Z continue 2025-03-14T03:51:40.8873923Z 2025-03-14T03:51:40.8874095Z valid_settings = {} 2025-03-14T03:51:40.8874577Z for setting in exp_settings: 2025-03-14T03:51:40.8875109Z if setting not in Experiment._fields: 2025-03-14T03:51:40.8875654Z log.warning( 2025-03-14T03:51:40.8876318Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-03-14T03:51:40.8876993Z ) 2025-03-14T03:51:40.8877387Z else: 2025-03-14T03:51:40.8877867Z valid_settings[setting] = exp_settings[setting] 2025-03-14T03:51:40.8878272Z 2025-03-14T03:51:40.8878529Z experiments[exp_name] = Experiment(**valid_settings) 2025-03-14T03:51:40.8879249Z return Settings(experiments) 2025-03-14T03:51:40.8879585Z 2025-03-14T03:51:40.8879752Z except Exception: 2025-03-14T03:51:40.8880188Z log.exception("Failed to parse settings") 2025-03-14T03:51:40.8880561Z 2025-03-14T03:51:40.8880720Z return Settings() 2025-03-14T03:51:40.8880963Z 2025-03-14T03:51:40.8880968Z 2025-03-14T03:51:40.8881936Z def parse_settings(rollout_state: str) -> Settings: 2025-03-14T03:51:40.8882847Z """ 2025-03-14T03:51:40.8883454Z Parse settings, if any, from the rollout state. 2025-03-14T03:51:40.8884087Z 2025-03-14T03:51:40.8884512Z If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:40.8885231Z and the text below is the list of opted in users. 2025-03-14T03:51:40.8885627Z 2025-03-14T03:51:40.8886041Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-03-14T03:51:40.8886732Z """ 2025-03-14T03:51:40.8887269Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:40.8887979Z return parse_settings_from_text(settings_text) 2025-03-14T03:51:40.8888355Z 2025-03-14T03:51:40.8888368Z 2025-03-14T03:51:40.8888603Z def parse_users(rollout_state: str) -> UserOptins: 2025-03-14T03:51:40.8889123Z """ 2025-03-14T03:51:40.8889490Z Parse users from the rollout state. 2025-03-14T03:51:40.8889825Z 2025-03-14T03:51:40.8889973Z """ 2025-03-14T03:51:40.8890478Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:40.8891166Z return parse_user_opt_in_from_text(users_text) 2025-03-14T03:51:40.8891913Z 2025-03-14T03:51:40.8891919Z 2025-03-14T03:51:40.8892342Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:40.8893042Z """ 2025-03-14T03:51:40.8893425Z Check if a user is opted into an experiment 2025-03-14T03:51:40.8893924Z """ 2025-03-14T03:51:40.8894347Z return experiment_name in user_optins.get(user, []) 2025-03-14T03:51:40.8894740Z 2025-03-14T03:51:40.8894747Z 2025-03-14T03:51:40.8895144Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:40.8896110Z """ 2025-03-14T03:51:40.8896536Z Check if a user explicitly opted out of an experiment 2025-03-14T03:51:40.8897069Z """ 2025-03-14T03:51:40.8897544Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-03-14T03:51:40.8898175Z experiment_optout = "-" + experiment_name 2025-03-14T03:51:40.8898771Z if experiment_optout not in user_optins.get(user, []): 2025-03-14T03:51:40.8899331Z return False 2025-03-14T03:51:40.8899565Z 2025-03-14T03:51:40.8899818Z if is_user_opted_in(user, user_optins, experiment_name): 2025-03-14T03:51:40.8900374Z log.warning( 2025-03-14T03:51:40.8901122Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-03-14T03:51:40.8902131Z ) 2025-03-14T03:51:40.8902326Z 2025-03-14T03:51:40.8902483Z return True 2025-03-14T03:51:40.8902704Z 2025-03-14T03:51:40.8902711Z 2025-03-14T03:51:40.8902874Z def get_runner_prefix( 2025-03-14T03:51:40.8903278Z rollout_state: str, 2025-03-14T03:51:40.8903704Z workflow_requestors: Iterable[str], 2025-03-14T03:51:40.8904187Z branch: str, 2025-03-14T03:51:40.8904631Z eligible_experiments: frozenset[str] = frozenset(), 2025-03-14T03:51:40.8905182Z is_canary: bool = False, 2025-03-14T03:51:40.8905688Z ) -> str: 2025-03-14T03:51:40.8906074Z settings = parse_settings(rollout_state) 2025-03-14T03:51:40.8906614Z user_optins = parse_users(rollout_state) 2025-03-14T03:51:40.8906957Z 2025-03-14T03:51:40.8907123Z fleet_prefix = "" 2025-03-14T03:51:40.8907512Z prefixes = [] 2025-03-14T03:51:40.8908096Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-03-14T03:51:40.8908980Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-03-14T03:51:40.8909787Z log.info( 2025-03-14T03:51:40.8910426Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-03-14T03:51:40.8911138Z ) 2025-03-14T03:51:40.8911614Z continue 2025-03-14T03:51:40.8911851Z 2025-03-14T03:51:40.8912025Z if eligible_experiments: 2025-03-14T03:51:40.8912545Z if experiment_name not in eligible_experiments: 2025-03-14T03:51:40.8913291Z exp_list = ", ".join(eligible_experiments) 2025-03-14T03:51:40.8913809Z log.info( 2025-03-14T03:51:40.8914558Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-03-14T03:51:40.8915350Z ) 2025-03-14T03:51:40.8915715Z continue 2025-03-14T03:51:40.8916185Z elif not experiment_settings.default: 2025-03-14T03:51:40.8916675Z log.info( 2025-03-14T03:51:40.8917299Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-03-14T03:51:40.8917987Z ) 2025-03-14T03:51:40.8918335Z continue 2025-03-14T03:51:40.8918571Z 2025-03-14T03:51:40.8918836Z # Is any workflow_requestor opted out to this experiment? 2025-03-14T03:51:40.8919415Z opted_out_users = [ 2025-03-14T03:51:40.8919825Z requestor 2025-03-14T03:51:40.8920252Z for requestor in workflow_requestors 2025-03-14T03:51:40.8920879Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-03-14T03:51:40.8921572Z ] 2025-03-14T03:51:40.8921768Z 2025-03-14T03:51:40.8921941Z if opted_out_users: 2025-03-14T03:51:40.8922348Z log.info( 2025-03-14T03:51:40.8922925Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-03-14T03:51:40.8923573Z ) 2025-03-14T03:51:40.8923915Z continue 2025-03-14T03:51:40.8924145Z 2025-03-14T03:51:40.8924406Z # Is any workflow_requestor opted in to this experiment? 2025-03-14T03:51:40.8924975Z opted_in_users = [ 2025-03-14T03:51:40.8925388Z requestor 2025-03-14T03:51:40.8925940Z for requestor in workflow_requestors 2025-03-14T03:51:40.8926559Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-03-14T03:51:40.8927136Z ] 2025-03-14T03:51:40.8927323Z 2025-03-14T03:51:40.8927477Z enabled = False 2025-03-14T03:51:40.8927882Z if opted_in_users: 2025-03-14T03:51:40.8928291Z log.info( 2025-03-14T03:51:40.8928852Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-03-14T03:51:40.8929493Z ) 2025-03-14T03:51:40.8929844Z enabled = True 2025-03-14T03:51:40.8930113Z 2025-03-14T03:51:40.8930313Z elif experiment_settings.rollout_perc: 2025-03-14T03:51:40.8931091Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-03-14T03:51:40.8932266Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-03-14T03:51:40.8932882Z log.info( 2025-03-14T03:51:40.8933703Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-03-14T03:51:40.8934565Z ) 2025-03-14T03:51:40.8934942Z enabled = True 2025-03-14T03:51:40.8935226Z 2025-03-14T03:51:40.8935379Z if enabled: 2025-03-14T03:51:40.8935769Z label = experiment_name 2025-03-14T03:51:40.8936287Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-03-14T03:51:40.8937061Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-03-14T03:51:40.8937885Z # - If it's enabled, then we always list it's prefix first 2025-03-14T03:51:40.8938722Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-03-14T03:51:40.8939347Z if is_canary: 2025-03-14T03:51:40.8939804Z label += CANARY_FLEET_SUFFIX 2025-03-14T03:51:40.8940317Z fleet_prefix = label 2025-03-14T03:51:40.8940767Z else: 2025-03-14T03:51:40.8941162Z prefixes.append(label) 2025-03-14T03:51:40.8941708Z 2025-03-14T03:51:40.8941884Z if len(prefixes) > 1: 2025-03-14T03:51:40.8942297Z log.error( 2025-03-14T03:51:40.8943268Z 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-03-14T03:51:40.8944305Z ) 2025-03-14T03:51:40.8944663Z prefixes = prefixes[:1] 2025-03-14T03:51:40.8944958Z 2025-03-14T03:51:40.8945133Z # Fleet always comes first 2025-03-14T03:51:40.8945564Z if fleet_prefix: 2025-03-14T03:51:40.8945977Z prefixes.insert(0, fleet_prefix) 2025-03-14T03:51:40.8946312Z 2025-03-14T03:51:40.8946555Z return ".".join(prefixes) + "." if prefixes else "" 2025-03-14T03:51:40.8946941Z 2025-03-14T03:51:40.8946946Z 2025-03-14T03:51:40.8947358Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-03-14T03:51:40.8948087Z """ 2025-03-14T03:51:40.8948641Z Gets the first comment of the issue, which contains the desired rollout state. 2025-03-14T03:51:40.8949166Z 2025-03-14T03:51:40.8949527Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-03-14T03:51:40.8950185Z """ 2025-03-14T03:51:40.8950533Z gh = get_gh_client(github_token) 2025-03-14T03:51:40.8951031Z issue = get_issue(gh, repo, issue_num) 2025-03-14T03:51:40.8951737Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-03-14T03:51:40.8952157Z 2025-03-14T03:51:40.8952163Z 2025-03-14T03:51:40.8952534Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-03-14T03:51:40.8953249Z for _ in range(num_retries): 2025-03-14T03:51:40.8953689Z try: 2025-03-14T03:51:40.8954074Z req = Request(url=url, headers=headers) 2025-03-14T03:51:40.8954690Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-03-14T03:51:40.8955428Z return json.loads(content) 2025-03-14T03:51:40.8955916Z except Exception as e: 2025-03-14T03:51:40.8956412Z log.warning(f"Could not download {url}: {e}") 2025-03-14T03:51:40.8956785Z 2025-03-14T03:51:40.8957142Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-03-14T03:51:40.8957804Z return {} 2025-03-14T03:51:40.8958010Z 2025-03-14T03:51:40.8958017Z 2025-03-14T03:51:40.8958159Z @cache 2025-03-14T03:51:40.8958734Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-03-14T03:51:40.8959432Z """ 2025-03-14T03:51:40.8959790Z Dynamically get PR information 2025-03-14T03:51:40.8960233Z """ 2025-03-14T03:51:40.8960693Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-03-14T03:51:40.8961269Z headers = { 2025-03-14T03:51:40.8961802Z "Accept": "application/vnd.github.v3+json", 2025-03-14T03:51:40.8962364Z "Authorization": f"token {github_token}", 2025-03-14T03:51:40.8962853Z } 2025-03-14T03:51:40.8963245Z json_response: dict[str, Any] = download_json( 2025-03-14T03:51:40.8963806Z url=f"{github_api}/issues/{pr_number}", 2025-03-14T03:51:40.8964318Z headers=headers, 2025-03-14T03:51:40.8964704Z ) 2025-03-14T03:51:40.8964887Z 2025-03-14T03:51:40.8965052Z if not json_response: 2025-03-14T03:51:40.8965579Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-03-14T03:51:40.8966153Z return {} 2025-03-14T03:51:40.8966368Z 2025-03-14T03:51:40.8966535Z return json_response 2025-03-14T03:51:40.8966790Z 2025-03-14T03:51:40.8966797Z 2025-03-14T03:51:40.8967171Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-03-14T03:51:40.8967968Z """ 2025-03-14T03:51:40.8968457Z Dynamically get the latest list of labels from the pull request 2025-03-14T03:51:40.8969065Z """ 2025-03-14T03:51:40.8969518Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-03-14T03:51:40.8970079Z return { 2025-03-14T03:51:40.8970615Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-03-14T03:51:40.8971268Z } 2025-03-14T03:51:40.8971725Z 2025-03-14T03:51:40.8971732Z 2025-03-14T03:51:40.8971910Z def main() -> None: 2025-03-14T03:51:40.8972305Z args = parse_args() 2025-03-14T03:51:40.8972558Z 2025-03-14T03:51:40.8972771Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-03-14T03:51:40.8973133Z 2025-03-14T03:51:40.8973315Z # Check if the PR is opt-out 2025-03-14T03:51:40.8973757Z if args.pr_number: 2025-03-14T03:51:40.8974364Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-03-14T03:51:40.8975064Z if OPT_OUT_LABEL in labels: 2025-03-14T03:51:40.8975630Z log.info( 2025-03-14T03:51:40.8976274Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-03-14T03:51:40.8976984Z ) 2025-03-14T03:51:40.8977486Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:40.8978101Z sys.exit() 2025-03-14T03:51:40.8978345Z 2025-03-14T03:51:40.8978487Z try: 2025-03-14T03:51:40.8978888Z rollout_state = get_rollout_state_from_issue( 2025-03-14T03:51:40.8979543Z args.github_token, args.github_issue_repo, args.github_issue 2025-03-14T03:51:40.8980128Z ) 2025-03-14T03:51:40.8980317Z 2025-03-14T03:51:40.8980509Z username = get_potential_pr_author( 2025-03-14T03:51:40.8981010Z args.github_token, 2025-03-14T03:51:40.8981663Z args.github_repo, 2025-03-14T03:51:40.8982125Z args.github_actor, 2025-03-14T03:51:40.8982562Z args.github_ref_type, 2025-03-14T03:51:40.8983023Z args.github_branch, 2025-03-14T03:51:40.8983440Z ) 2025-03-14T03:51:40.8983629Z 2025-03-14T03:51:40.8983889Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-03-14T03:51:40.8984481Z 2025-03-14T03:51:40.8984684Z runner_label_prefix = get_runner_prefix( 2025-03-14T03:51:40.8985191Z rollout_state, 2025-03-14T03:51:40.8985643Z (args.github_issue_owner, username), 2025-03-14T03:51:40.8986150Z args.github_branch, 2025-03-14T03:51:40.8986604Z args.eligible_experiments, 2025-03-14T03:51:40.8987076Z is_canary, 2025-03-14T03:51:40.8987450Z ) 2025-03-14T03:51:40.8987640Z 2025-03-14T03:51:40.8987807Z except Exception as e: 2025-03-14T03:51:40.8988235Z log.error( 2025-03-14T03:51:40.8988852Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-03-14T03:51:40.8989560Z ) 2025-03-14T03:51:40.8989749Z 2025-03-14T03:51:40.8990064Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:40.8990524Z 2025-03-14T03:51:40.8990530Z 2025-03-14T03:51:40.8990700Z if __name__ == "__main__": 2025-03-14T03:51:40.8991104Z main() 2025-03-14T03:51:40.8991310Z 2025-03-14T03:51:40.9079719Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-03-14T03:51:40.9080551Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-03-14T03:51:40.9127569Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:40.9128034Z env: 2025-03-14T03:51:40.9128621Z GITHUB_TOKEN: *** 2025-03-14T03:51:40.9129016Z ISSUE_NUMBER: 5132 2025-03-14T03:51:40.9129434Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:40.9129909Z ISSUE_OWNER: 2025-03-14T03:51:40.9130281Z CHECK_EXPERIMENTS: 2025-03-14T03:51:40.9130667Z PR_NUMBER: 2025-03-14T03:51:40.9131031Z ##[endgroup] 2025-03-14T03:51:41.2907344Z Defaulting to user installation because normal site-packages is not writeable 2025-03-14T03:51:41.6090986Z Collecting urllib3==1.26.18 2025-03-14T03:51:41.6584071Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-03-14T03:51:41.6858304Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 3.4 MB/s eta 0:00:00 2025-03-14T03:51:41.7122845Z Collecting PyGithub==2.3.0 2025-03-14T03:51:41.7177651Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-03-14T03:51:41.7665252Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-03-14T03:51:41.7739450Z 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-03-14T03:51:41.7793452Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-03-14T03:51:41.7812176Z 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-03-14T03:51:41.7828817Z Requirement already satisfied: typing-extensions>=4.0.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (4.10.0) 2025-03-14T03:51:41.8130370Z Collecting Deprecated (from PyGithub==2.3.0) 2025-03-14T03:51:41.8202722Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl.metadata (5.7 kB) 2025-03-14T03:51:41.8435214Z 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-03-14T03:51:41.9617318Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-03-14T03:51:41.9692781Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-03-14T03:51:42.0782057Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-03-14T03:51:42.0870729Z 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-03-14T03:51:42.1105381Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-03-14T03:51:42.1186236Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-03-14T03:51:42.1464600Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-03-14T03:51:42.1627458Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 9.3 MB/s eta 0:00:00 2025-03-14T03:51:42.1706432Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-03-14T03:51:42.1985811Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 13.2 MB/s eta 0:00:00 2025-03-14T03:51:42.2064212Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (856 kB) 2025-03-14T03:51:42.2572867Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 17.2 MB/s eta 0:00:00 2025-03-14T03:51:42.2648393Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl (10.0 kB) 2025-03-14T03:51:42.2746319Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-03-14T03:51:42.2982195Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 21.5 MB/s eta 0:00:00 2025-03-14T03:51:42.3053505Z 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-03-14T03:51:42.3103393Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 24.2 MB/s eta 0:00:00 2025-03-14T03:51:42.3225549Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-03-14T03:51:42.3282372Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 28.4 MB/s eta 0:00:00 2025-03-14T03:51:42.6139236Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-03-14T03:51:43.1437575Z Successfully installed Deprecated-1.2.18 PyGithub-2.3.0 cffi-1.17.1 pycparser-2.22 pynacl-1.5.0 urllib3-1.26.18 wrapt-1.17.2 2025-03-14T03:51:43.2237380Z ##[group]Run curr_branch="main" 2025-03-14T03:51:43.2237747Z curr_branch="main" 2025-03-14T03:51:43.2238020Z curr_ref_type="branch" 2025-03-14T03:51:43.2238320Z echo "Current branch is '$curr_branch'" 2025-03-14T03:51:43.2238631Z  2025-03-14T03:51:43.2238871Z python3 runner_determinator.py \ 2025-03-14T03:51:43.2239208Z  --github-token "$GITHUB_TOKEN" \ 2025-03-14T03:51:43.2239551Z  --github-issue "$ISSUE_NUMBER" \ 2025-03-14T03:51:43.2239860Z  --github-branch "$curr_branch" \ 2025-03-14T03:51:43.2240182Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-03-14T03:51:43.2240520Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-03-14T03:51:43.2240853Z  --github-ref-type "$curr_ref_type" \ 2025-03-14T03:51:43.2241190Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-03-14T03:51:43.2241853Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-03-14T03:51:43.2242282Z  --pr-number "${PR_NUMBER}" 2025-03-14T03:51:43.2289391Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:43.2289675Z env: 2025-03-14T03:51:43.2290322Z GITHUB_TOKEN: *** 2025-03-14T03:51:43.2290571Z ISSUE_NUMBER: 5132 2025-03-14T03:51:43.2290818Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:43.2291099Z ISSUE_OWNER: 2025-03-14T03:51:43.2291325Z CHECK_EXPERIMENTS: 2025-03-14T03:51:43.2291719Z PR_NUMBER: 2025-03-14T03:51:43.2291951Z ##[endgroup] 2025-03-14T03:51:43.2356488Z Current branch is 'main' 2025-03-14T03:51:44.5226301Z INFO : Based on rollout percentage of 50%, enabling experiment lf. 2025-03-14T03:51:44.5227242Z INFO : Branch main is an exception branch. Not enabling experiment ephemeral. 2025-03-14T03:51:44.5227811Z INFO : Setting output: label-type='lf.' 2025-03-14T03:51:44.5529256Z Evaluate and set job outputs 2025-03-14T03:51:44.5536393Z Set output 'label-type' 2025-03-14T03:51:44.5538185Z Cleaning up orphan processes