2025-07-17T06:33:18.9772248Z Current runner version: '2.326.0' 2025-07-17T06:33:18.9797058Z ##[group]Runner Image Provisioner 2025-07-17T06:33:18.9797921Z Hosted Compute Agent 2025-07-17T06:33:18.9798538Z Version: 20250711.363 2025-07-17T06:33:18.9799093Z Commit: 6785254374ce925a23743850c1cb91912ce5c14c 2025-07-17T06:33:18.9799759Z Build Date: 2025-07-11T20:04:25Z 2025-07-17T06:33:18.9800353Z ##[endgroup] 2025-07-17T06:33:18.9800916Z ##[group]Operating System 2025-07-17T06:33:18.9801437Z Ubuntu 2025-07-17T06:33:18.9801937Z 24.04.2 2025-07-17T06:33:18.9802368Z LTS 2025-07-17T06:33:18.9802822Z ##[endgroup] 2025-07-17T06:33:18.9803253Z ##[group]Runner Image 2025-07-17T06:33:18.9803913Z Image: ubuntu-24.04 2025-07-17T06:33:18.9804394Z Version: 20250710.1.0 2025-07-17T06:33:18.9805330Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250710.1/images/ubuntu/Ubuntu2404-Readme.md 2025-07-17T06:33:18.9807103Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250710.1 2025-07-17T06:33:18.9808233Z ##[endgroup] 2025-07-17T06:33:18.9809176Z ##[group]GITHUB_TOKEN Permissions 2025-07-17T06:33:18.9811205Z Metadata: read 2025-07-17T06:33:18.9811820Z ##[endgroup] 2025-07-17T06:33:18.9814134Z Secret source: Actions 2025-07-17T06:33:18.9814990Z Prepare workflow directory 2025-07-17T06:33:19.0335364Z Prepare all required actions 2025-07-17T06:33:19.0393466Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (a38f433be2e94a64b095a44ba39879d02d0c2316) 2025-07-17T06:33:19.0398614Z ##[group] Inputs 2025-07-17T06:33:19.0399181Z check_experiments: 2025-07-17T06:33:19.0399802Z opt_out_experiments: 2025-07-17T06:33:19.0400397Z triggering_actor: pytorchmergebot 2025-07-17T06:33:19.0400999Z issue_owner: 2025-07-17T06:33:19.0401536Z curr_branch: main 2025-07-17T06:33:19.0402045Z curr_ref_type: branch 2025-07-17T06:33:19.0402558Z issue_number: 5132 2025-07-17T06:33:19.0403224Z ##[endgroup] 2025-07-17T06:33:19.0403810Z Complete job name: get-label-type / runner-determinator 2025-07-17T06:33:19.5732293Z ##[group]Run cat < runner_determinator.py 2025-07-17T06:33:19.5734716Z cat < runner_determinator.py 2025-07-17T06:33:19.5735408Z # flake8: noqa: G004 2025-07-17T06:33:19.5736021Z  2025-07-17T06:33:19.5736820Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-07-17T06:33:19.5738340Z # must be kept in sync. You can do it easily by running the following command: 2025-07-17T06:33:19.5739424Z # python .github/scripts/update_runner_determinator.py 2025-07-17T06:33:19.5740182Z  2025-07-17T06:33:19.5740615Z """ 2025-07-17T06:33:19.5741414Z This runner determinator is used to determine which set of runners to run a 2025-07-17T06:33:19.5742461Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-07-17T06:33:19.5743687Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-07-17T06:33:19.5744675Z of which runners should be used to run which job. 2025-07-17T06:33:19.5745382Z  2025-07-17T06:33:19.5746150Z The configuration has two parts, the settings and a list of opted-in users, 2025-07-17T06:33:19.5747565Z separated by a line containing "---". If the line is not present, the 2025-07-17T06:33:19.5748702Z settings are considered to be empty with only the second part, the user 2025-07-17T06:33:19.5749627Z list, defined. 2025-07-17T06:33:19.5750150Z  2025-07-17T06:33:19.5750854Z The first part is a YAML block that defines the rollout settings. This can be 2025-07-17T06:33:19.5751900Z used to define any settings that are needed to determine which runners to use. 2025-07-17T06:33:19.5752967Z It's fields are defined by the RolloutSettings class below. 2025-07-17T06:33:19.5753675Z  2025-07-17T06:33:19.5754407Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-07-17T06:33:19.5755778Z The user list is also a comma separated list of additional features or 2025-07-17T06:33:19.5756653Z experiments which the user could be opted in to. 2025-07-17T06:33:19.5757473Z  2025-07-17T06:33:19.5758047Z The user list has the following rules: 2025-07-17T06:33:19.5758688Z  2025-07-17T06:33:19.5759395Z - Users are GitHub usernames, which must start with the @ prefix 2025-07-17T06:33:19.5760443Z - Each user is also a comma-separated list of features/experiments to enable 2025-07-17T06:33:19.5761364Z - A "#" prefix opts the user out of all experiments 2025-07-17T06:33:19.5762001Z  2025-07-17T06:33:19.5762523Z Example config: 2025-07-17T06:33:19.5763114Z  # A list of experiments that can be opted into. 2025-07-17T06:33:19.5763969Z  # This defines the behavior they'll induce when opted into. 2025-07-17T06:33:19.5764801Z  # Expected syntax is: 2025-07-17T06:33:19.5765587Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-07-17T06:33:19.5766703Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-07-17T06:33:19.5767704Z  2025-07-17T06:33:19.5768203Z  experiments: 2025-07-17T06:33:19.5768778Z  lf: 2025-07-17T06:33:19.5769294Z  rollout_percent: 25 2025-07-17T06:33:19.5769929Z  all_branches: false 2025-07-17T06:33:19.5770493Z  default: true 2025-07-17T06:33:19.5771089Z  --- 2025-07-17T06:33:19.5771564Z  2025-07-17T06:33:19.5772066Z  # Opt-ins: 2025-07-17T06:33:19.5853863Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-07-17T06:33:19.5855233Z  # and specifying experiments to enable in a comma-separated list. 2025-07-17T06:33:19.5856145Z  # To always opt out of an experiment, prefix it with a "-". 2025-07-17T06:33:19.5857113Z  # Experiments should be from the above list. 2025-07-17T06:33:19.5857865Z  2025-07-17T06:33:19.5858295Z  @User1,-lf,split_build 2025-07-17T06:33:19.5858805Z  @User2,lf 2025-07-17T06:33:19.5859289Z  @User3,split_build 2025-07-17T06:33:19.5859762Z """ 2025-07-17T06:33:19.5860152Z  2025-07-17T06:33:19.5860533Z import json 2025-07-17T06:33:19.5860975Z import logging 2025-07-17T06:33:19.5861416Z import os 2025-07-17T06:33:19.5861834Z import random 2025-07-17T06:33:19.5862270Z import re 2025-07-17T06:33:19.5862674Z import sys 2025-07-17T06:33:19.5863145Z from argparse import ArgumentParser 2025-07-17T06:33:19.5863807Z from collections.abc import Iterable 2025-07-17T06:33:19.5864387Z from functools import cache 2025-07-17T06:33:19.5864925Z from logging import LogRecord 2025-07-17T06:33:19.5865487Z from typing import Any, NamedTuple 2025-07-17T06:33:19.5866093Z from urllib.request import Request, urlopen 2025-07-17T06:33:19.5866662Z  2025-07-17T06:33:19.5867158Z import yaml 2025-07-17T06:33:19.5867620Z from github import Auth, Github 2025-07-17T06:33:19.5868178Z from github.Issue import Issue 2025-07-17T06:33:19.5868702Z  2025-07-17T06:33:19.5869062Z  2025-07-17T06:33:19.5869518Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-07-17T06:33:19.5870279Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-07-17T06:33:19.5871229Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-07-17T06:33:19.5871982Z  2025-07-17T06:33:19.5872463Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-07-17T06:33:19.5873278Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-07-17T06:33:19.5873864Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-07-17T06:33:19.5874530Z OPT_OUT_LABEL = "no-runner-experiments" 2025-07-17T06:33:19.5875094Z  2025-07-17T06:33:19.5875516Z SETTING_EXPERIMENTS = "experiments" 2025-07-17T06:33:19.5876047Z  2025-07-17T06:33:19.5876450Z LF_FLEET_EXPERIMENT = "lf" 2025-07-17T06:33:19.5877077Z CANARY_FLEET_SUFFIX = ".c" 2025-07-17T06:33:19.5877569Z  2025-07-17T06:33:19.5877933Z  2025-07-17T06:33:19.5878331Z class Experiment(NamedTuple): 2025-07-17T06:33:19.5878880Z  rollout_perc: float = ( 2025-07-17T06:33:19.5879607Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-07-17T06:33:19.5880332Z  ) 2025-07-17T06:33:19.5880747Z  all_branches: bool = ( 2025-07-17T06:33:19.5881473Z  False # If True, the experiment is also enabled on the exception branches 2025-07-17T06:33:19.5882185Z  ) 2025-07-17T06:33:19.5882591Z  default: bool = ( 2025-07-17T06:33:19.5883237Z  True # If True, the experiment is enabled by default for all queries 2025-07-17T06:33:19.5883920Z  ) 2025-07-17T06:33:19.5884298Z  2025-07-17T06:33:19.5884689Z  # Add more fields as needed 2025-07-17T06:33:19.5885202Z  2025-07-17T06:33:19.5885559Z  2025-07-17T06:33:19.5885954Z class Settings(NamedTuple): 2025-07-17T06:33:19.5886459Z  """ 2025-07-17T06:33:19.5887077Z  Settings for the experiments that can be opted into. 2025-07-17T06:33:19.5887724Z  """ 2025-07-17T06:33:19.5888107Z  2025-07-17T06:33:19.5888545Z  experiments: dict[str, Experiment] = {} 2025-07-17T06:33:19.5889094Z  2025-07-17T06:33:19.5889599Z  2025-07-17T06:33:19.5890053Z class ColorFormatter(logging.Formatter): 2025-07-17T06:33:19.5890751Z  """Color codes the log messages based on the log level""" 2025-07-17T06:33:19.5891370Z  2025-07-17T06:33:19.5891745Z  COLORS = { 2025-07-17T06:33:19.5892226Z  "WARNING": "\033[33m", # Yellow 2025-07-17T06:33:19.5892790Z  "ERROR": "\033[31m", # Red 2025-07-17T06:33:19.5893334Z  "CRITICAL": "\033[31m", # Red 2025-07-17T06:33:19.5893876Z  "INFO": "\033[0m", # Reset 2025-07-17T06:33:19.5894421Z  "DEBUG": "\033[0m", # Reset 2025-07-17T06:33:19.5894934Z  } 2025-07-17T06:33:19.5895320Z  2025-07-17T06:33:19.5895774Z  def format(self, record: LogRecord) -> str: 2025-07-17T06:33:19.5896577Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-07-17T06:33:19.5897779Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-07-17T06:33:19.5898417Z  return super().format(record) 2025-07-17T06:33:19.5898943Z  2025-07-17T06:33:19.5899295Z  2025-07-17T06:33:19.5899709Z handler = logging.StreamHandler() 2025-07-17T06:33:19.5900534Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-07-17T06:33:19.5901283Z  2025-07-17T06:33:19.5901760Z log = logging.getLogger(os.path.basename(__file__)) 2025-07-17T06:33:19.5902398Z log.addHandler(handler) 2025-07-17T06:33:19.5902914Z log.setLevel(logging.INFO) 2025-07-17T06:33:19.5903398Z  2025-07-17T06:33:19.5903761Z  2025-07-17T06:33:19.5904250Z def set_github_output(key: str, value: str) -> None: 2025-07-17T06:33:19.5904860Z  """ 2025-07-17T06:33:19.5905428Z  Defines outputs of the github action that invokes this script 2025-07-17T06:33:19.5906104Z  """ 2025-07-17T06:33:19.5906677Z  if not GITHUB_OUTPUT: 2025-07-17T06:33:19.5908147Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-07-17T06:33:19.5909345Z  log.warning( 2025-07-17T06:33:19.5910311Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-07-17T06:33:19.5911296Z  ) 2025-07-17T06:33:19.5911798Z  print(f"::set-output name={key}::{value}") 2025-07-17T06:33:19.5912381Z  return 2025-07-17T06:33:19.5912812Z  2025-07-17T06:33:19.5913234Z  with open(GITHUB_OUTPUT, "a") as f: 2025-07-17T06:33:19.5913874Z  log.info(f"Setting output: {key}='{value}'") 2025-07-17T06:33:19.5914493Z  f.write(f"{key}={value}\n") 2025-07-17T06:33:19.5915017Z  2025-07-17T06:33:19.5915397Z  2025-07-17T06:33:19.5915950Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-07-17T06:33:19.5916664Z  return frozenset( 2025-07-17T06:33:19.5917471Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-07-17T06:33:19.5918199Z  ) 2025-07-17T06:33:19.5918593Z  2025-07-17T06:33:19.5918964Z  2025-07-17T06:33:19.5919352Z def parse_args() -> Any: 2025-07-17T06:33:19.5920015Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-07-17T06:33:19.5920959Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-07-17T06:33:19.5921775Z  parser.add_argument( 2025-07-17T06:33:19.5922312Z  "--github-issue-repo", 2025-07-17T06:33:19.5922848Z  type=str, 2025-07-17T06:33:19.5923327Z  required=False, 2025-07-17T06:33:19.5923980Z  default="pytorch/test-infra", 2025-07-17T06:33:19.5924598Z  help="GitHub repo to get the issue", 2025-07-17T06:33:19.5925151Z  ) 2025-07-17T06:33:19.5925569Z  parser.add_argument( 2025-07-17T06:33:19.5926085Z  "--github-repo", 2025-07-17T06:33:19.5926582Z  type=str, 2025-07-17T06:33:19.5927162Z  required=True, 2025-07-17T06:33:19.5927708Z  help="GitHub repo where CI is running", 2025-07-17T06:33:19.5928272Z  ) 2025-07-17T06:33:19.5928686Z  parser.add_argument( 2025-07-17T06:33:19.5929379Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-07-17T06:33:19.5930086Z  ) 2025-07-17T06:33:19.5930495Z  parser.add_argument( 2025-07-17T06:33:19.5931206Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-07-17T06:33:19.5931931Z  ) 2025-07-17T06:33:19.5932352Z  parser.add_argument( 2025-07-17T06:33:19.5933074Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-07-17T06:33:19.5933818Z  ) 2025-07-17T06:33:19.5934237Z  parser.add_argument( 2025-07-17T06:33:19.5934982Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-07-17T06:33:19.5935731Z  ) 2025-07-17T06:33:19.5936154Z  parser.add_argument( 2025-07-17T06:33:19.5936672Z  "--github-ref-type", 2025-07-17T06:33:19.5937505Z  type=str, 2025-07-17T06:33:19.5938004Z  required=True, 2025-07-17T06:33:19.5938595Z  help="Current GitHub ref type, branch or tag", 2025-07-17T06:33:19.5939201Z  ) 2025-07-17T06:33:19.5939623Z  parser.add_argument( 2025-07-17T06:33:19.5940166Z  "--eligible-experiments", 2025-07-17T06:33:19.5940911Z  type=_str_comma_separated_to_set, 2025-07-17T06:33:19.5941478Z  required=False, 2025-07-17T06:33:19.5941968Z  default="", 2025-07-17T06:33:19.5942909Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-07-17T06:33:19.5943893Z  ) 2025-07-17T06:33:19.5944309Z  parser.add_argument( 2025-07-17T06:33:19.5944845Z  "--opt-out-experiments", 2025-07-17T06:33:19.5945419Z  type=_str_comma_separated_to_set, 2025-07-17T06:33:19.5945984Z  required=False, 2025-07-17T06:33:19.5946471Z  default="", 2025-07-17T06:33:19.5947077Z  help=( 2025-07-17T06:33:19.5947842Z  "comma separated list of experiments to opt-out of. If unset, no opt-outs will occur. " 2025-07-17T06:33:19.5949038Z  "If the same experiment is listed both here and in '--eligible-experiments' opt-out will take priority." 2025-07-17T06:33:19.5949933Z  ), 2025-07-17T06:33:19.5950341Z  ) 2025-07-17T06:33:19.5950757Z  parser.add_argument( 2025-07-17T06:33:19.5951263Z  "--pr-number", 2025-07-17T06:33:19.5951753Z  type=str, 2025-07-17T06:33:19.5952225Z  required=False, 2025-07-17T06:33:19.5952712Z  default="", 2025-07-17T06:33:19.5953279Z  help="the optional PR number where this is run", 2025-07-17T06:33:19.5953874Z  ) 2025-07-17T06:33:19.5954266Z  2025-07-17T06:33:19.5954671Z  return parser.parse_args() 2025-07-17T06:33:19.5955190Z  2025-07-17T06:33:19.5955557Z  2025-07-17T06:33:19.5956206Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-07-17T06:33:19.5957255Z  auth = Auth.Token(github_token) 2025-07-17T06:33:19.5957894Z  return Github(auth=auth) 2025-07-17T06:33:19.5958404Z  2025-07-17T06:33:19.5958765Z  2025-07-17T06:33:19.5959470Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-07-17T06:33:19.5960322Z  repo = gh.get_repo(repo) 2025-07-17T06:33:19.5960903Z  return repo.get_issue(number=issue_num) 2025-07-17T06:33:19.5961479Z  2025-07-17T06:33:19.5961836Z  2025-07-17T06:33:19.5962238Z def get_potential_pr_author( 2025-07-17T06:33:19.5962970Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-07-17T06:33:19.5963701Z ) -> str: 2025-07-17T06:33:19.5964293Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-07-17T06:33:19.5965180Z  # Fetch the actual username from the original PR. The PR number is 2025-07-17T06:33:19.5966012Z  # embedded in the tag name: ciflow// 2025-07-17T06:33:19.5966634Z  2025-07-17T06:33:19.5967168Z  gh = get_gh_client(github_token) 2025-07-17T06:33:19.5967708Z  2025-07-17T06:33:19.5968233Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-07-17T06:33:19.5968937Z  split_tag = ref_name.split("/") 2025-07-17T06:33:19.5969491Z  if ( 2025-07-17T06:33:19.5969939Z  len(split_tag) == 3 2025-07-17T06:33:19.5970560Z  and split_tag[0] == "ciflow" 2025-07-17T06:33:19.5971163Z  and split_tag[2].isnumeric() 2025-07-17T06:33:19.5971705Z  ): 2025-07-17T06:33:19.5972165Z  pr_number = split_tag[2] 2025-07-17T06:33:19.5972708Z  try: 2025-07-17T06:33:19.5973224Z  repository = gh.get_repo(repo) 2025-07-17T06:33:19.5973907Z  pull = repository.get_pull(number=int(pr_number)) 2025-07-17T06:33:19.5974711Z  except Exception as e: 2025-07-17T06:33:19.5975304Z  raise Exception( # noqa: TRY002 2025-07-17T06:33:19.5976042Z  f"issue with pull request {pr_number} from repo {repository}" 2025-07-17T06:33:19.5976746Z  ) from e 2025-07-17T06:33:19.5977702Z  return pull.user.login # type: ignore[no-any-return] 2025-07-17T06:33:19.5978485Z  # In all other cases, return the original input username 2025-07-17T06:33:19.5979133Z  return username 2025-07-17T06:33:19.5979593Z  2025-07-17T06:33:19.5979961Z  2025-07-17T06:33:19.5980425Z def is_exception_branch(branch: str) -> bool: 2025-07-17T06:33:19.5981010Z  """ 2025-07-17T06:33:19.5981731Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-07-17T06:33:19.5982580Z  """ 2025-07-17T06:33:19.5983194Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-07-17T06:33:19.5983900Z  2025-07-17T06:33:19.5984272Z  2025-07-17T06:33:19.5984695Z def load_yaml(yaml_text: str) -> Any: 2025-07-17T06:33:19.5985254Z  try: 2025-07-17T06:33:19.5985707Z  data = yaml.safe_load(yaml_text) 2025-07-17T06:33:19.5986272Z  return data 2025-07-17T06:33:19.5986760Z  except yaml.YAMLError: 2025-07-17T06:33:19.5987967Z  log.exception("Error loading YAML") 2025-07-17T06:33:19.5988537Z  raise 2025-07-17T06:33:19.5988960Z  2025-07-17T06:33:19.5989326Z  2025-07-17T06:33:19.5989995Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-07-17T06:33:19.5990798Z  """ 2025-07-17T06:33:19.5991641Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-07-17T06:33:19.5992458Z  2025-07-17T06:33:19.5993041Z  If the issue body contains "---" then the text above that is the settings 2025-07-17T06:33:19.5993891Z  and the text below is the list of opted in users. 2025-07-17T06:33:19.5994498Z  2025-07-17T06:33:19.5995115Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-07-17T06:33:19.5995866Z  """ 2025-07-17T06:33:19.5996375Z  rollout_state_parts = rollout_state.split("---") 2025-07-17T06:33:19.5997251Z  if len(rollout_state_parts) >= 2: 2025-07-17T06:33:19.5998105Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-07-17T06:33:19.5998748Z  else: 2025-07-17T06:33:19.5999186Z  return "", rollout_state 2025-07-17T06:33:19.5999693Z  2025-07-17T06:33:19.6000084Z  2025-07-17T06:33:19.6000528Z class UserOptins(dict[str, list[str]]): 2025-07-17T06:33:19.6001077Z  """ 2025-07-17T06:33:19.6001663Z  Dictionary of users with a list of features they have opted into 2025-07-17T06:33:19.6002362Z  """ 2025-07-17T06:33:19.6002737Z  2025-07-17T06:33:19.6003089Z  2025-07-17T06:33:19.6003670Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-07-17T06:33:19.6004362Z  """ 2025-07-17T06:33:19.6005148Z  Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-07-17T06:33:19.6006029Z  2025-07-17T06:33:19.6006992Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-07-17T06:33:19.6008066Z  - Example line: "@User1,lf,split_build" 2025-07-17T06:33:19.6008812Z  - A "#" prefix indicates the user is opted out of all experiments 2025-07-17T06:33:19.6009625Z  2025-07-17T06:33:19.6009977Z  2025-07-17T06:33:19.6010336Z  """ 2025-07-17T06:33:19.6010747Z  optins = UserOptins() 2025-07-17T06:33:19.6011316Z  for user in user_optin_text.split("\n"): 2025-07-17T06:33:19.6011922Z  user = user.strip("\r\n\t -") 2025-07-17T06:33:19.6012531Z  if not user or not user.startswith("@"): 2025-07-17T06:33:19.6013137Z  # Not a valid user. Skip 2025-07-17T06:33:19.6013702Z  continue 2025-07-17T06:33:19.6014159Z  2025-07-17T06:33:19.6014525Z  if user: 2025-07-17T06:33:19.6015041Z  usr_name = user.split(",")[0].strip("@") 2025-07-17T06:33:19.6015790Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-07-17T06:33:19.6016463Z  2025-07-17T06:33:19.6016843Z  return optins 2025-07-17T06:33:19.6017619Z  2025-07-17T06:33:19.6017982Z  2025-07-17T06:33:19.6018523Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-07-17T06:33:19.6019179Z  """ 2025-07-17T06:33:19.6019639Z  Check if the experiment name is valid. 2025-07-17T06:33:19.6020205Z  A valid name: 2025-07-17T06:33:19.6020948Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-07-17T06:33:19.6021951Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-07-17T06:33:19.6022717Z  - Cannot contain spaces 2025-07-17T06:33:19.6023226Z  """ 2025-07-17T06:33:19.6023610Z  2025-07-17T06:33:19.6024100Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-07-17T06:33:19.6024889Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-07-17T06:33:19.6025783Z  2025-07-17T06:33:19.6026340Z  if valid: 2025-07-17T06:33:19.6026783Z  return True 2025-07-17T06:33:19.6027350Z  2025-07-17T06:33:19.6027723Z  log.error( 2025-07-17T06:33:19.6029262Z  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-07-17T06:33:19.6030884Z  ) 2025-07-17T06:33:19.6031294Z  return False 2025-07-17T06:33:19.6031748Z  2025-07-17T06:33:19.6032109Z  2025-07-17T06:33:19.6032657Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-07-17T06:33:19.6033343Z  """ 2025-07-17T06:33:19.6034001Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-07-17T06:33:19.6034763Z  """ 2025-07-17T06:33:19.6035161Z  try: 2025-07-17T06:33:19.6035586Z  if settings_text: 2025-07-17T06:33:19.6036390Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-07-17T06:33:19.6037359Z  # for easy reading 2025-07-17T06:33:19.6038244Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-07-17T06:33:19.6039193Z  # the backtick character in shell commands. 2025-07-17T06:33:19.6039861Z  backtick = chr(96) # backtick character 2025-07-17T06:33:19.6040600Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-07-17T06:33:19.6041322Z  settings = load_yaml(settings_text) 2025-07-17T06:33:19.6041878Z  2025-07-17T06:33:19.6042518Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-07-17T06:33:19.6043442Z  experiments = {} 2025-07-17T06:33:19.6043940Z  2025-07-17T06:33:19.6044537Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-07-17T06:33:19.6045349Z  if not is_valid_experiment_name(exp_name): 2025-07-17T06:33:19.6046521Z  # 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-07-17T06:33:19.6047718Z  continue 2025-07-17T06:33:19.6048212Z  2025-07-17T06:33:19.6048614Z  valid_settings = {} 2025-07-17T06:33:19.6049196Z  for setting in exp_settings: 2025-07-17T06:33:19.6049810Z  if setting not in Experiment._fields: 2025-07-17T06:33:19.6050418Z  log.warning( 2025-07-17T06:33:19.6051200Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-07-17T06:33:19.6051948Z  ) 2025-07-17T06:33:19.6052433Z  else: 2025-07-17T06:33:19.6053013Z  valid_settings[setting] = exp_settings[setting] 2025-07-17T06:33:19.6053622Z  2025-07-17T06:33:19.6054124Z  experiments[exp_name] = Experiment(**valid_settings) 2025-07-17T06:33:19.6054816Z  return Settings(experiments) 2025-07-17T06:33:19.6055352Z  2025-07-17T06:33:19.6055738Z  except Exception: 2025-07-17T06:33:19.6056296Z  log.exception("Failed to parse settings") 2025-07-17T06:33:19.6056866Z  2025-07-17T06:33:19.6057539Z  return Settings() 2025-07-17T06:33:19.6058013Z  2025-07-17T06:33:19.6058371Z  2025-07-17T06:33:19.6059010Z def parse_settings(rollout_state: str) -> Settings: 2025-07-17T06:33:19.6059654Z  """ 2025-07-17T06:33:19.6060147Z  Parse settings, if any, from the rollout state. 2025-07-17T06:33:19.6060736Z  2025-07-17T06:33:19.6061309Z  If the issue body contains "---" then the text above that is the settings 2025-07-17T06:33:19.6062131Z  and the text below is the list of opted in users. 2025-07-17T06:33:19.6062722Z  2025-07-17T06:33:19.6063362Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-07-17T06:33:19.6064132Z  """ 2025-07-17T06:33:19.6064749Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-17T06:33:19.6065582Z  return parse_settings_from_text(settings_text) 2025-07-17T06:33:19.6066162Z  2025-07-17T06:33:19.6066516Z  2025-07-17T06:33:19.6067155Z def parse_users(rollout_state: str) -> UserOptins: 2025-07-17T06:33:19.6067776Z  """ 2025-07-17T06:33:19.6068232Z  Parse users from the rollout state. 2025-07-17T06:33:19.6068768Z  2025-07-17T06:33:19.6069132Z  """ 2025-07-17T06:33:19.6069732Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-17T06:33:19.6070541Z  return parse_user_opt_in_from_text(users_text) 2025-07-17T06:33:19.6071120Z  2025-07-17T06:33:19.6071487Z  2025-07-17T06:33:19.6072182Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-17T06:33:19.6072969Z  """ 2025-07-17T06:33:19.6073446Z  Check if a user is opted into an experiment 2025-07-17T06:33:19.6074022Z  """ 2025-07-17T06:33:19.6074569Z  return experiment_name in user_optins.get(user, []) 2025-07-17T06:33:19.6075179Z  2025-07-17T06:33:19.6075549Z  2025-07-17T06:33:19.6076352Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-17T06:33:19.6077254Z  """ 2025-07-17T06:33:19.6077790Z  Check if a user explicitly opted out of an experiment 2025-07-17T06:33:19.6078415Z  """ 2025-07-17T06:33:19.6078992Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-07-17T06:33:19.6079740Z  experiment_optout = "-" + experiment_name 2025-07-17T06:33:19.6080447Z  if experiment_optout not in user_optins.get(user, []): 2025-07-17T06:33:19.6081089Z  return False 2025-07-17T06:33:19.6081539Z  2025-07-17T06:33:19.6082038Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-07-17T06:33:19.6082676Z  log.warning( 2025-07-17T06:33:19.6083582Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-07-17T06:33:19.6084496Z  ) 2025-07-17T06:33:19.6084896Z  2025-07-17T06:33:19.6085271Z  return True 2025-07-17T06:33:19.6085704Z  2025-07-17T06:33:19.6086064Z  2025-07-17T06:33:19.6086443Z def get_runner_prefix( 2025-07-17T06:33:19.6087040Z  rollout_state: str, 2025-07-17T06:33:19.6087570Z  workflow_requestors: Iterable[str], 2025-07-17T06:33:19.6088128Z  branch: str, 2025-07-17T06:33:19.6088697Z  eligible_experiments: frozenset[str] = frozenset(), 2025-07-17T06:33:19.6089439Z  opt_out_experiments: frozenset[str] = frozenset(), 2025-07-17T06:33:19.6090071Z  is_canary: bool = False, 2025-07-17T06:33:19.6090574Z ) -> str: 2025-07-17T06:33:19.6091066Z  settings = parse_settings(rollout_state) 2025-07-17T06:33:19.6091693Z  user_optins = parse_users(rollout_state) 2025-07-17T06:33:19.6092250Z  2025-07-17T06:33:19.6092757Z  fleet_prefix = "" 2025-07-17T06:33:19.6093247Z  prefixes = [] 2025-07-17T06:33:19.6093954Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-07-17T06:33:19.6094984Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-07-17T06:33:19.6095745Z  log.info( 2025-07-17T06:33:19.6096502Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-07-17T06:33:19.6097401Z  ) 2025-07-17T06:33:19.6097851Z  continue 2025-07-17T06:33:19.6098309Z  2025-07-17T06:33:19.6098707Z  if opt_out_experiments: 2025-07-17T06:33:19.6099316Z  if experiment_name in opt_out_experiments: 2025-07-17T06:33:19.6100071Z  opt_out_exp_list = ", ".join(opt_out_experiments) 2025-07-17T06:33:19.6100703Z  log.info( 2025-07-17T06:33:19.6101725Z  f"Skipping experiment '{experiment_name}', as this workflow has opted-out (opted out experiments are: {opt_out_exp_list})" 2025-07-17T06:33:19.6102732Z  ) 2025-07-17T06:33:19.6103193Z  continue 2025-07-17T06:33:19.6103697Z  2025-07-17T06:33:19.6104100Z  if eligible_experiments: 2025-07-17T06:33:19.6104736Z  if experiment_name not in eligible_experiments: 2025-07-17T06:33:19.6105419Z  exp_list = ", ".join(eligible_experiments) 2025-07-17T06:33:19.6106008Z  log.info( 2025-07-17T06:33:19.6106857Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-07-17T06:33:19.6107825Z  ) 2025-07-17T06:33:19.6108281Z  continue 2025-07-17T06:33:19.6108961Z  elif not experiment_settings.default: 2025-07-17T06:33:19.6109537Z  log.info( 2025-07-17T06:33:19.6110271Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-07-17T06:33:19.6111043Z  ) 2025-07-17T06:33:19.6111470Z  continue 2025-07-17T06:33:19.6111923Z  2025-07-17T06:33:19.6112433Z  # Is any workflow_requestor opted out to this experiment? 2025-07-17T06:33:19.6113090Z  opted_out_users = [ 2025-07-17T06:33:19.6113603Z  requestor 2025-07-17T06:33:19.6114137Z  for requestor in workflow_requestors 2025-07-17T06:33:19.6114875Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-07-17T06:33:19.6115546Z  ] 2025-07-17T06:33:19.6115942Z  2025-07-17T06:33:19.6116335Z  if opted_out_users: 2025-07-17T06:33:19.6116860Z  log.info( 2025-07-17T06:33:19.6117669Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-07-17T06:33:19.6118399Z  ) 2025-07-17T06:33:19.6118839Z  continue 2025-07-17T06:33:19.6119285Z  2025-07-17T06:33:19.6119790Z  # Is any workflow_requestor opted in to this experiment? 2025-07-17T06:33:19.6120436Z  opted_in_users = [ 2025-07-17T06:33:19.6120960Z  requestor 2025-07-17T06:33:19.6121508Z  for requestor in workflow_requestors 2025-07-17T06:33:19.6122238Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-07-17T06:33:19.6122905Z  ] 2025-07-17T06:33:19.6123300Z  2025-07-17T06:33:19.6123678Z  enabled = False 2025-07-17T06:33:19.6124178Z  if opted_in_users: 2025-07-17T06:33:19.6124807Z  log.info( 2025-07-17T06:33:19.6125517Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-07-17T06:33:19.6126233Z  ) 2025-07-17T06:33:19.6126678Z  enabled = True 2025-07-17T06:33:19.6127466Z  2025-07-17T06:33:19.6127920Z  elif experiment_settings.rollout_perc: 2025-07-17T06:33:19.6128811Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-07-17T06:33:19.6129817Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-07-17T06:33:19.6130515Z  log.info( 2025-07-17T06:33:19.6131454Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-07-17T06:33:19.6132419Z  ) 2025-07-17T06:33:19.6132888Z  enabled = True 2025-07-17T06:33:19.6133401Z  2025-07-17T06:33:19.6133775Z  if enabled: 2025-07-17T06:33:19.6134274Z  label = experiment_name 2025-07-17T06:33:19.6134891Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-07-17T06:33:19.6135778Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-07-17T06:33:19.6136707Z  # - If it's enabled, then we always list it's prefix first 2025-07-17T06:33:19.6137634Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-07-17T06:33:19.6138337Z  if is_canary: 2025-07-17T06:33:19.6138894Z  label += CANARY_FLEET_SUFFIX 2025-07-17T06:33:19.6139480Z  fleet_prefix = label 2025-07-17T06:33:19.6140020Z  else: 2025-07-17T06:33:19.6140519Z  prefixes.append(label) 2025-07-17T06:33:19.6141203Z  2025-07-17T06:33:19.6141590Z  if len(prefixes) > 1: 2025-07-17T06:33:19.6142100Z  log.error( 2025-07-17T06:33:19.6143213Z  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-07-17T06:33:19.6144371Z  ) 2025-07-17T06:33:19.6144808Z  prefixes = prefixes[:1] 2025-07-17T06:33:19.6145315Z  2025-07-17T06:33:19.6145717Z  # Fleet always comes first 2025-07-17T06:33:19.6146238Z  if fleet_prefix: 2025-07-17T06:33:19.6146754Z  prefixes.insert(0, fleet_prefix) 2025-07-17T06:33:19.6147406Z  2025-07-17T06:33:19.6147888Z  return ".".join(prefixes) + "." if prefixes else "" 2025-07-17T06:33:19.6148502Z  2025-07-17T06:33:19.6148864Z  2025-07-17T06:33:19.6149558Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-07-17T06:33:19.6150379Z  """ 2025-07-17T06:33:19.6151030Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-07-17T06:33:19.6151772Z  2025-07-17T06:33:19.6152388Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-07-17T06:33:19.6153136Z  """ 2025-07-17T06:33:19.6153572Z  gh = get_gh_client(github_token) 2025-07-17T06:33:19.6154182Z  issue = get_issue(gh, repo, issue_num) 2025-07-17T06:33:19.6154882Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-07-17T06:33:19.6155521Z  2025-07-17T06:33:19.6155875Z  2025-07-17T06:33:19.6156508Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-07-17T06:33:19.6157547Z  for _ in range(num_retries): 2025-07-17T06:33:19.6158085Z  try: 2025-07-17T06:33:19.6158582Z  req = Request(url=url, headers=headers) 2025-07-17T06:33:19.6159299Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-07-17T06:33:19.6159998Z  return json.loads(content) 2025-07-17T06:33:19.6160569Z  except Exception as e: 2025-07-17T06:33:19.6161268Z  log.warning(f"Could not download {url}: {e}") 2025-07-17T06:33:19.6161863Z  2025-07-17T06:33:19.6162476Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-07-17T06:33:19.6163235Z  return {} 2025-07-17T06:33:19.6163657Z  2025-07-17T06:33:19.6164015Z  2025-07-17T06:33:19.6164375Z @cache 2025-07-17T06:33:19.6165060Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-07-17T06:33:19.6165871Z  """ 2025-07-17T06:33:19.6166312Z  Dynamically get PR information 2025-07-17T06:33:19.6166840Z  """ 2025-07-17T06:33:19.6167492Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-07-17T06:33:19.6168163Z  headers = { 2025-07-17T06:33:19.6168693Z  "Accept": "application/vnd.github.v3+json", 2025-07-17T06:33:19.6169356Z  "Authorization": f"token {github_token}", 2025-07-17T06:33:19.6169921Z  } 2025-07-17T06:33:19.6170413Z  json_response: dict[str, Any] = download_json( 2025-07-17T06:33:19.6171078Z  url=f"{github_api}/issues/{pr_number}", 2025-07-17T06:33:19.6171651Z  headers=headers, 2025-07-17T06:33:19.6172134Z  ) 2025-07-17T06:33:19.6172514Z  2025-07-17T06:33:19.6172902Z  if not json_response: 2025-07-17T06:33:19.6173550Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-07-17T06:33:19.6174350Z  return {} 2025-07-17T06:33:19.6174800Z  2025-07-17T06:33:19.6175181Z  return json_response 2025-07-17T06:33:19.6175662Z  2025-07-17T06:33:19.6176019Z  2025-07-17T06:33:19.6176648Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-07-17T06:33:19.6177519Z  """ 2025-07-17T06:33:19.6178114Z  Dynamically get the latest list of labels from the pull request 2025-07-17T06:33:19.6178794Z  """ 2025-07-17T06:33:19.6179333Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-07-17T06:33:19.6179985Z  return { 2025-07-17T06:33:19.6180630Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-07-17T06:33:19.6181355Z  } 2025-07-17T06:33:19.6181735Z  2025-07-17T06:33:19.6182106Z  2025-07-17T06:33:19.6182484Z def main() -> None: 2025-07-17T06:33:19.6182977Z  args = parse_args() 2025-07-17T06:33:19.6183452Z  2025-07-17T06:33:19.6183911Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-07-17T06:33:19.6184483Z  2025-07-17T06:33:19.6184880Z  # Check if the PR is opt-out 2025-07-17T06:33:19.6185424Z  if args.pr_number: 2025-07-17T06:33:19.6186152Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-07-17T06:33:19.6187057Z  if OPT_OUT_LABEL in labels: 2025-07-17T06:33:19.6187603Z  log.info( 2025-07-17T06:33:19.6188374Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-07-17T06:33:19.6189175Z  ) 2025-07-17T06:33:19.6189798Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-17T06:33:19.6190520Z  sys.exit() 2025-07-17T06:33:19.6191107Z  2025-07-17T06:33:19.6191476Z  try: 2025-07-17T06:33:19.6191964Z  rollout_state = get_rollout_state_from_issue( 2025-07-17T06:33:19.6192739Z  args.github_token, args.github_issue_repo, args.github_issue 2025-07-17T06:33:19.6193416Z  ) 2025-07-17T06:33:19.6193808Z  2025-07-17T06:33:19.6194231Z  username = get_potential_pr_author( 2025-07-17T06:33:19.6194802Z  args.github_token, 2025-07-17T06:33:19.6195330Z  args.github_repo, 2025-07-17T06:33:19.6195853Z  args.github_actor, 2025-07-17T06:33:19.6196391Z  args.github_ref_type, 2025-07-17T06:33:19.6197035Z  args.github_branch, 2025-07-17T06:33:19.6197549Z  ) 2025-07-17T06:33:19.6197944Z  2025-07-17T06:33:19.6198457Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-07-17T06:33:19.6199105Z  2025-07-17T06:33:19.6199548Z  runner_label_prefix = get_runner_prefix( 2025-07-17T06:33:19.6200170Z  rollout_state, 2025-07-17T06:33:19.6200721Z  (args.github_issue_owner, username), 2025-07-17T06:33:19.6201310Z  args.github_branch, 2025-07-17T06:33:19.6201875Z  args.eligible_experiments, 2025-07-17T06:33:19.6202458Z  args.opt_out_experiments, 2025-07-17T06:33:19.6203013Z  is_canary, 2025-07-17T06:33:19.6203474Z  ) 2025-07-17T06:33:19.6203875Z  2025-07-17T06:33:19.6204264Z  except Exception as e: 2025-07-17T06:33:19.6204773Z  log.error( 2025-07-17T06:33:19.6205522Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-07-17T06:33:19.6206336Z  ) 2025-07-17T06:33:19.6206743Z  2025-07-17T06:33:19.6207542Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-17T06:33:19.6208235Z  2025-07-17T06:33:19.6208594Z  2025-07-17T06:33:19.6208981Z if __name__ == "__main__": 2025-07-17T06:33:19.6209471Z  main() 2025-07-17T06:33:19.6209879Z  2025-07-17T06:33:19.6210233Z EOF 2025-07-17T06:33:19.6210607Z  2025-07-17T06:33:19.6210997Z cat runner_determinator.py 2025-07-17T06:33:19.6461663Z shell: /usr/bin/bash -e {0} 2025-07-17T06:33:19.6462495Z env: 2025-07-17T06:33:19.6463246Z GITHUB_TOKEN: *** 2025-07-17T06:33:19.6463686Z ISSUE_NUMBER: 5132 2025-07-17T06:33:19.6464159Z TRIGGERING_ACTOR: pytorchmergebot 2025-07-17T06:33:19.6464708Z ISSUE_OWNER: 2025-07-17T06:33:19.6465133Z CHECK_EXPERIMENTS: 2025-07-17T06:33:19.6465590Z OPT_OUT_EXPERIMENTS: 2025-07-17T06:33:19.6466048Z PR_NUMBER: 2025-07-17T06:33:19.6466447Z ##[endgroup] 2025-07-17T06:33:19.6669877Z # flake8: noqa: G004 2025-07-17T06:33:19.6670215Z 2025-07-17T06:33:19.6670639Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-07-17T06:33:19.6671581Z # must be kept in sync. You can do it easily by running the following command: 2025-07-17T06:33:19.6672387Z # python .github/scripts/update_runner_determinator.py 2025-07-17T06:33:19.6672835Z 2025-07-17T06:33:19.6672998Z """ 2025-07-17T06:33:19.6673566Z This runner determinator is used to determine which set of runners to run a 2025-07-17T06:33:19.6674437Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-07-17T06:33:19.6675328Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-07-17T06:33:19.6676124Z of which runners should be used to run which job. 2025-07-17T06:33:19.6676516Z 2025-07-17T06:33:19.6677241Z The configuration has two parts, the settings and a list of opted-in users, 2025-07-17T06:33:19.6678349Z separated by a line containing "---". If the line is not present, the 2025-07-17T06:33:19.6679252Z settings are considered to be empty with only the second part, the user 2025-07-17T06:33:19.6679933Z list, defined. 2025-07-17T06:33:19.6680165Z 2025-07-17T06:33:19.6680531Z The first part is a YAML block that defines the rollout settings. This can be 2025-07-17T06:33:19.6681447Z used to define any settings that are needed to determine which runners to use. 2025-07-17T06:33:19.6682267Z It's fields are defined by the RolloutSettings class below. 2025-07-17T06:33:19.6682704Z 2025-07-17T06:33:19.6683077Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-07-17T06:33:19.6683963Z The user list is also a comma separated list of additional features or 2025-07-17T06:33:19.6684694Z experiments which the user could be opted in to. 2025-07-17T06:33:19.6685084Z 2025-07-17T06:33:19.6685282Z The user list has the following rules: 2025-07-17T06:33:19.6685631Z 2025-07-17T06:33:19.6685944Z - Users are GitHub usernames, which must start with the @ prefix 2025-07-17T06:33:19.6686791Z - Each user is also a comma-separated list of features/experiments to enable 2025-07-17T06:33:19.6687838Z - A "#" prefix opts the user out of all experiments 2025-07-17T06:33:19.6688231Z 2025-07-17T06:33:19.6688407Z Example config: 2025-07-17T06:33:19.6688852Z # A list of experiments that can be opted into. 2025-07-17T06:33:19.6689676Z # This defines the behavior they'll induce when opted into. 2025-07-17T06:33:19.6690347Z # Expected syntax is: 2025-07-17T06:33:19.6690970Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-07-17T06:33:19.6691921Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-07-17T06:33:19.6692523Z 2025-07-17T06:33:19.6692689Z experiments: 2025-07-17T06:33:19.6693069Z lf: 2025-07-17T06:33:19.6693437Z rollout_percent: 25 2025-07-17T06:33:19.6693893Z all_branches: false 2025-07-17T06:33:19.6694518Z default: true 2025-07-17T06:33:19.6694930Z --- 2025-07-17T06:33:19.6695129Z 2025-07-17T06:33:19.6695290Z # Opt-ins: 2025-07-17T06:33:19.6695860Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-07-17T06:33:19.6696709Z # and specifying experiments to enable in a comma-separated list. 2025-07-17T06:33:19.6697843Z # To always opt out of an experiment, prefix it with a "-". 2025-07-17T06:33:19.6698497Z # Experiments should be from the above list. 2025-07-17T06:33:19.6698873Z 2025-07-17T06:33:19.6699051Z @User1,-lf,split_build 2025-07-17T06:33:19.6699480Z @User2,lf 2025-07-17T06:33:19.6699856Z @User3,split_build 2025-07-17T06:33:19.6700262Z """ 2025-07-17T06:33:19.6700448Z 2025-07-17T06:33:19.6700610Z import json 2025-07-17T06:33:19.6700973Z import logging 2025-07-17T06:33:19.6701348Z import os 2025-07-17T06:33:19.6701708Z import random 2025-07-17T06:33:19.6702078Z import re 2025-07-17T06:33:19.6702437Z import sys 2025-07-17T06:33:19.6702844Z from argparse import ArgumentParser 2025-07-17T06:33:19.6703359Z from collections.abc import Iterable 2025-07-17T06:33:19.6703874Z from functools import cache 2025-07-17T06:33:19.6704334Z from logging import LogRecord 2025-07-17T06:33:19.6704822Z from typing import Any, NamedTuple 2025-07-17T06:33:19.6705344Z from urllib.request import Request, urlopen 2025-07-17T06:33:19.6705711Z 2025-07-17T06:33:19.6705871Z import yaml 2025-07-17T06:33:19.6706249Z from github import Auth, Github 2025-07-17T06:33:19.6706733Z from github.Issue import Issue 2025-07-17T06:33:19.6707249Z 2025-07-17T06:33:19.6707256Z 2025-07-17T06:33:19.6707486Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-07-17T06:33:19.6708195Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-07-17T06:33:19.6709067Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-07-17T06:33:19.6709625Z 2025-07-17T06:33:19.6709850Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-07-17T06:33:19.6710567Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-07-17T06:33:19.6711084Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-07-17T06:33:19.6711625Z OPT_OUT_LABEL = "no-runner-experiments" 2025-07-17T06:33:19.6711967Z 2025-07-17T06:33:19.6712159Z SETTING_EXPERIMENTS = "experiments" 2025-07-17T06:33:19.6712484Z 2025-07-17T06:33:19.6712669Z LF_FLEET_EXPERIMENT = "lf" 2025-07-17T06:33:19.6713122Z CANARY_FLEET_SUFFIX = ".c" 2025-07-17T06:33:19.6713396Z 2025-07-17T06:33:19.6713402Z 2025-07-17T06:33:19.6713585Z class Experiment(NamedTuple): 2025-07-17T06:33:19.6714053Z rollout_perc: float = ( 2025-07-17T06:33:19.6714666Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-07-17T06:33:19.6715341Z ) 2025-07-17T06:33:19.6715710Z all_branches: bool = ( 2025-07-17T06:33:19.6716329Z False # If True, the experiment is also enabled on the exception branches 2025-07-17T06:33:19.6717175Z ) 2025-07-17T06:33:19.6717532Z default: bool = ( 2025-07-17T06:33:19.6718098Z True # If True, the experiment is enabled by default for all queries 2025-07-17T06:33:19.6718724Z ) 2025-07-17T06:33:19.6718923Z 2025-07-17T06:33:19.6719101Z # Add more fields as needed 2025-07-17T06:33:19.6719395Z 2025-07-17T06:33:19.6719401Z 2025-07-17T06:33:19.6719588Z class Settings(NamedTuple): 2025-07-17T06:33:19.6720019Z """ 2025-07-17T06:33:19.6720462Z Settings for the experiments that can be opted into. 2025-07-17T06:33:19.6721025Z """ 2025-07-17T06:33:19.6721214Z 2025-07-17T06:33:19.6721423Z experiments: dict[str, Experiment] = {} 2025-07-17T06:33:19.6721779Z 2025-07-17T06:33:19.6721784Z 2025-07-17T06:33:19.6721986Z class ColorFormatter(logging.Formatter): 2025-07-17T06:33:19.6722598Z """Color codes the log messages based on the log level""" 2025-07-17T06:33:19.6723022Z 2025-07-17T06:33:19.6723186Z COLORS = { 2025-07-17T06:33:19.6723577Z "WARNING": "\033[33m", # Yellow 2025-07-17T06:33:19.6724077Z "ERROR": "\033[31m", # Red 2025-07-17T06:33:19.6724699Z "CRITICAL": "\033[31m", # Red 2025-07-17T06:33:19.6725186Z "INFO": "\033[0m", # Reset 2025-07-17T06:33:19.6725653Z "DEBUG": "\033[0m", # Reset 2025-07-17T06:33:19.6726107Z } 2025-07-17T06:33:19.6726300Z 2025-07-17T06:33:19.6726529Z def format(self, record: LogRecord) -> str: 2025-07-17T06:33:19.6727497Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-07-17T06:33:19.6728268Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-07-17T06:33:19.6728835Z return super().format(record) 2025-07-17T06:33:19.6729172Z 2025-07-17T06:33:19.6729179Z 2025-07-17T06:33:19.6729379Z handler = logging.StreamHandler() 2025-07-17T06:33:19.6730083Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-07-17T06:33:19.6730639Z 2025-07-17T06:33:19.6730898Z log = logging.getLogger(os.path.basename(__file__)) 2025-07-17T06:33:19.6731470Z log.addHandler(handler) 2025-07-17T06:33:19.6731922Z log.setLevel(logging.INFO) 2025-07-17T06:33:19.6732215Z 2025-07-17T06:33:19.6732221Z 2025-07-17T06:33:19.6732472Z def set_github_output(key: str, value: str) -> None: 2025-07-17T06:33:19.6733012Z """ 2025-07-17T06:33:19.6733508Z Defines outputs of the github action that invokes this script 2025-07-17T06:33:19.6734113Z """ 2025-07-17T06:33:19.6734471Z if not GITHUB_OUTPUT: 2025-07-17T06:33:19.6735532Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-07-17T06:33:19.6736647Z log.warning( 2025-07-17T06:33:19.6737896Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-07-17T06:33:19.6738810Z ) 2025-07-17T06:33:19.6748394Z print(f"::set-output name={key}::{value}") 2025-07-17T06:33:19.6749000Z return 2025-07-17T06:33:19.6749238Z 2025-07-17T06:33:19.6749619Z with open(GITHUB_OUTPUT, "a") as f: 2025-07-17T06:33:19.6750237Z log.info(f"Setting output: {key}='{value}'") 2025-07-17T06:33:19.6750810Z f.write(f"{key}={value}\n") 2025-07-17T06:33:19.6751137Z 2025-07-17T06:33:19.6751144Z 2025-07-17T06:33:19.6751454Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-07-17T06:33:19.6752074Z return frozenset( 2025-07-17T06:33:19.6752683Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-07-17T06:33:19.6753354Z ) 2025-07-17T06:33:19.6753554Z 2025-07-17T06:33:19.6753562Z 2025-07-17T06:33:19.6753741Z def parse_args() -> Any: 2025-07-17T06:33:19.6754280Z parser = ArgumentParser("Get dynamic rollout settings") 2025-07-17T06:33:19.6755143Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-07-17T06:33:19.6755905Z parser.add_argument( 2025-07-17T06:33:19.6756348Z "--github-issue-repo", 2025-07-17T06:33:19.6756803Z type=str, 2025-07-17T06:33:19.6757432Z required=False, 2025-07-17T06:33:19.6757896Z default="pytorch/test-infra", 2025-07-17T06:33:19.6758423Z help="GitHub repo to get the issue", 2025-07-17T06:33:19.6758925Z ) 2025-07-17T06:33:19.6759279Z parser.add_argument( 2025-07-17T06:33:19.6759716Z "--github-repo", 2025-07-17T06:33:19.6760128Z type=str, 2025-07-17T06:33:19.6760516Z required=True, 2025-07-17T06:33:19.6760966Z help="GitHub repo where CI is running", 2025-07-17T06:33:19.6761487Z ) 2025-07-17T06:33:19.6761846Z parser.add_argument( 2025-07-17T06:33:19.6762448Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-07-17T06:33:19.6763107Z ) 2025-07-17T06:33:19.6763461Z parser.add_argument( 2025-07-17T06:33:19.6764076Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-07-17T06:33:19.6764742Z ) 2025-07-17T06:33:19.6765101Z parser.add_argument( 2025-07-17T06:33:19.6765729Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-07-17T06:33:19.6766547Z ) 2025-07-17T06:33:19.6767209Z parser.add_argument( 2025-07-17T06:33:19.6767938Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-07-17T06:33:19.6768651Z ) 2025-07-17T06:33:19.6769010Z parser.add_argument( 2025-07-17T06:33:19.6769454Z "--github-ref-type", 2025-07-17T06:33:19.6769895Z type=str, 2025-07-17T06:33:19.6770287Z required=True, 2025-07-17T06:33:19.6770753Z help="Current GitHub ref type, branch or tag", 2025-07-17T06:33:19.6771308Z ) 2025-07-17T06:33:19.6771668Z parser.add_argument( 2025-07-17T06:33:19.6772119Z "--eligible-experiments", 2025-07-17T06:33:19.6772622Z type=_str_comma_separated_to_set, 2025-07-17T06:33:19.6773141Z required=False, 2025-07-17T06:33:19.6773550Z default="", 2025-07-17T06:33:19.6774394Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-07-17T06:33:19.6775321Z ) 2025-07-17T06:33:19.6775677Z parser.add_argument( 2025-07-17T06:33:19.6776138Z "--opt-out-experiments", 2025-07-17T06:33:19.6825320Z type=_str_comma_separated_to_set, 2025-07-17T06:33:19.6826518Z required=False, 2025-07-17T06:33:19.6827504Z default="", 2025-07-17T06:33:19.6828057Z help=( 2025-07-17T06:33:19.6828741Z "comma separated list of experiments to opt-out of. If unset, no opt-outs will occur. " 2025-07-17T06:33:19.6829891Z "If the same experiment is listed both here and in '--eligible-experiments' opt-out will take priority." 2025-07-17T06:33:19.6830735Z ), 2025-07-17T06:33:19.6831079Z ) 2025-07-17T06:33:19.6831444Z parser.add_argument( 2025-07-17T06:33:19.6831871Z "--pr-number", 2025-07-17T06:33:19.6832280Z type=str, 2025-07-17T06:33:19.6832665Z required=False, 2025-07-17T06:33:19.6833077Z default="", 2025-07-17T06:33:19.6833735Z help="the optional PR number where this is run", 2025-07-17T06:33:19.6834299Z ) 2025-07-17T06:33:19.6834492Z 2025-07-17T06:33:19.6834680Z return parser.parse_args() 2025-07-17T06:33:19.6834989Z 2025-07-17T06:33:19.6834997Z 2025-07-17T06:33:19.6835396Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-07-17T06:33:19.6836149Z auth = Auth.Token(github_token) 2025-07-17T06:33:19.6836660Z return Github(auth=auth) 2025-07-17T06:33:19.6837211Z 2025-07-17T06:33:19.6837219Z 2025-07-17T06:33:19.6837689Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-07-17T06:33:19.6838490Z repo = gh.get_repo(repo) 2025-07-17T06:33:19.6838988Z return repo.get_issue(number=issue_num) 2025-07-17T06:33:19.6839351Z 2025-07-17T06:33:19.6839357Z 2025-07-17T06:33:19.6839551Z def get_potential_pr_author( 2025-07-17T06:33:19.6840192Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-07-17T06:33:19.6840871Z ) -> str: 2025-07-17T06:33:19.6841369Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-07-17T06:33:19.6842164Z # Fetch the actual username from the original PR. The PR number is 2025-07-17T06:33:19.6842893Z # embedded in the tag name: ciflow// 2025-07-17T06:33:19.6843321Z 2025-07-17T06:33:19.6843510Z gh = get_gh_client(github_token) 2025-07-17T06:33:19.6843839Z 2025-07-17T06:33:19.6844105Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-07-17T06:33:19.6844712Z split_tag = ref_name.split("/") 2025-07-17T06:33:19.6845202Z if ( 2025-07-17T06:33:19.6845577Z len(split_tag) == 3 2025-07-17T06:33:19.6846054Z and split_tag[0] == "ciflow" 2025-07-17T06:33:19.6846567Z and split_tag[2].isnumeric() 2025-07-17T06:33:19.6847238Z ): 2025-07-17T06:33:19.6847609Z pr_number = split_tag[2] 2025-07-17T06:33:19.6848090Z try: 2025-07-17T06:33:19.6848657Z repository = gh.get_repo(repo) 2025-07-17T06:33:19.6849258Z pull = repository.get_pull(number=int(pr_number)) 2025-07-17T06:33:19.6849852Z except Exception as e: 2025-07-17T06:33:19.6850356Z raise Exception( # noqa: TRY002 2025-07-17T06:33:19.6851013Z f"issue with pull request {pr_number} from repo {repository}" 2025-07-17T06:33:19.6851639Z ) from e 2025-07-17T06:33:19.6852164Z return pull.user.login # type: ignore[no-any-return] 2025-07-17T06:33:19.6852842Z # In all other cases, return the original input username 2025-07-17T06:33:19.6853424Z return username 2025-07-17T06:33:19.6853661Z 2025-07-17T06:33:19.6853667Z 2025-07-17T06:33:19.6853893Z def is_exception_branch(branch: str) -> bool: 2025-07-17T06:33:19.6854413Z """ 2025-07-17T06:33:19.6855041Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-07-17T06:33:19.6855811Z """ 2025-07-17T06:33:19.6856344Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-07-17T06:33:19.6856859Z 2025-07-17T06:33:19.6857063Z 2025-07-17T06:33:19.6857281Z def load_yaml(yaml_text: str) -> Any: 2025-07-17T06:33:19.6857769Z try: 2025-07-17T06:33:19.6858147Z data = yaml.safe_load(yaml_text) 2025-07-17T06:33:19.6858647Z return data 2025-07-17T06:33:19.6859054Z except yaml.YAMLError: 2025-07-17T06:33:19.6859530Z log.exception("Error loading YAML") 2025-07-17T06:33:19.6860034Z raise 2025-07-17T06:33:19.6860243Z 2025-07-17T06:33:19.6860250Z 2025-07-17T06:33:19.6860655Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-07-17T06:33:19.6861389Z """ 2025-07-17T06:33:19.6861999Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-07-17T06:33:19.6862586Z 2025-07-17T06:33:19.6863055Z If the issue body contains "---" then the text above that is the settings 2025-07-17T06:33:19.6863811Z and the text below is the list of opted in users. 2025-07-17T06:33:19.6864211Z 2025-07-17T06:33:19.6864577Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-07-17T06:33:19.6865274Z """ 2025-07-17T06:33:19.6865709Z rollout_state_parts = rollout_state.split("---") 2025-07-17T06:33:19.6866341Z if len(rollout_state_parts) >= 2: 2025-07-17T06:33:19.6867151Z return rollout_state_parts[0], rollout_state_parts[1] 2025-07-17T06:33:19.6867749Z else: 2025-07-17T06:33:19.6868122Z return "", rollout_state 2025-07-17T06:33:19.6868421Z 2025-07-17T06:33:19.6868429Z 2025-07-17T06:33:19.6868626Z class UserOptins(dict[str, list[str]]): 2025-07-17T06:33:19.6869129Z """ 2025-07-17T06:33:19.6869633Z Dictionary of users with a list of features they have opted into 2025-07-17T06:33:19.6870276Z """ 2025-07-17T06:33:19.6870465Z 2025-07-17T06:33:19.6870471Z 2025-07-17T06:33:19.6870818Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-07-17T06:33:19.6871456Z """ 2025-07-17T06:33:19.6872144Z Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-07-17T06:33:19.6872822Z 2025-07-17T06:33:19.6873430Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-07-17T06:33:19.6874413Z - Example line: "@User1,lf,split_build" 2025-07-17T06:33:19.6875077Z - A "#" prefix indicates the user is opted out of all experiments 2025-07-17T06:33:19.6875554Z 2025-07-17T06:33:19.6875561Z 2025-07-17T06:33:19.6875715Z """ 2025-07-17T06:33:19.6876082Z optins = UserOptins() 2025-07-17T06:33:19.6876556Z for user in user_optin_text.split("\n"): 2025-07-17T06:33:19.6877299Z user = user.strip("\r\n\t -") 2025-07-17T06:33:19.6877839Z if not user or not user.startswith("@"): 2025-07-17T06:33:19.6878394Z # Not a valid user. Skip 2025-07-17T06:33:19.6879013Z continue 2025-07-17T06:33:19.6879262Z 2025-07-17T06:33:19.6879419Z if user: 2025-07-17T06:33:19.6879844Z usr_name = user.split(",")[0].strip("@") 2025-07-17T06:33:19.6880520Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-07-17T06:33:19.6881006Z 2025-07-17T06:33:19.6881177Z return optins 2025-07-17T06:33:19.6881410Z 2025-07-17T06:33:19.6881417Z 2025-07-17T06:33:19.6881696Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-07-17T06:33:19.6882292Z """ 2025-07-17T06:33:19.6882678Z Check if the experiment name is valid. 2025-07-17T06:33:19.6883228Z A valid name: 2025-07-17T06:33:19.6883866Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-07-17T06:33:19.6884791Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-07-17T06:33:19.6885525Z - Cannot contain spaces 2025-07-17T06:33:19.6885984Z """ 2025-07-17T06:33:19.6886186Z 2025-07-17T06:33:19.6886444Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-07-17T06:33:19.6887330Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-07-17T06:33:19.6887780Z 2025-07-17T06:33:19.6887936Z if valid: 2025-07-17T06:33:19.6888307Z return True 2025-07-17T06:33:19.6888540Z 2025-07-17T06:33:19.6888701Z log.error( 2025-07-17T06:33:19.6890143Z 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-07-17T06:33:19.6891745Z ) 2025-07-17T06:33:19.6892093Z return False 2025-07-17T06:33:19.6892318Z 2025-07-17T06:33:19.6892326Z 2025-07-17T06:33:19.6892622Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-07-17T06:33:19.6893232Z """ 2025-07-17T06:33:19.6893945Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-07-17T06:33:19.6894669Z """ 2025-07-17T06:33:19.6895011Z try: 2025-07-17T06:33:19.6895370Z if settings_text: 2025-07-17T06:33:19.6896090Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-07-17T06:33:19.6897085Z # for easy reading 2025-07-17T06:33:19.6897894Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-07-17T06:33:19.6898771Z # the backtick character in shell commands. 2025-07-17T06:33:19.6899360Z backtick = chr(96) # backtick character 2025-07-17T06:33:19.6900015Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-07-17T06:33:19.6900662Z settings = load_yaml(settings_text) 2025-07-17T06:33:19.6901034Z 2025-07-17T06:33:19.6901443Z # For now we just load experiments. We can expand this if/when we add more settings 2025-07-17T06:33:19.6902198Z experiments = {} 2025-07-17T06:33:19.6902488Z 2025-07-17T06:33:19.6902862Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-07-17T06:33:19.6903625Z if not is_valid_experiment_name(exp_name): 2025-07-17T06:33:19.6904737Z # 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-07-17T06:33:19.6905785Z continue 2025-07-17T06:33:19.6906063Z 2025-07-17T06:33:19.6906247Z valid_settings = {} 2025-07-17T06:33:19.6906753Z for setting in exp_settings: 2025-07-17T06:33:19.6907521Z if setting not in Experiment._fields: 2025-07-17T06:33:19.6908074Z log.warning( 2025-07-17T06:33:19.6908772Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-07-17T06:33:19.6909481Z ) 2025-07-17T06:33:19.6910046Z else: 2025-07-17T06:33:19.6910554Z valid_settings[setting] = exp_settings[setting] 2025-07-17T06:33:19.6910977Z 2025-07-17T06:33:19.6911245Z experiments[exp_name] = Experiment(**valid_settings) 2025-07-17T06:33:19.6911869Z return Settings(experiments) 2025-07-17T06:33:19.6912215Z 2025-07-17T06:33:19.6912387Z except Exception: 2025-07-17T06:33:19.6912854Z log.exception("Failed to parse settings") 2025-07-17T06:33:19.6913239Z 2025-07-17T06:33:19.6913404Z return Settings() 2025-07-17T06:33:19.6913663Z 2025-07-17T06:33:19.6913669Z 2025-07-17T06:33:19.6913915Z def parse_settings(rollout_state: str) -> Settings: 2025-07-17T06:33:19.6914475Z """ 2025-07-17T06:33:19.6914893Z Parse settings, if any, from the rollout state. 2025-07-17T06:33:19.6915301Z 2025-07-17T06:33:19.6915654Z If the issue body contains "---" then the text above that is the settings 2025-07-17T06:33:19.6916416Z and the text below is the list of opted in users. 2025-07-17T06:33:19.6916827Z 2025-07-17T06:33:19.6917455Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-07-17T06:33:19.6918216Z """ 2025-07-17T06:33:19.6918764Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-17T06:33:19.6919519Z return parse_settings_from_text(settings_text) 2025-07-17T06:33:19.6919915Z 2025-07-17T06:33:19.6919922Z 2025-07-17T06:33:19.6920162Z def parse_users(rollout_state: str) -> UserOptins: 2025-07-17T06:33:19.6920717Z """ 2025-07-17T06:33:19.6921093Z Parse users from the rollout state. 2025-07-17T06:33:19.6921450Z 2025-07-17T06:33:19.6921605Z """ 2025-07-17T06:33:19.6922119Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-17T06:33:19.6922844Z return parse_user_opt_in_from_text(users_text) 2025-07-17T06:33:19.6923246Z 2025-07-17T06:33:19.6923253Z 2025-07-17T06:33:19.6923804Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-17T06:33:19.6924554Z """ 2025-07-17T06:33:19.6924961Z Check if a user is opted into an experiment 2025-07-17T06:33:19.6925488Z """ 2025-07-17T06:33:19.6925934Z return experiment_name in user_optins.get(user, []) 2025-07-17T06:33:19.6926352Z 2025-07-17T06:33:19.6926358Z 2025-07-17T06:33:19.6926763Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-17T06:33:19.6927695Z """ 2025-07-17T06:33:19.6928157Z Check if a user explicitly opted out of an experiment 2025-07-17T06:33:19.6928725Z """ 2025-07-17T06:33:19.6929228Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-07-17T06:33:19.6929898Z experiment_optout = "-" + experiment_name 2025-07-17T06:33:19.6930531Z if experiment_optout not in user_optins.get(user, []): 2025-07-17T06:33:19.6931116Z return False 2025-07-17T06:33:19.6931374Z 2025-07-17T06:33:19.6931654Z if is_user_opted_in(user, user_optins, experiment_name): 2025-07-17T06:33:19.6932248Z log.warning( 2025-07-17T06:33:19.6933037Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-07-17T06:33:19.6933915Z ) 2025-07-17T06:33:19.6934120Z 2025-07-17T06:33:19.6934283Z return True 2025-07-17T06:33:19.6934518Z 2025-07-17T06:33:19.6934524Z 2025-07-17T06:33:19.6934695Z def get_runner_prefix( 2025-07-17T06:33:19.6935120Z rollout_state: str, 2025-07-17T06:33:19.6935576Z workflow_requestors: Iterable[str], 2025-07-17T06:33:19.6936080Z branch: str, 2025-07-17T06:33:19.6936561Z eligible_experiments: frozenset[str] = frozenset(), 2025-07-17T06:33:19.6937462Z opt_out_experiments: frozenset[str] = frozenset(), 2025-07-17T06:33:19.6938042Z is_canary: bool = False, 2025-07-17T06:33:19.6938495Z ) -> str: 2025-07-17T06:33:19.6938895Z settings = parse_settings(rollout_state) 2025-07-17T06:33:19.6939623Z user_optins = parse_users(rollout_state) 2025-07-17T06:33:19.6939986Z 2025-07-17T06:33:19.6940157Z fleet_prefix = "" 2025-07-17T06:33:19.6940573Z prefixes = [] 2025-07-17T06:33:19.6941185Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-07-17T06:33:19.6942116Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-07-17T06:33:19.6942819Z log.info( 2025-07-17T06:33:19.6943480Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-07-17T06:33:19.6944233Z ) 2025-07-17T06:33:19.6944597Z continue 2025-07-17T06:33:19.6944850Z 2025-07-17T06:33:19.6945035Z if opt_out_experiments: 2025-07-17T06:33:19.6945557Z if experiment_name in opt_out_experiments: 2025-07-17T06:33:19.6946202Z opt_out_exp_list = ", ".join(opt_out_experiments) 2025-07-17T06:33:19.6946783Z log.info( 2025-07-17T06:33:19.6947966Z f"Skipping experiment '{experiment_name}', as this workflow has opted-out (opted out experiments are: {opt_out_exp_list})" 2025-07-17T06:33:19.6948951Z ) 2025-07-17T06:33:19.6949336Z continue 2025-07-17T06:33:19.6949612Z 2025-07-17T06:33:19.6949798Z if eligible_experiments: 2025-07-17T06:33:19.6950341Z if experiment_name not in eligible_experiments: 2025-07-17T06:33:19.6950973Z exp_list = ", ".join(eligible_experiments) 2025-07-17T06:33:19.6951522Z log.info( 2025-07-17T06:33:19.6952288Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-07-17T06:33:19.6953122Z ) 2025-07-17T06:33:19.6953509Z continue 2025-07-17T06:33:19.6953969Z elif not experiment_settings.default: 2025-07-17T06:33:19.6954492Z log.info( 2025-07-17T06:33:19.6955274Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-07-17T06:33:19.6956013Z ) 2025-07-17T06:33:19.6956387Z continue 2025-07-17T06:33:19.6956395Z 2025-07-17T06:33:19.6956678Z # Is any workflow_requestor opted out to this experiment? 2025-07-17T06:33:19.6956855Z opted_out_users = [ 2025-07-17T06:33:19.6957230Z requestor 2025-07-17T06:33:19.6957466Z for requestor in workflow_requestors 2025-07-17T06:33:19.6957790Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-07-17T06:33:19.6957944Z ] 2025-07-17T06:33:19.6957952Z 2025-07-17T06:33:19.6958126Z if opted_out_users: 2025-07-17T06:33:19.6958285Z log.info( 2025-07-17T06:33:19.6958654Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-07-17T06:33:19.6958811Z ) 2025-07-17T06:33:19.6958969Z continue 2025-07-17T06:33:19.6958976Z 2025-07-17T06:33:19.6959253Z # Is any workflow_requestor opted in to this experiment? 2025-07-17T06:33:19.6959432Z opted_in_users = [ 2025-07-17T06:33:19.6959591Z requestor 2025-07-17T06:33:19.6959794Z for requestor in workflow_requestors 2025-07-17T06:33:19.6960087Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-07-17T06:33:19.6960242Z ] 2025-07-17T06:33:19.6960250Z 2025-07-17T06:33:19.6960415Z enabled = False 2025-07-17T06:33:19.6960587Z if opted_in_users: 2025-07-17T06:33:19.6960744Z log.info( 2025-07-17T06:33:19.6961088Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-07-17T06:33:19.6961253Z ) 2025-07-17T06:33:19.6961420Z enabled = True 2025-07-17T06:33:19.6961428Z 2025-07-17T06:33:19.6961631Z elif experiment_settings.rollout_perc: 2025-07-17T06:33:19.6962078Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-07-17T06:33:19.6962564Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-07-17T06:33:19.6962730Z log.info( 2025-07-17T06:33:19.6963319Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-07-17T06:33:19.6963477Z ) 2025-07-17T06:33:19.6963648Z enabled = True 2025-07-17T06:33:19.6963655Z 2025-07-17T06:33:19.6963814Z if enabled: 2025-07-17T06:33:19.6963997Z label = experiment_name 2025-07-17T06:33:19.6964210Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-07-17T06:33:19.6964636Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-07-17T06:33:19.6964903Z # - If it's enabled, then we always list it's prefix first 2025-07-17T06:33:19.6965210Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-07-17T06:33:19.6965385Z if is_canary: 2025-07-17T06:33:19.6965576Z label += CANARY_FLEET_SUFFIX 2025-07-17T06:33:19.6965763Z fleet_prefix = label 2025-07-17T06:33:19.6965921Z else: 2025-07-17T06:33:19.6966112Z prefixes.append(label) 2025-07-17T06:33:19.6966120Z 2025-07-17T06:33:19.6966296Z if len(prefixes) > 1: 2025-07-17T06:33:19.6966454Z log.error( 2025-07-17T06:33:19.6967383Z 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-07-17T06:33:19.6967541Z ) 2025-07-17T06:33:19.6967726Z prefixes = prefixes[:1] 2025-07-17T06:33:19.6967734Z 2025-07-17T06:33:19.6967915Z # Fleet always comes first 2025-07-17T06:33:19.6968082Z if fleet_prefix: 2025-07-17T06:33:19.6968282Z prefixes.insert(0, fleet_prefix) 2025-07-17T06:33:19.6968290Z 2025-07-17T06:33:19.6968658Z return ".".join(prefixes) + "." if prefixes else "" 2025-07-17T06:33:19.6968667Z 2025-07-17T06:33:19.6968673Z 2025-07-17T06:33:19.6969114Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-07-17T06:33:19.6969274Z """ 2025-07-17T06:33:19.6969657Z Gets the first comment of the issue, which contains the desired rollout state. 2025-07-17T06:33:19.6969665Z 2025-07-17T06:33:19.6970039Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-07-17T06:33:19.6970202Z """ 2025-07-17T06:33:19.6970390Z gh = get_gh_client(github_token) 2025-07-17T06:33:19.6970588Z issue = get_issue(gh, repo, issue_num) 2025-07-17T06:33:19.6970870Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-07-17T06:33:19.6970878Z 2025-07-17T06:33:19.6970883Z 2025-07-17T06:33:19.6971283Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-07-17T06:33:19.6971466Z for _ in range(num_retries): 2025-07-17T06:33:19.6971640Z try: 2025-07-17T06:33:19.6971850Z req = Request(url=url, headers=headers) 2025-07-17T06:33:19.6972135Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-07-17T06:33:19.6972327Z return json.loads(content) 2025-07-17T06:33:19.6972510Z except Exception as e: 2025-07-17T06:33:19.6972743Z log.warning(f"Could not download {url}: {e}") 2025-07-17T06:33:19.6972751Z 2025-07-17T06:33:19.6973125Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-07-17T06:33:19.6973292Z return {} 2025-07-17T06:33:19.6973299Z 2025-07-17T06:33:19.6973305Z 2025-07-17T06:33:19.6973459Z @cache 2025-07-17T06:33:19.6973885Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-07-17T06:33:19.6974047Z """ 2025-07-17T06:33:19.6974238Z Dynamically get PR information 2025-07-17T06:33:19.6974392Z """ 2025-07-17T06:33:19.6974687Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-07-17T06:33:19.6974972Z headers = { 2025-07-17T06:33:19.6975194Z "Accept": "application/vnd.github.v3+json", 2025-07-17T06:33:19.6975402Z "Authorization": f"token {github_token}", 2025-07-17T06:33:19.6975560Z } 2025-07-17T06:33:19.6975787Z json_response: dict[str, Any] = download_json( 2025-07-17T06:33:19.6975989Z url=f"{github_api}/issues/{pr_number}", 2025-07-17T06:33:19.6976159Z headers=headers, 2025-07-17T06:33:19.6976320Z ) 2025-07-17T06:33:19.6976327Z 2025-07-17T06:33:19.6976498Z if not json_response: 2025-07-17T06:33:19.6976778Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-07-17T06:33:19.6977107Z return {} 2025-07-17T06:33:19.6977119Z 2025-07-17T06:33:19.6977298Z return json_response 2025-07-17T06:33:19.6977305Z 2025-07-17T06:33:19.6977312Z 2025-07-17T06:33:19.6977707Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-07-17T06:33:19.6977878Z """ 2025-07-17T06:33:19.6978201Z Dynamically get the latest list of labels from the pull request 2025-07-17T06:33:19.6978357Z """ 2025-07-17T06:33:19.6978641Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-07-17T06:33:19.6978798Z return { 2025-07-17T06:33:19.6979154Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-07-17T06:33:19.6979306Z } 2025-07-17T06:33:19.6979313Z 2025-07-17T06:33:19.6979325Z 2025-07-17T06:33:19.6979495Z def main() -> None: 2025-07-17T06:33:19.6979666Z args = parse_args() 2025-07-17T06:33:19.6979674Z 2025-07-17T06:33:19.6979885Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-07-17T06:33:19.6979894Z 2025-07-17T06:33:19.6980082Z # Check if the PR is opt-out 2025-07-17T06:33:19.6980249Z if args.pr_number: 2025-07-17T06:33:19.6980635Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-07-17T06:33:19.6980948Z if OPT_OUT_LABEL in labels: 2025-07-17T06:33:19.6981121Z log.info( 2025-07-17T06:33:19.6981550Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-07-17T06:33:19.6981706Z ) 2025-07-17T06:33:19.6982035Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-17T06:33:19.6982197Z sys.exit() 2025-07-17T06:33:19.6982204Z 2025-07-17T06:33:19.6982357Z try: 2025-07-17T06:33:19.6982588Z rollout_state = get_rollout_state_from_issue( 2025-07-17T06:33:19.6982895Z args.github_token, args.github_issue_repo, args.github_issue 2025-07-17T06:33:19.6983052Z ) 2025-07-17T06:33:19.6983060Z 2025-07-17T06:33:19.6983293Z username = get_potential_pr_author( 2025-07-17T06:33:19.6983469Z args.github_token, 2025-07-17T06:33:19.6983643Z args.github_repo, 2025-07-17T06:33:19.6983813Z args.github_actor, 2025-07-17T06:33:19.6983995Z args.github_ref_type, 2025-07-17T06:33:19.6984177Z args.github_branch, 2025-07-17T06:33:19.6984333Z ) 2025-07-17T06:33:19.6984341Z 2025-07-17T06:33:19.6984628Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-07-17T06:33:19.6984635Z 2025-07-17T06:33:19.6984843Z runner_label_prefix = get_runner_prefix( 2025-07-17T06:33:19.6985016Z rollout_state, 2025-07-17T06:33:19.6985224Z (args.github_issue_owner, username), 2025-07-17T06:33:19.6985399Z args.github_branch, 2025-07-17T06:33:19.6985591Z args.eligible_experiments, 2025-07-17T06:33:19.6985782Z args.opt_out_experiments, 2025-07-17T06:33:19.6985948Z is_canary, 2025-07-17T06:33:19.6986100Z ) 2025-07-17T06:33:19.6986107Z 2025-07-17T06:33:19.6986284Z except Exception as e: 2025-07-17T06:33:19.6986449Z log.error( 2025-07-17T06:33:19.6986980Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-07-17T06:33:19.6987270Z ) 2025-07-17T06:33:19.6987279Z 2025-07-17T06:33:19.6987607Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-17T06:33:19.6987614Z 2025-07-17T06:33:19.6987621Z 2025-07-17T06:33:19.6987791Z if __name__ == "__main__": 2025-07-17T06:33:19.6987945Z main() 2025-07-17T06:33:19.6987953Z 2025-07-17T06:33:19.7076440Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-07-17T06:33:19.7077522Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-07-17T06:33:19.7105733Z shell: /usr/bin/bash -e {0} 2025-07-17T06:33:19.7106212Z env: 2025-07-17T06:33:19.7106833Z GITHUB_TOKEN: *** 2025-07-17T06:33:19.7107548Z ISSUE_NUMBER: 5132 2025-07-17T06:33:19.7107993Z TRIGGERING_ACTOR: pytorchmergebot 2025-07-17T06:33:19.7108486Z ISSUE_OWNER: 2025-07-17T06:33:19.7108878Z CHECK_EXPERIMENTS: 2025-07-17T06:33:19.7109296Z OPT_OUT_EXPERIMENTS: 2025-07-17T06:33:19.7109712Z PR_NUMBER: 2025-07-17T06:33:19.7110084Z ##[endgroup] 2025-07-17T06:33:20.7519185Z Defaulting to user installation because normal site-packages is not writeable 2025-07-17T06:33:21.3691053Z Collecting urllib3==1.26.18 2025-07-17T06:33:21.4138093Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-07-17T06:33:21.4342727Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 4.1 MB/s eta 0:00:00 2025-07-17T06:33:21.4608044Z Collecting PyGithub==2.3.0 2025-07-17T06:33:21.4691142Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-07-17T06:33:21.5155268Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-07-17T06:33:21.5228835Z 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-07-17T06:33:21.5274668Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-07-17T06:33:21.5292292Z 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-07-17T06:33:21.5307511Z Requirement already satisfied: typing-extensions>=4.0.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (4.10.0) 2025-07-17T06:33:21.5587741Z Collecting Deprecated (from PyGithub==2.3.0) 2025-07-17T06:33:21.5657733Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl.metadata (5.7 kB) 2025-07-17T06:33:21.5888808Z 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-07-17T06:33:21.7033345Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-07-17T06:33:21.7106598Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-07-17T06:33:21.8171845Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-07-17T06:33:21.8245768Z 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-07-17T06:33:21.8464229Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-07-17T06:33:21.8532053Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-07-17T06:33:21.8793576Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-07-17T06:33:21.8894702Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 16.2 MB/s eta 0:00:00 2025-07-17T06:33:21.8974363Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-07-17T06:33:21.9082534Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 38.4 MB/s eta 0:00:00 2025-07-17T06:33:21.9155898Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (856 kB) 2025-07-17T06:33:21.9268589Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 89.3 MB/s eta 0:00:00 2025-07-17T06:33:21.9338779Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl (10.0 kB) 2025-07-17T06:33:21.9434106Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-07-17T06:33:21.9499336Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 98.6 MB/s eta 0:00:00 2025-07-17T06:33:21.9575503Z 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-07-17T06:33:21.9617970Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 30.1 MB/s eta 0:00:00 2025-07-17T06:33:21.9687212Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-07-17T06:33:21.9733824Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 34.9 MB/s eta 0:00:00 2025-07-17T06:33:22.2850202Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-07-17T06:33:22.8209104Z 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-07-17T06:33:22.9101835Z ##[group]Run curr_branch="main" 2025-07-17T06:33:22.9102139Z curr_branch="main" 2025-07-17T06:33:22.9102363Z curr_ref_type="branch" 2025-07-17T06:33:22.9102637Z echo "Current branch is '$curr_branch'" 2025-07-17T06:33:22.9102895Z  2025-07-17T06:33:22.9103075Z python3 runner_determinator.py \ 2025-07-17T06:33:22.9103365Z  --github-token "$GITHUB_TOKEN" \ 2025-07-17T06:33:22.9103633Z  --github-issue "$ISSUE_NUMBER" \ 2025-07-17T06:33:22.9103877Z  --github-branch "$curr_branch" \ 2025-07-17T06:33:22.9104139Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-07-17T06:33:22.9104409Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-07-17T06:33:22.9104685Z  --github-ref-type "$curr_ref_type" \ 2025-07-17T06:33:22.9104950Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-07-17T06:33:22.9105254Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-07-17T06:33:22.9105613Z  --opt-out-experiments "$OPT_OUT_EXPERIMENTS" \ 2025-07-17T06:33:22.9105899Z  --pr-number "${PR_NUMBER}" 2025-07-17T06:33:22.9134899Z shell: /usr/bin/bash -e {0} 2025-07-17T06:33:22.9135128Z env: 2025-07-17T06:33:22.9135664Z GITHUB_TOKEN: *** 2025-07-17T06:33:22.9135864Z ISSUE_NUMBER: 5132 2025-07-17T06:33:22.9136073Z TRIGGERING_ACTOR: pytorchmergebot 2025-07-17T06:33:22.9136300Z ISSUE_OWNER: 2025-07-17T06:33:22.9136478Z CHECK_EXPERIMENTS: 2025-07-17T06:33:22.9136670Z OPT_OUT_EXPERIMENTS: 2025-07-17T06:33:22.9136860Z PR_NUMBER: 2025-07-17T06:33:22.9137243Z ##[endgroup] 2025-07-17T06:33:22.9184638Z Current branch is 'main' 2025-07-17T06:33:24.2749206Z INFO : Branch main is an exception branch. Not enabling experiment ephemeral. 2025-07-17T06:33:24.2750531Z INFO : Branch main is an exception branch. Not enabling experiment wincanary. 2025-07-17T06:33:24.2752017Z INFO : Setting output: label-type='' 2025-07-17T06:33:24.3062896Z Evaluate and set job outputs 2025-07-17T06:33:24.3070366Z Cleaning up orphan processes