2025-01-24T00:44:50.6809020Z Current runner version: '2.321.0' 2025-01-24T00:44:50.6832699Z ##[group]Operating System 2025-01-24T00:44:50.6833441Z Ubuntu 2025-01-24T00:44:50.6833965Z 24.04.1 2025-01-24T00:44:50.6834507Z LTS 2025-01-24T00:44:50.6834966Z ##[endgroup] 2025-01-24T00:44:50.6835705Z ##[group]Runner Image 2025-01-24T00:44:50.6836364Z Image: ubuntu-24.04 2025-01-24T00:44:50.6836858Z Version: 20250120.5.0 2025-01-24T00:44:50.6837910Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250120.5/images/ubuntu/Ubuntu2404-Readme.md 2025-01-24T00:44:50.6839347Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250120.5 2025-01-24T00:44:50.6840240Z ##[endgroup] 2025-01-24T00:44:50.6840754Z ##[group]Runner Image Provisioner 2025-01-24T00:44:50.6841382Z 2.0.417.1 2025-01-24T00:44:50.6841816Z ##[endgroup] 2025-01-24T00:44:50.6842799Z ##[group]GITHUB_TOKEN Permissions 2025-01-24T00:44:50.6844870Z Contents: read 2025-01-24T00:44:50.6845398Z Metadata: read 2025-01-24T00:44:50.6846341Z ##[endgroup] 2025-01-24T00:44:50.6849430Z Secret source: Actions 2025-01-24T00:44:50.6850512Z Prepare workflow directory 2025-01-24T00:44:50.7332507Z Prepare all required actions 2025-01-24T00:44:50.7387115Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (b2c89bc115123aea8e075e882ee121537ec92f89) 2025-01-24T00:44:50.7391776Z ##[group] Inputs 2025-01-24T00:44:50.7392433Z check_experiments: 2025-01-24T00:44:50.7393068Z triggering_actor: pytorch-bot[bot] 2025-01-24T00:44:50.7393684Z issue_owner: 2025-01-24T00:44:50.7394239Z curr_branch: ciflow/inductor/145539 2025-01-24T00:44:50.7394940Z curr_ref_type: tag 2025-01-24T00:44:50.7395468Z issue_number: 5132 2025-01-24T00:44:50.7396181Z ##[endgroup] 2025-01-24T00:44:50.7396830Z Complete job name: get-label-type / runner-determinator 2025-01-24T00:44:51.5986140Z ##[group]Run cat < runner_determinator.py 2025-01-24T00:44:51.5987885Z cat < runner_determinator.py 2025-01-24T00:44:51.5988450Z # flake8: noqa: G004 2025-01-24T00:44:51.5988919Z  2025-01-24T00:44:51.5989597Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-01-24T00:44:51.5990607Z # must be kept in sync. You can do it easily by running the following command: 2025-01-24T00:44:51.5991464Z # python .github/scripts/update_runner_determinator.py 2025-01-24T00:44:51.5992109Z  2025-01-24T00:44:51.5992470Z """ 2025-01-24T00:44:51.5993086Z This runner determinator is used to determine which set of runners to run a 2025-01-24T00:44:51.5994053Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-01-24T00:44:51.5995088Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-01-24T00:44:51.5996147Z of which runners should be used to run which job. 2025-01-24T00:44:51.5996751Z  2025-01-24T00:44:51.5997371Z The configuration has two parts, the settings and a list of opted-in users, 2025-01-24T00:44:51.5998351Z separated by a line containing "---". If the line is not present, the 2025-01-24T00:44:51.5999301Z settings are considered to be empty with only the second part, the user 2025-01-24T00:44:51.6000046Z list, defined. 2025-01-24T00:44:51.6000469Z  2025-01-24T00:44:51.6001063Z The first part is a YAML block that defines the rollout settings. This can be 2025-01-24T00:44:51.6002053Z used to define any settings that are needed to determine which runners to use. 2025-01-24T00:44:51.6002948Z It's fields are defined by the RolloutSettings class below. 2025-01-24T00:44:51.6003637Z  2025-01-24T00:44:51.6004284Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-01-24T00:44:51.6005302Z The user list is also a comma separated list of additional features or 2025-01-24T00:44:51.6006529Z experiments which the user could be opted in to. 2025-01-24T00:44:51.6007146Z  2025-01-24T00:44:51.6007571Z The user list has the following rules: 2025-01-24T00:44:51.6008120Z  2025-01-24T00:44:51.6008699Z - Users are GitHub usernames, which must start with the @ prefix 2025-01-24T00:44:51.6009670Z - Each user is also a comma-separated list of features/experiments to enable 2025-01-24T00:44:51.6010534Z - A "#" prefix opts the user out of all experiments 2025-01-24T00:44:51.6011139Z  2025-01-24T00:44:51.6011608Z Example config: 2025-01-24T00:44:51.6012144Z  # A list of experiments that can be opted into. 2025-01-24T00:44:51.6012915Z  # This defines the behavior they'll induce when opted into. 2025-01-24T00:44:51.6013628Z  # Expected syntax is: 2025-01-24T00:44:51.6014434Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-01-24T00:44:51.6015752Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-01-24T00:44:51.6016701Z  2025-01-24T00:44:51.6017087Z  experiments: 2025-01-24T00:44:51.6017548Z  lf: 2025-01-24T00:44:51.6017994Z  rollout_percent: 25 2025-01-24T00:44:51.6018863Z  all_branches: false 2025-01-24T00:44:51.6019631Z  default: true 2025-01-24T00:44:51.6020134Z  --- 2025-01-24T00:44:51.6020544Z  2025-01-24T00:44:51.6020928Z  # Opt-ins: 2025-01-24T00:44:51.6021646Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-01-24T00:44:51.6022891Z  # and specifying experiments to enable in a comma-separated list. 2025-01-24T00:44:51.6023863Z  # To always opt out of an experiment, prefix it with a "-". 2025-01-24T00:44:51.6024675Z  # Experiments should be from the above list. 2025-01-24T00:44:51.6025303Z  2025-01-24T00:44:51.6025857Z  @User1,-lf,split_build 2025-01-24T00:44:51.6026395Z  @User2,lf 2025-01-24T00:44:51.6026845Z  @User3,split_build 2025-01-24T00:44:51.6027330Z """ 2025-01-24T00:44:51.6027709Z  2025-01-24T00:44:51.6028080Z import json 2025-01-24T00:44:51.6028504Z import logging 2025-01-24T00:44:51.6028949Z import os 2025-01-24T00:44:51.6029369Z import random 2025-01-24T00:44:51.6029811Z import re 2025-01-24T00:44:51.6030224Z import sys 2025-01-24T00:44:51.6030713Z from argparse import ArgumentParser 2025-01-24T00:44:51.6031324Z from functools import lru_cache 2025-01-24T00:44:51.6031913Z from logging import LogRecord 2025-01-24T00:44:51.6032758Z from typing import Any, Dict, FrozenSet, Iterable, List, NamedTuple, Set, Tuple 2025-01-24T00:44:51.6033698Z from urllib.request import Request, urlopen 2025-01-24T00:44:51.6034303Z  2025-01-24T00:44:51.6034675Z import yaml 2025-01-24T00:44:51.6035126Z from github import Auth, Github 2025-01-24T00:44:51.6035815Z from github.Issue import Issue 2025-01-24T00:44:51.6036363Z  2025-01-24T00:44:51.6036719Z  2025-01-24T00:44:51.6037190Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-01-24T00:44:51.6038024Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-01-24T00:44:51.6039060Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-01-24T00:44:51.6039902Z  2025-01-24T00:44:51.6040380Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-01-24T00:44:51.6041051Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-01-24T00:44:51.6041670Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-01-24T00:44:51.6042339Z OPT_OUT_LABEL = "no-runner-experiments" 2025-01-24T00:44:51.6043102Z  2025-01-24T00:44:51.6043526Z SETTING_EXPERIMENTS = "experiments" 2025-01-24T00:44:51.6044093Z  2025-01-24T00:44:51.6044479Z LF_FLEET_EXPERIMENT = "lf" 2025-01-24T00:44:51.6045022Z CANARY_FLEET_SUFFIX = ".c" 2025-01-24T00:44:51.6045665Z  2025-01-24T00:44:51.6046023Z  2025-01-24T00:44:51.6046429Z class Experiment(NamedTuple): 2025-01-24T00:44:51.6046992Z  rollout_perc: float = ( 2025-01-24T00:44:51.6047775Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-01-24T00:44:51.6048581Z  ) 2025-01-24T00:44:51.6048992Z  all_branches: bool = ( 2025-01-24T00:44:51.6049772Z  False # If True, the experiment is also enabled on the exception branches 2025-01-24T00:44:51.6050577Z  ) 2025-01-24T00:44:51.6051022Z  default: bool = ( 2025-01-24T00:44:51.6051731Z  True # If True, the experiment is enabled by default for all queries 2025-01-24T00:44:51.6052480Z  ) 2025-01-24T00:44:51.6052868Z  2025-01-24T00:44:51.6053266Z  # Add more fields as needed 2025-01-24T00:44:51.6053792Z  2025-01-24T00:44:51.6054154Z  2025-01-24T00:44:51.6054542Z class Settings(NamedTuple): 2025-01-24T00:44:51.6055053Z  """ 2025-01-24T00:44:51.6055682Z  Settings for the experiments that can be opted into. 2025-01-24T00:44:51.6056347Z  """ 2025-01-24T00:44:51.6056724Z  2025-01-24T00:44:51.6057158Z  experiments: Dict[str, Experiment] = {} 2025-01-24T00:44:51.6057755Z  2025-01-24T00:44:51.6058108Z  2025-01-24T00:44:51.6058677Z class ColorFormatter(logging.Formatter): 2025-01-24T00:44:51.6059441Z  """Color codes the log messages based on the log level""" 2025-01-24T00:44:51.6060115Z  2025-01-24T00:44:51.6060489Z  COLORS = { 2025-01-24T00:44:51.6060969Z  "WARNING": "\033[33m", # Yellow 2025-01-24T00:44:51.6061563Z  "ERROR": "\033[31m", # Red 2025-01-24T00:44:51.6062138Z  "CRITICAL": "\033[31m", # Red 2025-01-24T00:44:51.6062728Z  "INFO": "\033[0m", # Reset 2025-01-24T00:44:51.6063300Z  "DEBUG": "\033[0m", # Reset 2025-01-24T00:44:51.6063852Z  } 2025-01-24T00:44:51.6064230Z  2025-01-24T00:44:51.6064678Z  def format(self, record: LogRecord) -> str: 2025-01-24T00:44:51.6065713Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-01-24T00:44:51.6066654Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-01-24T00:44:51.6067332Z  return super().format(record) 2025-01-24T00:44:51.6067889Z  2025-01-24T00:44:51.6068255Z  2025-01-24T00:44:51.6068670Z handler = logging.StreamHandler() 2025-01-24T00:44:51.6069521Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-01-24T00:44:51.6070344Z  2025-01-24T00:44:51.6070835Z log = logging.getLogger(os.path.basename(__file__)) 2025-01-24T00:44:51.6071514Z log.addHandler(handler) 2025-01-24T00:44:51.6072070Z log.setLevel(logging.INFO) 2025-01-24T00:44:51.6072591Z  2025-01-24T00:44:51.6072951Z  2025-01-24T00:44:51.6073453Z def set_github_output(key: str, value: str) -> None: 2025-01-24T00:44:51.6074105Z  """ 2025-01-24T00:44:51.6074705Z  Defines outputs of the github action that invokes this script 2025-01-24T00:44:51.6075435Z  """ 2025-01-24T00:44:51.6075955Z  if not GITHUB_OUTPUT: 2025-01-24T00:44:51.6077207Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-01-24T00:44:51.6078640Z  log.warning( 2025-01-24T00:44:51.6079662Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-01-24T00:44:51.6080736Z  ) 2025-01-24T00:44:51.6081235Z  print(f"::set-output name={key}::{value}") 2025-01-24T00:44:51.6081847Z  return 2025-01-24T00:44:51.6082271Z  2025-01-24T00:44:51.6082695Z  with open(GITHUB_OUTPUT, "a") as f: 2025-01-24T00:44:51.6083361Z  log.info(f"Setting output: {key}='{value}'") 2025-01-24T00:44:51.6084015Z  f.write(f"{key}={value}\n") 2025-01-24T00:44:51.6084564Z  2025-01-24T00:44:51.6084935Z  2025-01-24T00:44:51.6085488Z def _str_comma_separated_to_set(value: str) -> FrozenSet[str]: 2025-01-24T00:44:51.6086336Z  return frozenset( 2025-01-24T00:44:51.6087085Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-01-24T00:44:51.6087877Z  ) 2025-01-24T00:44:51.6088263Z  2025-01-24T00:44:51.6088618Z  2025-01-24T00:44:51.6089000Z def parse_args() -> Any: 2025-01-24T00:44:51.6089665Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-01-24T00:44:51.6090686Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-01-24T00:44:51.6091562Z  parser.add_argument( 2025-01-24T00:44:51.6092092Z  "--github-issue-repo", 2025-01-24T00:44:51.6092640Z  type=str, 2025-01-24T00:44:51.6093102Z  required=False, 2025-01-24T00:44:51.6093760Z  default="pytorch/test-infra", 2025-01-24T00:44:51.6094394Z  help="GitHub repo to get the issue", 2025-01-24T00:44:51.6094971Z  ) 2025-01-24T00:44:51.6095382Z  parser.add_argument( 2025-01-24T00:44:51.6096005Z  "--github-repo", 2025-01-24T00:44:51.6096506Z  type=str, 2025-01-24T00:44:51.6096964Z  required=True, 2025-01-24T00:44:51.6097517Z  help="GitHub repo where CI is running", 2025-01-24T00:44:51.6098104Z  ) 2025-01-24T00:44:51.6098500Z  parser.add_argument( 2025-01-24T00:44:51.6099234Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-01-24T00:44:51.6099992Z  ) 2025-01-24T00:44:51.6100393Z  parser.add_argument( 2025-01-24T00:44:51.6101149Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-01-24T00:44:51.6101936Z  ) 2025-01-24T00:44:51.6102347Z  parser.add_argument( 2025-01-24T00:44:51.6103114Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-01-24T00:44:51.6103913Z  ) 2025-01-24T00:44:51.6104322Z  parser.add_argument( 2025-01-24T00:44:51.6105115Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-01-24T00:44:51.6106029Z  ) 2025-01-24T00:44:51.6106436Z  parser.add_argument( 2025-01-24T00:44:51.6106954Z  "--github-ref-type", 2025-01-24T00:44:51.6107474Z  type=str, 2025-01-24T00:44:51.6107941Z  required=True, 2025-01-24T00:44:51.6108527Z  help="Current GitHub ref type, branch or tag", 2025-01-24T00:44:51.6109152Z  ) 2025-01-24T00:44:51.6109554Z  parser.add_argument( 2025-01-24T00:44:51.6110087Z  "--eligible-experiments", 2025-01-24T00:44:51.6110695Z  type=_str_comma_separated_to_set, 2025-01-24T00:44:51.6111278Z  required=False, 2025-01-24T00:44:51.6111773Z  default="", 2025-01-24T00:44:51.6112945Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-01-24T00:44:51.6114011Z  ) 2025-01-24T00:44:51.6114412Z  parser.add_argument( 2025-01-24T00:44:51.6114940Z  "--pr-number", 2025-01-24T00:44:51.6115431Z  type=str, 2025-01-24T00:44:51.6116065Z  required=False, 2025-01-24T00:44:51.6116576Z  default="", 2025-01-24T00:44:51.6117196Z  help="the optional PR number where this is run", 2025-01-24T00:44:51.6117832Z  ) 2025-01-24T00:44:51.6118216Z  2025-01-24T00:44:51.6118623Z  return parser.parse_args() 2025-01-24T00:44:51.6119147Z  2025-01-24T00:44:51.6119509Z  2025-01-24T00:44:51.6119980Z def get_gh_client(github_token: str) -> Github: 2025-01-24T00:44:51.6120636Z  auth = Auth.Token(github_token) 2025-01-24T00:44:51.6121217Z  return Github(auth=auth) 2025-01-24T00:44:51.6121728Z  2025-01-24T00:44:51.6122077Z  2025-01-24T00:44:51.6122624Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: 2025-01-24T00:44:51.6123355Z  repo = gh.get_repo(repo) 2025-01-24T00:44:51.6123937Z  return repo.get_issue(number=issue_num) 2025-01-24T00:44:51.6124509Z  2025-01-24T00:44:51.6124869Z  2025-01-24T00:44:51.6125255Z def get_potential_pr_author( 2025-01-24T00:44:51.6126126Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-01-24T00:44:51.6126909Z ) -> str: 2025-01-24T00:44:51.6127531Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-01-24T00:44:51.6128592Z  # Fetch the actual username from the original PR. The PR number is 2025-01-24T00:44:51.6129465Z  # embedded in the tag name: ciflow// 2025-01-24T00:44:51.6130117Z  2025-01-24T00:44:51.6130529Z  gh = get_gh_client(github_token) 2025-01-24T00:44:51.6131072Z  2025-01-24T00:44:51.6131586Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-01-24T00:44:51.6132307Z  split_tag = ref_name.split("/") 2025-01-24T00:44:51.6132874Z  if ( 2025-01-24T00:44:51.6133314Z  len(split_tag) == 3 2025-01-24T00:44:51.6133896Z  and split_tag[0] == "ciflow" 2025-01-24T00:44:51.6134497Z  and split_tag[2].isnumeric() 2025-01-24T00:44:51.6135059Z  ): 2025-01-24T00:44:51.6135506Z  pr_number = split_tag[2] 2025-01-24T00:44:51.6136154Z  try: 2025-01-24T00:44:51.6136724Z  repository = gh.get_repo(repo) 2025-01-24T00:44:51.6137440Z  pull = repository.get_pull(number=int(pr_number)) 2025-01-24T00:44:51.6138152Z  except Exception as e: 2025-01-24T00:44:51.6138787Z  raise Exception( # noqa: TRY002 2025-01-24T00:44:51.6139594Z  f"issue with pull request {pr_number} from repo {repository}" 2025-01-24T00:44:51.6140338Z  ) from e 2025-01-24T00:44:51.6140866Z  return pull.user.login 2025-01-24T00:44:51.6141553Z  # In all other cases, return the original input username 2025-01-24T00:44:51.6142224Z  return username 2025-01-24T00:44:51.6142675Z  2025-01-24T00:44:51.6143024Z  2025-01-24T00:44:51.6143474Z def is_exception_branch(branch: str) -> bool: 2025-01-24T00:44:51.6144072Z  """ 2025-01-24T00:44:51.6144834Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-01-24T00:44:51.6145821Z  """ 2025-01-24T00:44:51.6146461Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-01-24T00:44:51.6147354Z  2025-01-24T00:44:51.6147714Z  2025-01-24T00:44:51.6148116Z def load_yaml(yaml_text: str) -> Any: 2025-01-24T00:44:51.6148677Z  try: 2025-01-24T00:44:51.6149113Z  data = yaml.safe_load(yaml_text) 2025-01-24T00:44:51.6149667Z  return data 2025-01-24T00:44:51.6150149Z  except yaml.YAMLError: 2025-01-24T00:44:51.6150710Z  log.exception("Error loading YAML") 2025-01-24T00:44:51.6151284Z  raise 2025-01-24T00:44:51.6151701Z  2025-01-24T00:44:51.6152049Z  2025-01-24T00:44:51.6152716Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> Tuple[str, str]: 2025-01-24T00:44:51.6153542Z  """ 2025-01-24T00:44:51.6154243Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-01-24T00:44:51.6155092Z  2025-01-24T00:44:51.6155783Z  If the issue body contains "---" then the text above that is the settings 2025-01-24T00:44:51.6156644Z  and the text below is the list of opted in users. 2025-01-24T00:44:51.6157253Z  2025-01-24T00:44:51.6157875Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-01-24T00:44:51.6158659Z  """ 2025-01-24T00:44:51.6159185Z  rollout_state_parts = rollout_state.split("---") 2025-01-24T00:44:51.6159844Z  if len(rollout_state_parts) >= 2: 2025-01-24T00:44:51.6160526Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-01-24T00:44:51.6161176Z  else: 2025-01-24T00:44:51.6161716Z  return "", rollout_state 2025-01-24T00:44:51.6162238Z  2025-01-24T00:44:51.6162580Z  2025-01-24T00:44:51.6162995Z class UserOptins(Dict[str, List[str]]): 2025-01-24T00:44:51.6163554Z  """ 2025-01-24T00:44:51.6164135Z  Dictionary of users with a list of features they have opted into 2025-01-24T00:44:51.6164836Z  """ 2025-01-24T00:44:51.6165224Z  2025-01-24T00:44:51.6165665Z  2025-01-24T00:44:51.6166238Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-01-24T00:44:51.6166969Z  """ 2025-01-24T00:44:51.6167778Z  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:44:51.6168710Z  2025-01-24T00:44:51.6169609Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-01-24T00:44:51.6170736Z  - Example line: "@User1,lf,split_build" 2025-01-24T00:44:51.6171499Z  - A "#" prefix indicates the user is opted out of all experiments 2025-01-24T00:44:51.6172203Z  2025-01-24T00:44:51.6172558Z  2025-01-24T00:44:51.6172899Z  """ 2025-01-24T00:44:51.6173300Z  optins = UserOptins() 2025-01-24T00:44:51.6173863Z  for user in user_optin_text.split("\n"): 2025-01-24T00:44:51.6174477Z  user = user.strip("\r\n\t -") 2025-01-24T00:44:51.6175093Z  if not user or not user.startswith("@"): 2025-01-24T00:44:51.6175811Z  # Not a valid user. Skip 2025-01-24T00:44:51.6176348Z  continue 2025-01-24T00:44:51.6176821Z  2025-01-24T00:44:51.6177174Z  if user: 2025-01-24T00:44:51.6177673Z  usr_name = user.split(",")[0].strip("@") 2025-01-24T00:44:51.6178509Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-01-24T00:44:51.6179216Z  2025-01-24T00:44:51.6179575Z  return optins 2025-01-24T00:44:51.6180012Z  2025-01-24T00:44:51.6180482Z  2025-01-24T00:44:51.6181002Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-01-24T00:44:51.6181674Z  """ 2025-01-24T00:44:51.6182122Z  Check if the experiment name is valid. 2025-01-24T00:44:51.6182682Z  A valid name: 2025-01-24T00:44:51.6183417Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-01-24T00:44:51.6184458Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-01-24T00:44:51.6185251Z  - Cannot contain spaces 2025-01-24T00:44:51.6185860Z  """ 2025-01-24T00:44:51.6186238Z  2025-01-24T00:44:51.6186721Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-01-24T00:44:51.6187510Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-01-24T00:44:51.6188157Z  2025-01-24T00:44:51.6188509Z  if valid: 2025-01-24T00:44:51.6188941Z  return True 2025-01-24T00:44:51.6189387Z  2025-01-24T00:44:51.6189739Z  log.error( 2025-01-24T00:44:51.6191379Z  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:44:51.6193080Z  ) 2025-01-24T00:44:51.6193462Z  return False 2025-01-24T00:44:51.6193881Z  2025-01-24T00:44:51.6194226Z  2025-01-24T00:44:51.6194758Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-01-24T00:44:51.6195457Z  """ 2025-01-24T00:44:51.6196329Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-01-24T00:44:51.6197112Z  """ 2025-01-24T00:44:51.6197484Z  try: 2025-01-24T00:44:51.6197883Z  if settings_text: 2025-01-24T00:44:51.6198718Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-01-24T00:44:51.6199585Z  # for easy reading 2025-01-24T00:44:51.6200478Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-01-24T00:44:51.6201462Z  # the backtick character in shell commands. 2025-01-24T00:44:51.6202138Z  backtick = chr(96) # backtick character 2025-01-24T00:44:51.6202880Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-01-24T00:44:51.6203617Z  settings = load_yaml(settings_text) 2025-01-24T00:44:51.6204168Z  2025-01-24T00:44:51.6204813Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-01-24T00:44:51.6205727Z  experiments = {} 2025-01-24T00:44:51.6206226Z  2025-01-24T00:44:51.6206842Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-01-24T00:44:51.6207689Z  if not is_valid_experiment_name(exp_name): 2025-01-24T00:44:51.6208913Z  # 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:44:51.6210054Z  continue 2025-01-24T00:44:51.6210539Z  2025-01-24T00:44:51.6210927Z  valid_settings = {} 2025-01-24T00:44:51.6211503Z  for setting in exp_settings: 2025-01-24T00:44:51.6212140Z  if setting not in Experiment._fields: 2025-01-24T00:44:51.6212763Z  log.warning( 2025-01-24T00:44:51.6213557Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-01-24T00:44:51.6214460Z  ) 2025-01-24T00:44:51.6214941Z  else: 2025-01-24T00:44:51.6215644Z  valid_settings[setting] = exp_settings[setting] 2025-01-24T00:44:51.6216293Z  2025-01-24T00:44:51.6216805Z  experiments[exp_name] = Experiment(**valid_settings) 2025-01-24T00:44:51.6217517Z  return Settings(experiments) 2025-01-24T00:44:51.6218052Z  2025-01-24T00:44:51.6218422Z  except Exception: 2025-01-24T00:44:51.6218970Z  log.exception("Failed to parse settings") 2025-01-24T00:44:51.6219544Z  2025-01-24T00:44:51.6219913Z  return Settings() 2025-01-24T00:44:51.6220375Z  2025-01-24T00:44:51.6220715Z  2025-01-24T00:44:51.6221184Z def parse_settings(rollout_state: str) -> Settings: 2025-01-24T00:44:51.6221797Z  """ 2025-01-24T00:44:51.6222280Z  Parse settings, if any, from the rollout state. 2025-01-24T00:44:51.6222872Z  2025-01-24T00:44:51.6223450Z  If the issue body contains "---" then the text above that is the settings 2025-01-24T00:44:51.6224283Z  and the text below is the list of opted in users. 2025-01-24T00:44:51.6224884Z  2025-01-24T00:44:51.6225628Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-01-24T00:44:51.6226422Z  """ 2025-01-24T00:44:51.6227040Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:44:51.6227878Z  return parse_settings_from_text(settings_text) 2025-01-24T00:44:51.6228463Z  2025-01-24T00:44:51.6229028Z  2025-01-24T00:44:51.6229521Z def parse_users(rollout_state: str) -> UserOptins: 2025-01-24T00:44:51.6230124Z  """ 2025-01-24T00:44:51.6230559Z  Parse users from the rollout state. 2025-01-24T00:44:51.6231097Z  2025-01-24T00:44:51.6231440Z  """ 2025-01-24T00:44:51.6232026Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:44:51.6232846Z  return parse_user_opt_in_from_text(users_text) 2025-01-24T00:44:51.6233423Z  2025-01-24T00:44:51.6233782Z  2025-01-24T00:44:51.6234457Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:44:51.6235279Z  """ 2025-01-24T00:44:51.6235857Z  Check if a user is opted into an experiment 2025-01-24T00:44:51.6236443Z  """ 2025-01-24T00:44:51.6236965Z  return experiment_name in user_optins.get(user, []) 2025-01-24T00:44:51.6237589Z  2025-01-24T00:44:51.6237936Z  2025-01-24T00:44:51.6238611Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:44:51.6239445Z  """ 2025-01-24T00:44:51.6239967Z  Check if a user explicitly opted out of an experiment 2025-01-24T00:44:51.6240587Z  """ 2025-01-24T00:44:51.6241156Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-01-24T00:44:51.6241922Z  experiment_optout = "-" + experiment_name 2025-01-24T00:44:51.6242636Z  if experiment_optout not in user_optins.get(user, []): 2025-01-24T00:44:51.6243277Z  return False 2025-01-24T00:44:51.6243720Z  2025-01-24T00:44:51.6244218Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-01-24T00:44:51.6244865Z  log.warning( 2025-01-24T00:44:51.6245911Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-01-24T00:44:51.6246890Z  ) 2025-01-24T00:44:51.6247272Z  2025-01-24T00:44:51.6247767Z  return True 2025-01-24T00:44:51.6248191Z  2025-01-24T00:44:51.6248541Z  2025-01-24T00:44:51.6248918Z def get_runner_prefix( 2025-01-24T00:44:51.6249413Z  rollout_state: str, 2025-01-24T00:44:51.6249931Z  workflow_requestors: Iterable[str], 2025-01-24T00:44:51.6250475Z  branch: str, 2025-01-24T00:44:51.6251033Z  eligible_experiments: FrozenSet[str] = frozenset(), 2025-01-24T00:44:51.6251671Z  is_canary: bool = False, 2025-01-24T00:44:51.6252166Z ) -> str: 2025-01-24T00:44:51.6252636Z  settings = parse_settings(rollout_state) 2025-01-24T00:44:51.6253263Z  user_optins = parse_users(rollout_state) 2025-01-24T00:44:51.6253825Z  2025-01-24T00:44:51.6254200Z  fleet_prefix = "" 2025-01-24T00:44:51.6254667Z  prefixes = [] 2025-01-24T00:44:51.6255391Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-01-24T00:44:51.6256533Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-01-24T00:44:51.6257293Z  log.info( 2025-01-24T00:44:51.6258069Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-01-24T00:44:51.6258897Z  ) 2025-01-24T00:44:51.6259351Z  continue 2025-01-24T00:44:51.6259803Z  2025-01-24T00:44:51.6260190Z  if eligible_experiments: 2025-01-24T00:44:51.6260820Z  if experiment_name not in eligible_experiments: 2025-01-24T00:44:51.6261525Z  exp_list = ", ".join(eligible_experiments) 2025-01-24T00:44:51.6262246Z  log.info( 2025-01-24T00:44:51.6263134Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-01-24T00:44:51.6264048Z  ) 2025-01-24T00:44:51.6264491Z  continue 2025-01-24T00:44:51.6265033Z  elif not experiment_settings.default: 2025-01-24T00:44:51.6265705Z  log.info( 2025-01-24T00:44:51.6266459Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-01-24T00:44:51.6267251Z  ) 2025-01-24T00:44:51.6267685Z  continue 2025-01-24T00:44:51.6268119Z  2025-01-24T00:44:51.6268621Z  # Is any workflow_requestor opted out to this experiment? 2025-01-24T00:44:51.6269286Z  opted_out_users = [ 2025-01-24T00:44:51.6269801Z  requestor 2025-01-24T00:44:51.6270328Z  for requestor in workflow_requestors 2025-01-24T00:44:51.6271067Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-01-24T00:44:51.6271755Z  ] 2025-01-24T00:44:51.6272136Z  2025-01-24T00:44:51.6272500Z  if opted_out_users: 2025-01-24T00:44:51.6272999Z  log.info( 2025-01-24T00:44:51.6273708Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-01-24T00:44:51.6274465Z  ) 2025-01-24T00:44:51.6274884Z  continue 2025-01-24T00:44:51.6275324Z  2025-01-24T00:44:51.6275897Z  # Is any workflow_requestor opted in to this experiment? 2025-01-24T00:44:51.6276562Z  opted_in_users = [ 2025-01-24T00:44:51.6277063Z  requestor 2025-01-24T00:44:51.6277574Z  for requestor in workflow_requestors 2025-01-24T00:44:51.6278313Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-01-24T00:44:51.6278982Z  ] 2025-01-24T00:44:51.6279366Z  2025-01-24T00:44:51.6279869Z  enabled = False 2025-01-24T00:44:51.6280358Z  if opted_in_users: 2025-01-24T00:44:51.6280847Z  log.info( 2025-01-24T00:44:51.6281553Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-01-24T00:44:51.6282295Z  ) 2025-01-24T00:44:51.6282714Z  enabled = True 2025-01-24T00:44:51.6283189Z  2025-01-24T00:44:51.6283616Z  elif experiment_settings.rollout_perc: 2025-01-24T00:44:51.6284525Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-01-24T00:44:51.6285670Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-01-24T00:44:51.6286374Z  log.info( 2025-01-24T00:44:51.6287335Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-01-24T00:44:51.6288339Z  ) 2025-01-24T00:44:51.6288796Z  enabled = True 2025-01-24T00:44:51.6289295Z  2025-01-24T00:44:51.6289655Z  if enabled: 2025-01-24T00:44:51.6290144Z  label = experiment_name 2025-01-24T00:44:51.6290760Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-01-24T00:44:51.6291654Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-01-24T00:44:51.6292674Z  # - If it's enabled, then we always list it's prefix first 2025-01-24T00:44:51.6293654Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-01-24T00:44:51.6294381Z  if is_canary: 2025-01-24T00:44:51.6294929Z  label += CANARY_FLEET_SUFFIX 2025-01-24T00:44:51.6295621Z  fleet_prefix = label 2025-01-24T00:44:51.6296170Z  else: 2025-01-24T00:44:51.6296652Z  prefixes.append(label) 2025-01-24T00:44:51.6297195Z  2025-01-24T00:44:51.6297570Z  if len(prefixes) > 1: 2025-01-24T00:44:51.6298059Z  log.error( 2025-01-24T00:44:51.6299214Z  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:44:51.6300416Z  ) 2025-01-24T00:44:51.6300835Z  prefixes = prefixes[:1] 2025-01-24T00:44:51.6301344Z  2025-01-24T00:44:51.6301733Z  # Fleet always comes first 2025-01-24T00:44:51.6302256Z  if fleet_prefix: 2025-01-24T00:44:51.6302765Z  prefixes.insert(0, fleet_prefix) 2025-01-24T00:44:51.6303306Z  2025-01-24T00:44:51.6303771Z  return ".".join(prefixes) + "." if prefixes else "" 2025-01-24T00:44:51.6304387Z  2025-01-24T00:44:51.6304728Z  2025-01-24T00:44:51.6305394Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-01-24T00:44:51.6306344Z  """ 2025-01-24T00:44:51.6306980Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-01-24T00:44:51.6307727Z  2025-01-24T00:44:51.6308359Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-01-24T00:44:51.6309159Z  """ 2025-01-24T00:44:51.6309630Z  gh = get_gh_client(github_token) 2025-01-24T00:44:51.6310249Z  issue = get_issue(gh, repo, issue_num) 2025-01-24T00:44:51.6310950Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-01-24T00:44:51.6311584Z  2025-01-24T00:44:51.6311929Z  2025-01-24T00:44:51.6312562Z def download_json(url: str, headers: Dict[str, str], num_retries: int = 3) -> Any: 2025-01-24T00:44:51.6313511Z  for _ in range(num_retries): 2025-01-24T00:44:51.6314025Z  try: 2025-01-24T00:44:51.6314490Z  req = Request(url=url, headers=headers) 2025-01-24T00:44:51.6315207Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-01-24T00:44:51.6316001Z  return json.loads(content) 2025-01-24T00:44:51.6316598Z  except Exception as e: 2025-01-24T00:44:51.6317207Z  log.warning(f"Could not download {url}: {e}") 2025-01-24T00:44:51.6317794Z  2025-01-24T00:44:51.6318403Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-01-24T00:44:51.6319173Z  return {} 2025-01-24T00:44:51.6319580Z  2025-01-24T00:44:51.6319957Z  2025-01-24T00:44:51.6320336Z @lru_cache(maxsize=None) 2025-01-24T00:44:51.6321121Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> Dict[str, Any]: 2025-01-24T00:44:51.6321938Z  """ 2025-01-24T00:44:51.6322365Z  Dynamically get PR information 2025-01-24T00:44:51.6322886Z  """ 2025-01-24T00:44:51.6323432Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-01-24T00:44:51.6324105Z  headers = { 2025-01-24T00:44:51.6324616Z  "Accept": "application/vnd.github.v3+json", 2025-01-24T00:44:51.6325273Z  "Authorization": f"token {github_token}", 2025-01-24T00:44:51.6325937Z  } 2025-01-24T00:44:51.6326403Z  json_response: Dict[str, Any] = download_json( 2025-01-24T00:44:51.6327185Z  url=f"{github_api}/issues/{pr_number}", 2025-01-24T00:44:51.6327767Z  headers=headers, 2025-01-24T00:44:51.6328228Z  ) 2025-01-24T00:44:51.6328595Z  2025-01-24T00:44:51.6328975Z  if not json_response: 2025-01-24T00:44:51.6329606Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-01-24T00:44:51.6330276Z  return {} 2025-01-24T00:44:51.6330713Z  2025-01-24T00:44:51.6331077Z  return json_response 2025-01-24T00:44:51.6331553Z  2025-01-24T00:44:51.6331900Z  2025-01-24T00:44:51.6332516Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> Set[str]: 2025-01-24T00:44:51.6333290Z  """ 2025-01-24T00:44:51.6333871Z  Dynamically get the latest list of labels from the pull request 2025-01-24T00:44:51.6334564Z  """ 2025-01-24T00:44:51.6335097Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-01-24T00:44:51.6335899Z  return { 2025-01-24T00:44:51.6336536Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-01-24T00:44:51.6337273Z  } 2025-01-24T00:44:51.6337642Z  2025-01-24T00:44:51.6337980Z  2025-01-24T00:44:51.6338342Z def main() -> None: 2025-01-24T00:44:51.6338810Z  args = parse_args() 2025-01-24T00:44:51.6339268Z  2025-01-24T00:44:51.6339699Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-01-24T00:44:51.6340262Z  2025-01-24T00:44:51.6340639Z  # Check if the PR is opt-out 2025-01-24T00:44:51.6341163Z  if args.pr_number: 2025-01-24T00:44:51.6341893Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-01-24T00:44:51.6342682Z  if OPT_OUT_LABEL in labels: 2025-01-24T00:44:51.6343210Z  log.info( 2025-01-24T00:44:51.6343998Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-01-24T00:44:51.6344806Z  ) 2025-01-24T00:44:51.6346036Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:44:51.6346865Z  sys.exit() 2025-01-24T00:44:51.6347438Z  2025-01-24T00:44:51.6347799Z  try: 2025-01-24T00:44:51.6348289Z  rollout_state = get_rollout_state_from_issue( 2025-01-24T00:44:51.6416695Z  args.github_token, args.github_issue_repo, args.github_issue 2025-01-24T00:44:51.6418030Z  ) 2025-01-24T00:44:51.6418753Z  2025-01-24T00:44:51.6419516Z  username = get_potential_pr_author( 2025-01-24T00:44:51.6420493Z  args.github_token, 2025-01-24T00:44:51.6421343Z  args.github_repo, 2025-01-24T00:44:51.6422201Z  args.github_actor, 2025-01-24T00:44:51.6423110Z  args.github_ref_type, 2025-01-24T00:44:51.6423750Z  args.github_branch, 2025-01-24T00:44:51.6424268Z  ) 2025-01-24T00:44:51.6424660Z  2025-01-24T00:44:51.6425165Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-01-24T00:44:51.6426031Z  2025-01-24T00:44:51.6426481Z  runner_label_prefix = get_runner_prefix( 2025-01-24T00:44:51.6427075Z  rollout_state, 2025-01-24T00:44:51.6427628Z  (args.github_issue_owner, username), 2025-01-24T00:44:51.6428214Z  args.github_branch, 2025-01-24T00:44:51.6428774Z  args.eligible_experiments, 2025-01-24T00:44:51.6429325Z  is_canary, 2025-01-24T00:44:51.6429781Z  ) 2025-01-24T00:44:51.6430165Z  2025-01-24T00:44:51.6430538Z  except Exception as e: 2025-01-24T00:44:51.6431230Z  log.error( 2025-01-24T00:44:51.6431992Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-01-24T00:44:51.6432804Z  ) 2025-01-24T00:44:51.6433193Z  2025-01-24T00:44:51.6433752Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:44:51.6434451Z  2025-01-24T00:44:51.6434806Z  2025-01-24T00:44:51.6435175Z if __name__ == "__main__": 2025-01-24T00:44:51.6435765Z  main() 2025-01-24T00:44:51.6436166Z  2025-01-24T00:44:51.6436513Z EOF 2025-01-24T00:44:51.6436880Z  2025-01-24T00:44:51.6437262Z cat runner_determinator.py 2025-01-24T00:44:51.6761432Z shell: /usr/bin/bash -e {0} 2025-01-24T00:44:51.6762211Z env: 2025-01-24T00:44:51.6762826Z GITHUB_TOKEN: *** 2025-01-24T00:44:51.6763244Z ISSUE_NUMBER: 5132 2025-01-24T00:44:51.6763694Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:44:51.6764211Z ISSUE_OWNER: 2025-01-24T00:44:51.6764604Z CHECK_EXPERIMENTS: 2025-01-24T00:44:51.6765021Z PR_NUMBER: 2025-01-24T00:44:51.6765400Z ##[endgroup] 2025-01-24T00:44:51.6958696Z # flake8: noqa: G004 2025-01-24T00:44:51.6959045Z 2025-01-24T00:44:51.6959496Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-01-24T00:44:51.6960498Z # must be kept in sync. You can do it easily by running the following command: 2025-01-24T00:44:51.6961326Z # python .github/scripts/update_runner_determinator.py 2025-01-24T00:44:51.6961791Z 2025-01-24T00:44:51.6961944Z """ 2025-01-24T00:44:51.6962511Z This runner determinator is used to determine which set of runners to run a 2025-01-24T00:44:51.6963400Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-01-24T00:44:51.6964315Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-01-24T00:44:51.6965156Z of which runners should be used to run which job. 2025-01-24T00:44:51.6965756Z 2025-01-24T00:44:51.6966157Z The configuration has two parts, the settings and a list of opted-in users, 2025-01-24T00:44:51.6967281Z separated by a line containing "---". If the line is not present, the 2025-01-24T00:44:51.6968156Z settings are considered to be empty with only the second part, the user 2025-01-24T00:44:51.6968845Z list, defined. 2025-01-24T00:44:51.6969061Z 2025-01-24T00:44:51.6969431Z The first part is a YAML block that defines the rollout settings. This can be 2025-01-24T00:44:51.6970375Z used to define any settings that are needed to determine which runners to use. 2025-01-24T00:44:51.6971198Z It's fields are defined by the RolloutSettings class below. 2025-01-24T00:44:51.6971643Z 2025-01-24T00:44:51.6972014Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-01-24T00:44:51.6972898Z The user list is also a comma separated list of additional features or 2025-01-24T00:44:51.6973635Z experiments which the user could be opted in to. 2025-01-24T00:44:51.6974040Z 2025-01-24T00:44:51.6974229Z The user list has the following rules: 2025-01-24T00:44:51.6974577Z 2025-01-24T00:44:51.6974881Z - Users are GitHub usernames, which must start with the @ prefix 2025-01-24T00:44:51.6976356Z - Each user is also a comma-separated list of features/experiments to enable 2025-01-24T00:44:51.6977153Z - A "#" prefix opts the user out of all experiments 2025-01-24T00:44:51.6977562Z 2025-01-24T00:44:51.6977730Z Example config: 2025-01-24T00:44:51.6978173Z # A list of experiments that can be opted into. 2025-01-24T00:44:51.6978840Z # This defines the behavior they'll induce when opted into. 2025-01-24T00:44:51.6979460Z # Expected syntax is: 2025-01-24T00:44:51.6980102Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-01-24T00:44:51.6981103Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-01-24T00:44:51.6981876Z 2025-01-24T00:44:51.6982054Z experiments: 2025-01-24T00:44:51.6982440Z lf: 2025-01-24T00:44:51.6982814Z rollout_percent: 25 2025-01-24T00:44:51.6983260Z all_branches: false 2025-01-24T00:44:51.6983704Z default: true 2025-01-24T00:44:51.6984097Z --- 2025-01-24T00:44:51.6984287Z 2025-01-24T00:44:51.6984447Z # Opt-ins: 2025-01-24T00:44:51.6985026Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-01-24T00:44:51.6986122Z # and specifying experiments to enable in a comma-separated list. 2025-01-24T00:44:51.6986901Z # To always opt out of an experiment, prefix it with a "-". 2025-01-24T00:44:51.6987554Z # Experiments should be from the above list. 2025-01-24T00:44:51.6987934Z 2025-01-24T00:44:51.6988115Z @User1,-lf,split_build 2025-01-24T00:44:51.6988537Z @User2,lf 2025-01-24T00:44:51.6988911Z @User3,split_build 2025-01-24T00:44:51.6989307Z """ 2025-01-24T00:44:51.6989494Z 2025-01-24T00:44:51.6989665Z import json 2025-01-24T00:44:51.6990029Z import logging 2025-01-24T00:44:51.6990398Z import os 2025-01-24T00:44:51.6990752Z import random 2025-01-24T00:44:51.6991123Z import re 2025-01-24T00:44:51.6991473Z import sys 2025-01-24T00:44:51.6991919Z from argparse import ArgumentParser 2025-01-24T00:44:51.6992431Z from functools import lru_cache 2025-01-24T00:44:51.6992899Z from logging import LogRecord 2025-01-24T00:44:51.6993569Z from typing import Any, Dict, FrozenSet, Iterable, List, NamedTuple, Set, Tuple 2025-01-24T00:44:51.6994333Z from urllib.request import Request, urlopen 2025-01-24T00:44:51.6994697Z 2025-01-24T00:44:51.6994859Z import yaml 2025-01-24T00:44:51.6995233Z from github import Auth, Github 2025-01-24T00:44:51.6996088Z from github.Issue import Issue 2025-01-24T00:44:51.6996419Z 2025-01-24T00:44:51.6996429Z 2025-01-24T00:44:51.6996644Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-01-24T00:44:51.6997340Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-01-24T00:44:51.6998234Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-01-24T00:44:51.6998803Z 2025-01-24T00:44:51.6999027Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-01-24T00:44:51.6999766Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-01-24T00:44:51.7000265Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-01-24T00:44:51.7000802Z OPT_OUT_LABEL = "no-runner-experiments" 2025-01-24T00:44:51.7001154Z 2025-01-24T00:44:51.7001335Z SETTING_EXPERIMENTS = "experiments" 2025-01-24T00:44:51.7001668Z 2025-01-24T00:44:51.7001849Z LF_FLEET_EXPERIMENT = "lf" 2025-01-24T00:44:51.7002296Z CANARY_FLEET_SUFFIX = ".c" 2025-01-24T00:44:51.7002566Z 2025-01-24T00:44:51.7002574Z 2025-01-24T00:44:51.7002758Z class Experiment(NamedTuple): 2025-01-24T00:44:51.7003222Z rollout_perc: float = ( 2025-01-24T00:44:51.7003850Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-01-24T00:44:51.7004520Z ) 2025-01-24T00:44:51.7004875Z all_branches: bool = ( 2025-01-24T00:44:51.7005495Z False # If True, the experiment is also enabled on the exception branches 2025-01-24T00:44:51.7006401Z ) 2025-01-24T00:44:51.7006755Z default: bool = ( 2025-01-24T00:44:51.7007330Z True # If True, the experiment is enabled by default for all queries 2025-01-24T00:44:51.7007962Z ) 2025-01-24T00:44:51.7008160Z 2025-01-24T00:44:51.7008331Z # Add more fields as needed 2025-01-24T00:44:51.7008621Z 2025-01-24T00:44:51.7008627Z 2025-01-24T00:44:51.7008808Z class Settings(NamedTuple): 2025-01-24T00:44:51.7009231Z """ 2025-01-24T00:44:51.7009667Z Settings for the experiments that can be opted into. 2025-01-24T00:44:51.7010253Z """ 2025-01-24T00:44:51.7010443Z 2025-01-24T00:44:51.7010641Z experiments: Dict[str, Experiment] = {} 2025-01-24T00:44:51.7011004Z 2025-01-24T00:44:51.7011010Z 2025-01-24T00:44:51.7011207Z class ColorFormatter(logging.Formatter): 2025-01-24T00:44:51.7011820Z """Color codes the log messages based on the log level""" 2025-01-24T00:44:51.7012389Z 2025-01-24T00:44:51.7012551Z COLORS = { 2025-01-24T00:44:51.7012945Z "WARNING": "\033[33m", # Yellow 2025-01-24T00:44:51.7013459Z "ERROR": "\033[31m", # Red 2025-01-24T00:44:51.7013945Z "CRITICAL": "\033[31m", # Red 2025-01-24T00:44:51.7014440Z "INFO": "\033[0m", # Reset 2025-01-24T00:44:51.7014916Z "DEBUG": "\033[0m", # Reset 2025-01-24T00:44:51.7015371Z } 2025-01-24T00:44:51.7015734Z 2025-01-24T00:44:51.7015955Z def format(self, record: LogRecord) -> str: 2025-01-24T00:44:51.7016714Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-01-24T00:44:51.7017495Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-01-24T00:44:51.7018068Z return super().format(record) 2025-01-24T00:44:51.7018410Z 2025-01-24T00:44:51.7018416Z 2025-01-24T00:44:51.7018604Z handler = logging.StreamHandler() 2025-01-24T00:44:51.7019312Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-01-24T00:44:51.7019863Z 2025-01-24T00:44:51.7020095Z log = logging.getLogger(os.path.basename(__file__)) 2025-01-24T00:44:51.7020666Z log.addHandler(handler) 2025-01-24T00:44:51.7021100Z log.setLevel(logging.INFO) 2025-01-24T00:44:51.7021375Z 2025-01-24T00:44:51.7021382Z 2025-01-24T00:44:51.7021619Z def set_github_output(key: str, value: str) -> None: 2025-01-24T00:44:51.7022171Z """ 2025-01-24T00:44:51.7022658Z Defines outputs of the github action that invokes this script 2025-01-24T00:44:51.7023271Z """ 2025-01-24T00:44:51.7023624Z if not GITHUB_OUTPUT: 2025-01-24T00:44:51.7024679Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-01-24T00:44:51.7026028Z log.warning( 2025-01-24T00:44:51.7026924Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-01-24T00:44:51.7027891Z ) 2025-01-24T00:44:51.7037580Z print(f"::set-output name={key}::{value}") 2025-01-24T00:44:51.7038199Z return 2025-01-24T00:44:51.7038421Z 2025-01-24T00:44:51.7038616Z with open(GITHUB_OUTPUT, "a") as f: 2025-01-24T00:44:51.7039402Z log.info(f"Setting output: {key}='{value}'") 2025-01-24T00:44:51.7039965Z f.write(f"{key}={value}\n") 2025-01-24T00:44:51.7040290Z 2025-01-24T00:44:51.7040304Z 2025-01-24T00:44:51.7040598Z def _str_comma_separated_to_set(value: str) -> FrozenSet[str]: 2025-01-24T00:44:51.7041229Z return frozenset( 2025-01-24T00:44:51.7041827Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-01-24T00:44:51.7042523Z ) 2025-01-24T00:44:51.7042716Z 2025-01-24T00:44:51.7042723Z 2025-01-24T00:44:51.7042908Z def parse_args() -> Any: 2025-01-24T00:44:51.7043441Z parser = ArgumentParser("Get dynamic rollout settings") 2025-01-24T00:44:51.7044312Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-01-24T00:44:51.7045083Z parser.add_argument( 2025-01-24T00:44:51.7045518Z "--github-issue-repo", 2025-01-24T00:44:51.7046198Z type=str, 2025-01-24T00:44:51.7046599Z required=False, 2025-01-24T00:44:51.7047029Z default="pytorch/test-infra", 2025-01-24T00:44:51.7047558Z help="GitHub repo to get the issue", 2025-01-24T00:44:51.7048073Z ) 2025-01-24T00:44:51.7048430Z parser.add_argument( 2025-01-24T00:44:51.7048864Z "--github-repo", 2025-01-24T00:44:51.7049273Z type=str, 2025-01-24T00:44:51.7049661Z required=True, 2025-01-24T00:44:51.7050101Z help="GitHub repo where CI is running", 2025-01-24T00:44:51.7050624Z ) 2025-01-24T00:44:51.7050983Z parser.add_argument( 2025-01-24T00:44:51.7051580Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-01-24T00:44:51.7052233Z ) 2025-01-24T00:44:51.7052587Z parser.add_argument( 2025-01-24T00:44:51.7053331Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-01-24T00:44:51.7054015Z ) 2025-01-24T00:44:51.7054368Z parser.add_argument( 2025-01-24T00:44:51.7054989Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-01-24T00:44:51.7056007Z ) 2025-01-24T00:44:51.7056381Z parser.add_argument( 2025-01-24T00:44:51.7057047Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-01-24T00:44:51.7057771Z ) 2025-01-24T00:44:51.7058123Z parser.add_argument( 2025-01-24T00:44:51.7058559Z "--github-ref-type", 2025-01-24T00:44:51.7058994Z type=str, 2025-01-24T00:44:51.7059377Z required=True, 2025-01-24T00:44:51.7059851Z help="Current GitHub ref type, branch or tag", 2025-01-24T00:44:51.7060397Z ) 2025-01-24T00:44:51.7060752Z parser.add_argument( 2025-01-24T00:44:51.7061193Z "--eligible-experiments", 2025-01-24T00:44:51.7061690Z type=_str_comma_separated_to_set, 2025-01-24T00:44:51.7062199Z required=False, 2025-01-24T00:44:51.7062599Z default="", 2025-01-24T00:44:51.7063439Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-01-24T00:44:51.7064393Z ) 2025-01-24T00:44:51.7064743Z parser.add_argument( 2025-01-24T00:44:51.7065165Z "--pr-number", 2025-01-24T00:44:51.7065768Z type=str, 2025-01-24T00:44:51.7066163Z required=False, 2025-01-24T00:44:51.7066565Z default="", 2025-01-24T00:44:51.7067026Z help="the optional PR number where this is run", 2025-01-24T00:44:51.7067569Z ) 2025-01-24T00:44:51.7067760Z 2025-01-24T00:44:51.7067943Z return parser.parse_args() 2025-01-24T00:44:51.7068238Z 2025-01-24T00:44:51.7068246Z 2025-01-24T00:44:51.7068463Z def get_gh_client(github_token: str) -> Github: 2025-01-24T00:44:51.7069021Z auth = Auth.Token(github_token) 2025-01-24T00:44:51.7069513Z return Github(auth=auth) 2025-01-24T00:44:51.7069806Z 2025-01-24T00:44:51.7069814Z 2025-01-24T00:44:51.7070100Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: 2025-01-24T00:44:51.7070737Z repo = gh.get_repo(repo) 2025-01-24T00:44:51.7071364Z return repo.get_issue(number=issue_num) 2025-01-24T00:44:51.7071729Z 2025-01-24T00:44:51.7071736Z 2025-01-24T00:44:51.7071915Z def get_potential_pr_author( 2025-01-24T00:44:51.7072543Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-01-24T00:44:51.7073218Z ) -> str: 2025-01-24T00:44:51.7073717Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-01-24T00:44:51.7074516Z # Fetch the actual username from the original PR. The PR number is 2025-01-24T00:44:51.7075246Z # embedded in the tag name: ciflow// 2025-01-24T00:44:51.7075840Z 2025-01-24T00:44:51.7076024Z gh = get_gh_client(github_token) 2025-01-24T00:44:51.7076351Z 2025-01-24T00:44:51.7076622Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-01-24T00:44:51.7077256Z split_tag = ref_name.split("/") 2025-01-24T00:44:51.7077762Z if ( 2025-01-24T00:44:51.7078144Z len(split_tag) == 3 2025-01-24T00:44:51.7078609Z and split_tag[0] == "ciflow" 2025-01-24T00:44:51.7079128Z and split_tag[2].isnumeric() 2025-01-24T00:44:51.7079619Z ): 2025-01-24T00:44:51.7079983Z pr_number = split_tag[2] 2025-01-24T00:44:51.7080458Z try: 2025-01-24T00:44:51.7080874Z repository = gh.get_repo(repo) 2025-01-24T00:44:51.7081478Z pull = repository.get_pull(number=int(pr_number)) 2025-01-24T00:44:51.7082080Z except Exception as e: 2025-01-24T00:44:51.7082593Z raise Exception( # noqa: TRY002 2025-01-24T00:44:51.7083263Z f"issue with pull request {pr_number} from repo {repository}" 2025-01-24T00:44:51.7083918Z ) from e 2025-01-24T00:44:51.7084474Z return pull.user.login 2025-01-24T00:44:51.7085043Z # In all other cases, return the original input username 2025-01-24T00:44:51.7085823Z return username 2025-01-24T00:44:51.7086068Z 2025-01-24T00:44:51.7086081Z 2025-01-24T00:44:51.7086311Z def is_exception_branch(branch: str) -> bool: 2025-01-24T00:44:51.7086832Z """ 2025-01-24T00:44:51.7087474Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-01-24T00:44:51.7088245Z """ 2025-01-24T00:44:51.7088778Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-01-24T00:44:51.7089316Z 2025-01-24T00:44:51.7089323Z 2025-01-24T00:44:51.7089515Z def load_yaml(yaml_text: str) -> Any: 2025-01-24T00:44:51.7089993Z try: 2025-01-24T00:44:51.7090362Z data = yaml.safe_load(yaml_text) 2025-01-24T00:44:51.7090857Z return data 2025-01-24T00:44:51.7091303Z except yaml.YAMLError: 2025-01-24T00:44:51.7091814Z log.exception("Error loading YAML") 2025-01-24T00:44:51.7092342Z raise 2025-01-24T00:44:51.7092547Z 2025-01-24T00:44:51.7092553Z 2025-01-24T00:44:51.7092962Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> Tuple[str, str]: 2025-01-24T00:44:51.7093693Z """ 2025-01-24T00:44:51.7094300Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-01-24T00:44:51.7094906Z 2025-01-24T00:44:51.7095249Z If the issue body contains "---" then the text above that is the settings 2025-01-24T00:44:51.7096216Z and the text below is the list of opted in users. 2025-01-24T00:44:51.7096617Z 2025-01-24T00:44:51.7096985Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-01-24T00:44:51.7097686Z """ 2025-01-24T00:44:51.7098115Z rollout_state_parts = rollout_state.split("---") 2025-01-24T00:44:51.7098694Z if len(rollout_state_parts) >= 2: 2025-01-24T00:44:51.7099287Z return rollout_state_parts[0], rollout_state_parts[1] 2025-01-24T00:44:51.7099870Z else: 2025-01-24T00:44:51.7100233Z return "", rollout_state 2025-01-24T00:44:51.7100539Z 2025-01-24T00:44:51.7100546Z 2025-01-24T00:44:51.7100758Z class UserOptins(Dict[str, List[str]]): 2025-01-24T00:44:51.7101455Z """ 2025-01-24T00:44:51.7102005Z Dictionary of users with a list of features they have opted into 2025-01-24T00:44:51.7102679Z """ 2025-01-24T00:44:51.7102868Z 2025-01-24T00:44:51.7102876Z 2025-01-24T00:44:51.7103206Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-01-24T00:44:51.7103852Z """ 2025-01-24T00:44:51.7104552Z 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:44:51.7105250Z 2025-01-24T00:44:51.7106082Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-01-24T00:44:51.7107105Z - Example line: "@User1,lf,split_build" 2025-01-24T00:44:51.7107792Z - A "#" prefix indicates the user is opted out of all experiments 2025-01-24T00:44:51.7108273Z 2025-01-24T00:44:51.7108286Z 2025-01-24T00:44:51.7108439Z """ 2025-01-24T00:44:51.7108796Z optins = UserOptins() 2025-01-24T00:44:51.7109267Z for user in user_optin_text.split("\n"): 2025-01-24T00:44:51.7109814Z user = user.strip("\r\n\t -") 2025-01-24T00:44:51.7110333Z if not user or not user.startswith("@"): 2025-01-24T00:44:51.7110886Z # Not a valid user. Skip 2025-01-24T00:44:51.7111359Z continue 2025-01-24T00:44:51.7111588Z 2025-01-24T00:44:51.7111747Z if user: 2025-01-24T00:44:51.7112168Z usr_name = user.split(",")[0].strip("@") 2025-01-24T00:44:51.7112860Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-01-24T00:44:51.7113362Z 2025-01-24T00:44:51.7113533Z return optins 2025-01-24T00:44:51.7113760Z 2025-01-24T00:44:51.7113767Z 2025-01-24T00:44:51.7114040Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-01-24T00:44:51.7114767Z """ 2025-01-24T00:44:51.7115157Z Check if the experiment name is valid. 2025-01-24T00:44:51.7115777Z A valid name: 2025-01-24T00:44:51.7116402Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-01-24T00:44:51.7117342Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-01-24T00:44:51.7118054Z - Cannot contain spaces 2025-01-24T00:44:51.7118493Z """ 2025-01-24T00:44:51.7118680Z 2025-01-24T00:44:51.7118938Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-01-24T00:44:51.7119618Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-01-24T00:44:51.7120058Z 2025-01-24T00:44:51.7120217Z if valid: 2025-01-24T00:44:51.7120580Z return True 2025-01-24T00:44:51.7120806Z 2025-01-24T00:44:51.7120963Z log.error( 2025-01-24T00:44:51.7122442Z 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:44:51.7124034Z ) 2025-01-24T00:44:51.7124375Z return False 2025-01-24T00:44:51.7124602Z 2025-01-24T00:44:51.7124609Z 2025-01-24T00:44:51.7124902Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-01-24T00:44:51.7125513Z """ 2025-01-24T00:44:51.7126171Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-01-24T00:44:51.7126880Z """ 2025-01-24T00:44:51.7127222Z try: 2025-01-24T00:44:51.7127570Z if settings_text: 2025-01-24T00:44:51.7128292Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-01-24T00:44:51.7129100Z # for easy reading 2025-01-24T00:44:51.7129877Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-01-24T00:44:51.7130776Z # the backtick character in shell commands. 2025-01-24T00:44:51.7131371Z backtick = chr(96) # backtick character 2025-01-24T00:44:51.7132029Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-01-24T00:44:51.7132826Z settings = load_yaml(settings_text) 2025-01-24T00:44:51.7133193Z 2025-01-24T00:44:51.7133599Z # For now we just load experiments. We can expand this if/when we add more settings 2025-01-24T00:44:51.7134392Z experiments = {} 2025-01-24T00:44:51.7134670Z 2025-01-24T00:44:51.7135021Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-01-24T00:44:51.7135908Z if not is_valid_experiment_name(exp_name): 2025-01-24T00:44:51.7137039Z # 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:44:51.7138102Z continue 2025-01-24T00:44:51.7138387Z 2025-01-24T00:44:51.7138563Z valid_settings = {} 2025-01-24T00:44:51.7139066Z for setting in exp_settings: 2025-01-24T00:44:51.7139613Z if setting not in Experiment._fields: 2025-01-24T00:44:51.7140170Z log.warning( 2025-01-24T00:44:51.7140868Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-01-24T00:44:51.7142136Z ) 2025-01-24T00:44:51.7142874Z else: 2025-01-24T00:44:51.7143593Z valid_settings[setting] = exp_settings[setting] 2025-01-24T00:44:51.7144031Z 2025-01-24T00:44:51.7144305Z experiments[exp_name] = Experiment(**valid_settings) 2025-01-24T00:44:51.7144932Z return Settings(experiments) 2025-01-24T00:44:51.7145286Z 2025-01-24T00:44:51.7145455Z except Exception: 2025-01-24T00:44:51.7146059Z log.exception("Failed to parse settings") 2025-01-24T00:44:51.7146451Z 2025-01-24T00:44:51.7146790Z return Settings() 2025-01-24T00:44:51.7147069Z 2025-01-24T00:44:51.7147076Z 2025-01-24T00:44:51.7147314Z def parse_settings(rollout_state: str) -> Settings: 2025-01-24T00:44:51.7147888Z """ 2025-01-24T00:44:51.7148298Z Parse settings, if any, from the rollout state. 2025-01-24T00:44:51.7148700Z 2025-01-24T00:44:51.7149037Z If the issue body contains "---" then the text above that is the settings 2025-01-24T00:44:51.7149788Z and the text below is the list of opted in users. 2025-01-24T00:44:51.7150190Z 2025-01-24T00:44:51.7150587Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-01-24T00:44:51.7151320Z """ 2025-01-24T00:44:51.7151875Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:44:51.7152636Z return parse_settings_from_text(settings_text) 2025-01-24T00:44:51.7153038Z 2025-01-24T00:44:51.7153045Z 2025-01-24T00:44:51.7153284Z def parse_users(rollout_state: str) -> UserOptins: 2025-01-24T00:44:51.7153830Z """ 2025-01-24T00:44:51.7154203Z Parse users from the rollout state. 2025-01-24T00:44:51.7154548Z 2025-01-24T00:44:51.7154697Z """ 2025-01-24T00:44:51.7155204Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:44:51.7156030Z return parse_user_opt_in_from_text(users_text) 2025-01-24T00:44:51.7156420Z 2025-01-24T00:44:51.7156426Z 2025-01-24T00:44:51.7156831Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:44:51.7157563Z """ 2025-01-24T00:44:51.7157959Z Check if a user is opted into an experiment 2025-01-24T00:44:51.7158476Z """ 2025-01-24T00:44:51.7158899Z return experiment_name in user_optins.get(user, []) 2025-01-24T00:44:51.7159310Z 2025-01-24T00:44:51.7159316Z 2025-01-24T00:44:51.7159729Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:44:51.7160473Z """ 2025-01-24T00:44:51.7160914Z Check if a user explicitly opted out of an experiment 2025-01-24T00:44:51.7161478Z """ 2025-01-24T00:44:51.7161950Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-01-24T00:44:51.7162753Z experiment_optout = "-" + experiment_name 2025-01-24T00:44:51.7163369Z if experiment_optout not in user_optins.get(user, []): 2025-01-24T00:44:51.7163951Z return False 2025-01-24T00:44:51.7164197Z 2025-01-24T00:44:51.7164458Z if is_user_opted_in(user, user_optins, experiment_name): 2025-01-24T00:44:51.7165043Z log.warning( 2025-01-24T00:44:51.7165941Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-01-24T00:44:51.7166832Z ) 2025-01-24T00:44:51.7167022Z 2025-01-24T00:44:51.7167194Z return True 2025-01-24T00:44:51.7167412Z 2025-01-24T00:44:51.7167418Z 2025-01-24T00:44:51.7167586Z def get_runner_prefix( 2025-01-24T00:44:51.7168002Z rollout_state: str, 2025-01-24T00:44:51.7168446Z workflow_requestors: Iterable[str], 2025-01-24T00:44:51.7168943Z branch: str, 2025-01-24T00:44:51.7169403Z eligible_experiments: FrozenSet[str] = frozenset(), 2025-01-24T00:44:51.7169994Z is_canary: bool = False, 2025-01-24T00:44:51.7170424Z ) -> str: 2025-01-24T00:44:51.7170822Z settings = parse_settings(rollout_state) 2025-01-24T00:44:51.7171385Z user_optins = parse_users(rollout_state) 2025-01-24T00:44:51.7171745Z 2025-01-24T00:44:51.7171912Z fleet_prefix = "" 2025-01-24T00:44:51.7172317Z prefixes = [] 2025-01-24T00:44:51.7172920Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-01-24T00:44:51.7173833Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-01-24T00:44:51.7174541Z log.info( 2025-01-24T00:44:51.7175196Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-01-24T00:44:51.7176046Z ) 2025-01-24T00:44:51.7176532Z continue 2025-01-24T00:44:51.7176774Z 2025-01-24T00:44:51.7176973Z if eligible_experiments: 2025-01-24T00:44:51.7177500Z if experiment_name not in eligible_experiments: 2025-01-24T00:44:51.7178146Z exp_list = ", ".join(eligible_experiments) 2025-01-24T00:44:51.7178688Z log.info( 2025-01-24T00:44:51.7179454Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-01-24T00:44:51.7180302Z ) 2025-01-24T00:44:51.7180678Z continue 2025-01-24T00:44:51.7181121Z elif not experiment_settings.default: 2025-01-24T00:44:51.7181644Z log.info( 2025-01-24T00:44:51.7182290Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-01-24T00:44:51.7183028Z ) 2025-01-24T00:44:51.7183387Z continue 2025-01-24T00:44:51.7183619Z 2025-01-24T00:44:51.7183927Z # Is any workflow_requestor opted out to this experiment? 2025-01-24T00:44:51.7185017Z opted_out_users = [ 2025-01-24T00:44:51.7185913Z requestor 2025-01-24T00:44:51.7186448Z for requestor in workflow_requestors 2025-01-24T00:44:51.7187142Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-01-24T00:44:51.7187774Z ] 2025-01-24T00:44:51.7187966Z 2025-01-24T00:44:51.7188141Z if opted_out_users: 2025-01-24T00:44:51.7188566Z log.info( 2025-01-24T00:44:51.7189174Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-01-24T00:44:51.7189879Z ) 2025-01-24T00:44:51.7190237Z continue 2025-01-24T00:44:51.7190478Z 2025-01-24T00:44:51.7190743Z # Is any workflow_requestor opted in to this experiment? 2025-01-24T00:44:51.7191340Z opted_in_users = [ 2025-01-24T00:44:51.7191762Z requestor 2025-01-24T00:44:51.7192199Z for requestor in workflow_requestors 2025-01-24T00:44:51.7192853Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-01-24T00:44:51.7193459Z ] 2025-01-24T00:44:51.7193661Z 2025-01-24T00:44:51.7193990Z enabled = False 2025-01-24T00:44:51.7194410Z if opted_in_users: 2025-01-24T00:44:51.7194829Z log.info( 2025-01-24T00:44:51.7195418Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-01-24T00:44:51.7196454Z ) 2025-01-24T00:44:51.7196820Z enabled = True 2025-01-24T00:44:51.7197103Z 2025-01-24T00:44:51.7197310Z elif experiment_settings.rollout_perc: 2025-01-24T00:44:51.7198135Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-01-24T00:44:51.7199073Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-01-24T00:44:51.7199717Z log.info( 2025-01-24T00:44:51.7200623Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-01-24T00:44:51.7201559Z ) 2025-01-24T00:44:51.7201950Z enabled = True 2025-01-24T00:44:51.7202279Z 2025-01-24T00:44:51.7202441Z if enabled: 2025-01-24T00:44:51.7203002Z label = experiment_name 2025-01-24T00:44:51.7203841Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-01-24T00:44:51.7204739Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-01-24T00:44:51.7205804Z # - If it's enabled, then we always list it's prefix first 2025-01-24T00:44:51.7206566Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-01-24T00:44:51.7207240Z if is_canary: 2025-01-24T00:44:51.7207711Z label += CANARY_FLEET_SUFFIX 2025-01-24T00:44:51.7208244Z fleet_prefix = label 2025-01-24T00:44:51.7208868Z else: 2025-01-24T00:44:51.7209286Z prefixes.append(label) 2025-01-24T00:44:51.7209631Z 2025-01-24T00:44:51.7209803Z if len(prefixes) > 1: 2025-01-24T00:44:51.7210236Z log.error( 2025-01-24T00:44:51.7211265Z 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:44:51.7212390Z ) 2025-01-24T00:44:51.7212754Z prefixes = prefixes[:1] 2025-01-24T00:44:51.7213046Z 2025-01-24T00:44:51.7213227Z # Fleet always comes first 2025-01-24T00:44:51.7213671Z if fleet_prefix: 2025-01-24T00:44:51.7214095Z prefixes.insert(0, fleet_prefix) 2025-01-24T00:44:51.7214441Z 2025-01-24T00:44:51.7214679Z return ".".join(prefixes) + "." if prefixes else "" 2025-01-24T00:44:51.7215082Z 2025-01-24T00:44:51.7215089Z 2025-01-24T00:44:51.7215520Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-01-24T00:44:51.7216497Z """ 2025-01-24T00:44:51.7217056Z Gets the first comment of the issue, which contains the desired rollout state. 2025-01-24T00:44:51.7217605Z 2025-01-24T00:44:51.7217986Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-01-24T00:44:51.7218677Z """ 2025-01-24T00:44:51.7219045Z gh = get_gh_client(github_token) 2025-01-24T00:44:51.7219552Z issue = get_issue(gh, repo, issue_num) 2025-01-24T00:44:51.7220155Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-01-24T00:44:51.7220589Z 2025-01-24T00:44:51.7220596Z 2025-01-24T00:44:51.7220982Z def download_json(url: str, headers: Dict[str, str], num_retries: int = 3) -> Any: 2025-01-24T00:44:51.7221726Z for _ in range(num_retries): 2025-01-24T00:44:51.7222175Z try: 2025-01-24T00:44:51.7222573Z req = Request(url=url, headers=headers) 2025-01-24T00:44:51.7223214Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-01-24T00:44:51.7223841Z return json.loads(content) 2025-01-24T00:44:51.7224343Z except Exception as e: 2025-01-24T00:44:51.7224854Z log.warning(f"Could not download {url}: {e}") 2025-01-24T00:44:51.7225381Z 2025-01-24T00:44:51.7225858Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-01-24T00:44:51.7226563Z return {} 2025-01-24T00:44:51.7226774Z 2025-01-24T00:44:51.7226782Z 2025-01-24T00:44:51.7226961Z @lru_cache(maxsize=None) 2025-01-24T00:44:51.7227625Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> Dict[str, Any]: 2025-01-24T00:44:51.7228368Z """ 2025-01-24T00:44:51.7228740Z Dynamically get PR information 2025-01-24T00:44:51.7229200Z """ 2025-01-24T00:44:51.7229673Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-01-24T00:44:51.7230292Z headers = { 2025-01-24T00:44:51.7230717Z "Accept": "application/vnd.github.v3+json", 2025-01-24T00:44:51.7231307Z "Authorization": f"token {github_token}", 2025-01-24T00:44:51.7231823Z } 2025-01-24T00:44:51.7232222Z json_response: Dict[str, Any] = download_json( 2025-01-24T00:44:51.7232807Z url=f"{github_api}/issues/{pr_number}", 2025-01-24T00:44:51.7233348Z headers=headers, 2025-01-24T00:44:51.7233742Z ) 2025-01-24T00:44:51.7233966Z 2025-01-24T00:44:51.7234133Z if not json_response: 2025-01-24T00:44:51.7235032Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-01-24T00:44:51.7236159Z return {} 2025-01-24T00:44:51.7236402Z 2025-01-24T00:44:51.7236571Z return json_response 2025-01-24T00:44:51.7236837Z 2025-01-24T00:44:51.7236844Z 2025-01-24T00:44:51.7237291Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> Set[str]: 2025-01-24T00:44:51.7238059Z """ 2025-01-24T00:44:51.7238584Z Dynamically get the latest list of labels from the pull request 2025-01-24T00:44:51.7239255Z """ 2025-01-24T00:44:51.7239892Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-01-24T00:44:51.7240519Z return { 2025-01-24T00:44:51.7241078Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-01-24T00:44:51.7241772Z } 2025-01-24T00:44:51.7241962Z 2025-01-24T00:44:51.7241970Z 2025-01-24T00:44:51.7242135Z def main() -> None: 2025-01-24T00:44:51.7242535Z args = parse_args() 2025-01-24T00:44:51.7242787Z 2025-01-24T00:44:51.7242995Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-01-24T00:44:51.7243367Z 2025-01-24T00:44:51.7243546Z # Check if the PR is opt-out 2025-01-24T00:44:51.7244010Z if args.pr_number: 2025-01-24T00:44:51.7244627Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-01-24T00:44:51.7245370Z if OPT_OUT_LABEL in labels: 2025-01-24T00:44:51.7246076Z log.info( 2025-01-24T00:44:51.7246737Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-01-24T00:44:51.7247509Z ) 2025-01-24T00:44:51.7248040Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:44:51.7248689Z sys.exit() 2025-01-24T00:44:51.7248945Z 2025-01-24T00:44:51.7249106Z try: 2025-01-24T00:44:51.7249518Z rollout_state = get_rollout_state_from_issue( 2025-01-24T00:44:51.7250206Z args.github_token, args.github_issue_repo, args.github_issue 2025-01-24T00:44:51.7250833Z ) 2025-01-24T00:44:51.7251025Z 2025-01-24T00:44:51.7251224Z username = get_potential_pr_author( 2025-01-24T00:44:51.7251739Z args.github_token, 2025-01-24T00:44:51.7252193Z args.github_repo, 2025-01-24T00:44:51.7252641Z args.github_actor, 2025-01-24T00:44:51.7253089Z args.github_ref_type, 2025-01-24T00:44:51.7253570Z args.github_branch, 2025-01-24T00:44:51.7254008Z ) 2025-01-24T00:44:51.7254223Z 2025-01-24T00:44:51.7254529Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-01-24T00:44:51.7254972Z 2025-01-24T00:44:51.7255179Z runner_label_prefix = get_runner_prefix( 2025-01-24T00:44:51.7255939Z rollout_state, 2025-01-24T00:44:51.7256592Z (args.github_issue_owner, username), 2025-01-24T00:44:51.7257127Z args.github_branch, 2025-01-24T00:44:51.7257603Z args.eligible_experiments, 2025-01-24T00:44:51.7258095Z is_canary, 2025-01-24T00:44:51.7258483Z ) 2025-01-24T00:44:51.7258683Z 2025-01-24T00:44:51.7258856Z except Exception as e: 2025-01-24T00:44:51.7259285Z log.error( 2025-01-24T00:44:51.7259925Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-01-24T00:44:51.7260672Z ) 2025-01-24T00:44:51.7260865Z 2025-01-24T00:44:51.7261181Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:44:51.7261665Z 2025-01-24T00:44:51.7261671Z 2025-01-24T00:44:51.7261842Z if __name__ == "__main__": 2025-01-24T00:44:51.7262256Z main() 2025-01-24T00:44:51.7262456Z 2025-01-24T00:44:51.7356474Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-01-24T00:44:51.7357404Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-01-24T00:44:51.7385306Z shell: /usr/bin/bash -e {0} 2025-01-24T00:44:51.7385895Z env: 2025-01-24T00:44:51.7386462Z GITHUB_TOKEN: *** 2025-01-24T00:44:51.7386873Z ISSUE_NUMBER: 5132 2025-01-24T00:44:51.7387295Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:44:51.7387775Z ISSUE_OWNER: 2025-01-24T00:44:51.7388173Z CHECK_EXPERIMENTS: 2025-01-24T00:44:51.7388579Z PR_NUMBER: 2025-01-24T00:44:51.7388941Z ##[endgroup] 2025-01-24T00:44:53.3110871Z Defaulting to user installation because normal site-packages is not writeable 2025-01-24T00:44:54.5935349Z Collecting urllib3==1.26.18 2025-01-24T00:44:54.6264463Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-01-24T00:44:54.6486043Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 3.7 MB/s eta 0:00:00 2025-01-24T00:44:54.6682295Z Collecting PyGithub==2.3.0 2025-01-24T00:44:54.6721073Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-01-24T00:44:54.7144094Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-01-24T00:44:54.7172211Z 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:44:54.7218014Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-01-24T00:44:54.7234550Z 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:44:54.7249789Z 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:44:54.7495889Z Collecting Deprecated (from PyGithub==2.3.0) 2025-01-24T00:44:54.7525129Z Downloading Deprecated-1.2.15-py2.py3-none-any.whl.metadata (5.5 kB) 2025-01-24T00:44:54.7751739Z 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:44:54.8815187Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-01-24T00:44:54.8844375Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-01-24T00:44:54.9883073Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-01-24T00:44:54.9915239Z 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:44:55.0106487Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-01-24T00:44:55.0136916Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-01-24T00:44:55.0365980Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-01-24T00:44:55.0446494Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 22.1 MB/s eta 0:00:00 2025-01-24T00:44:55.0476091Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-01-24T00:44:55.0567489Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 47.6 MB/s eta 0:00:00 2025-01-24T00:44:55.0597181Z 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:44:55.0711304Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 86.3 MB/s eta 0:00:00 2025-01-24T00:44:55.0741024Z Downloading Deprecated-1.2.15-py2.py3-none-any.whl (9.9 kB) 2025-01-24T00:44:55.0801713Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-01-24T00:44:55.0877150Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 79.5 MB/s eta 0:00:00 2025-01-24T00:44:55.0906339Z 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:44:55.0956560Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 22.6 MB/s eta 0:00:00 2025-01-24T00:44:55.0986114Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-01-24T00:44:55.1052084Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 21.8 MB/s eta 0:00:00 2025-01-24T00:44:55.4298332Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-01-24T00:44:55.9610311Z 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:44:56.0318666Z ##[group]Run curr_branch="ciflow/inductor/145539" 2025-01-24T00:44:56.0319059Z curr_branch="ciflow/inductor/145539" 2025-01-24T00:44:56.0319322Z curr_ref_type="tag" 2025-01-24T00:44:56.0319570Z echo "Current branch is '$curr_branch'" 2025-01-24T00:44:56.0319825Z  2025-01-24T00:44:56.0320005Z python3 runner_determinator.py \ 2025-01-24T00:44:56.0320288Z  --github-token "$GITHUB_TOKEN" \ 2025-01-24T00:44:56.0320555Z  --github-issue "$ISSUE_NUMBER" \ 2025-01-24T00:44:56.0320819Z  --github-branch "$curr_branch" \ 2025-01-24T00:44:56.0321090Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-01-24T00:44:56.0321389Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-01-24T00:44:56.0321668Z  --github-ref-type "$curr_ref_type" \ 2025-01-24T00:44:56.0321936Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-01-24T00:44:56.0322265Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-01-24T00:44:56.0322554Z  --pr-number "${PR_NUMBER}" 2025-01-24T00:44:56.0350985Z shell: /usr/bin/bash -e {0} 2025-01-24T00:44:56.0351215Z env: 2025-01-24T00:44:56.0351751Z GITHUB_TOKEN: *** 2025-01-24T00:44:56.0351941Z ISSUE_NUMBER: 5132 2025-01-24T00:44:56.0352155Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:44:56.0352390Z ISSUE_OWNER: 2025-01-24T00:44:56.0352562Z CHECK_EXPERIMENTS: 2025-01-24T00:44:56.0352749Z PR_NUMBER: 2025-01-24T00:44:56.0352920Z ##[endgroup] 2025-01-24T00:44:56.0400258Z Current branch is 'ciflow/inductor/145539' 2025-01-24T00:44:58.8296754Z INFO : Skipping experiment 'awsa100', as it is not a default experiment 2025-01-24T00:44:58.8297566Z INFO : Setting output: label-type='' 2025-01-24T00:44:58.8610784Z Evaluate and set job outputs 2025-01-24T00:44:58.8617864Z Cleaning up orphan processes