2025-07-24T03:46:05.2100586Z Current runner version: '2.326.0' 2025-07-24T03:46:05.2125320Z ##[group]Runner Image Provisioner 2025-07-24T03:46:05.2126188Z Hosted Compute Agent 2025-07-24T03:46:05.2126862Z Version: 20250711.363 2025-07-24T03:46:05.2127642Z Commit: 6785254374ce925a23743850c1cb91912ce5c14c 2025-07-24T03:46:05.2128273Z Build Date: 2025-07-11T20:04:25Z 2025-07-24T03:46:05.2128854Z ##[endgroup] 2025-07-24T03:46:05.2129409Z ##[group]Operating System 2025-07-24T03:46:05.2129989Z Ubuntu 2025-07-24T03:46:05.2130453Z 24.04.2 2025-07-24T03:46:05.2130904Z LTS 2025-07-24T03:46:05.2131364Z ##[endgroup] 2025-07-24T03:46:05.2131803Z ##[group]Runner Image 2025-07-24T03:46:05.2132400Z Image: ubuntu-24.04 2025-07-24T03:46:05.2132876Z Version: 20250720.1.0 2025-07-24T03:46:05.2133853Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250720.1/images/ubuntu/Ubuntu2404-Readme.md 2025-07-24T03:46:05.2135374Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250720.1 2025-07-24T03:46:05.2136294Z ##[endgroup] 2025-07-24T03:46:05.2137465Z ##[group]GITHUB_TOKEN Permissions 2025-07-24T03:46:05.2139457Z Contents: read 2025-07-24T03:46:05.2139997Z Metadata: read 2025-07-24T03:46:05.2140548Z ##[endgroup] 2025-07-24T03:46:05.2142846Z Secret source: Actions 2025-07-24T03:46:05.2143809Z Prepare workflow directory 2025-07-24T03:46:05.2661988Z Prepare all required actions 2025-07-24T03:46:05.2716519Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (07df6ba7f5597488a93b3855d52d2ead55675125) 2025-07-24T03:46:05.2721808Z ##[group] Inputs 2025-07-24T03:46:05.2722415Z check_experiments: 2025-07-24T03:46:05.2723046Z opt_out_experiments: 2025-07-24T03:46:05.2723601Z triggering_actor: pytorch-bot[bot] 2025-07-24T03:46:05.2724205Z issue_owner: 2025-07-24T03:46:05.2724672Z curr_branch: ciflow/trunk/149961 2025-07-24T03:46:05.2725331Z curr_ref_type: tag 2025-07-24T03:46:05.2725985Z issue_number: 5132 2025-07-24T03:46:05.2726473Z ##[endgroup] 2025-07-24T03:46:05.2727412Z Complete job name: before-test / get-label-type / runner-determinator 2025-07-24T03:46:05.9390704Z ##[group]Run cat < runner_determinator.py 2025-07-24T03:46:05.9393155Z cat < runner_determinator.py 2025-07-24T03:46:05.9393822Z # flake8: noqa: G004 2025-07-24T03:46:05.9394442Z  2025-07-24T03:46:05.9395239Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-07-24T03:46:05.9396341Z # must be kept in sync. You can do it easily by running the following command: 2025-07-24T03:46:05.9397577Z # python .github/scripts/update_runner_determinator.py 2025-07-24T03:46:05.9398307Z  2025-07-24T03:46:05.9398781Z """ 2025-07-24T03:46:05.9399467Z This runner determinator is used to determine which set of runners to run a 2025-07-24T03:46:05.9400580Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-07-24T03:46:05.9401772Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-07-24T03:46:05.9402822Z of which runners should be used to run which job. 2025-07-24T03:46:05.9403491Z  2025-07-24T03:46:05.9404216Z The configuration has two parts, the settings and a list of opted-in users, 2025-07-24T03:46:05.9405349Z separated by a line containing "---". If the line is not present, the 2025-07-24T03:46:05.9406364Z settings are considered to be empty with only the second part, the user 2025-07-24T03:46:05.9407578Z list, defined. 2025-07-24T03:46:05.9408132Z  2025-07-24T03:46:05.9408851Z The first part is a YAML block that defines the rollout settings. This can be 2025-07-24T03:46:05.9409949Z used to define any settings that are needed to determine which runners to use. 2025-07-24T03:46:05.9410956Z It's fields are defined by the RolloutSettings class below. 2025-07-24T03:46:05.9411716Z  2025-07-24T03:46:05.9412690Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-07-24T03:46:05.9413774Z The user list is also a comma separated list of additional features or 2025-07-24T03:46:05.9414715Z experiments which the user could be opted in to. 2025-07-24T03:46:05.9415382Z  2025-07-24T03:46:05.9415940Z The user list has the following rules: 2025-07-24T03:46:05.9416564Z  2025-07-24T03:46:05.9417467Z - Users are GitHub usernames, which must start with the @ prefix 2025-07-24T03:46:05.9418469Z - Each user is also a comma-separated list of features/experiments to enable 2025-07-24T03:46:05.9419480Z - A "#" prefix opts the user out of all experiments 2025-07-24T03:46:05.9420163Z  2025-07-24T03:46:05.9420595Z Example config: 2025-07-24T03:46:05.9421292Z  # A list of experiments that can be opted into. 2025-07-24T03:46:05.9422124Z  # This defines the behavior they'll induce when opted into. 2025-07-24T03:46:05.9422901Z  # Expected syntax is: 2025-07-24T03:46:05.9423760Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-07-24T03:46:05.9424891Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-07-24T03:46:05.9425762Z  2025-07-24T03:46:05.9426292Z  experiments: 2025-07-24T03:46:05.9427094Z  lf: 2025-07-24T03:46:05.9427624Z  rollout_percent: 25 2025-07-24T03:46:05.9428305Z  all_branches: false 2025-07-24T03:46:05.9428906Z  default: true 2025-07-24T03:46:05.9429428Z  --- 2025-07-24T03:46:05.9429960Z  2025-07-24T03:46:05.9430437Z  # Opt-ins: 2025-07-24T03:46:05.9431206Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-07-24T03:46:05.9432425Z  # and specifying experiments to enable in a comma-separated list. 2025-07-24T03:46:05.9433441Z  # To always opt out of an experiment, prefix it with a "-". 2025-07-24T03:46:05.9434278Z  # Experiments should be from the above list. 2025-07-24T03:46:05.9434966Z  2025-07-24T03:46:05.9435533Z  @User1,-lf,split_build 2025-07-24T03:46:05.9436114Z  @User2,lf 2025-07-24T03:46:05.9511364Z  @User3,split_build 2025-07-24T03:46:05.9512042Z """ 2025-07-24T03:46:05.9512472Z  2025-07-24T03:46:05.9512857Z import json 2025-07-24T03:46:05.9513294Z import logging 2025-07-24T03:46:05.9513733Z import os 2025-07-24T03:46:05.9514155Z import random 2025-07-24T03:46:05.9514633Z import re 2025-07-24T03:46:05.9515049Z import sys 2025-07-24T03:46:05.9515526Z from argparse import ArgumentParser 2025-07-24T03:46:05.9516350Z from collections.abc import Iterable 2025-07-24T03:46:05.9517095Z from functools import cache 2025-07-24T03:46:05.9517664Z from logging import LogRecord 2025-07-24T03:46:05.9518233Z from typing import Any, NamedTuple 2025-07-24T03:46:05.9518856Z from urllib.request import Request, urlopen 2025-07-24T03:46:05.9519444Z  2025-07-24T03:46:05.9519822Z import yaml 2025-07-24T03:46:05.9520293Z from github import Auth, Github 2025-07-24T03:46:05.9520860Z from github.Issue import Issue 2025-07-24T03:46:05.9521378Z  2025-07-24T03:46:05.9521748Z  2025-07-24T03:46:05.9522209Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-07-24T03:46:05.9523004Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-07-24T03:46:05.9523959Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-07-24T03:46:05.9524719Z  2025-07-24T03:46:05.9525440Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-07-24T03:46:05.9526077Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-07-24T03:46:05.9526668Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-07-24T03:46:05.9527796Z OPT_OUT_LABEL = "no-runner-experiments" 2025-07-24T03:46:05.9528375Z  2025-07-24T03:46:05.9528811Z SETTING_EXPERIMENTS = "experiments" 2025-07-24T03:46:05.9529359Z  2025-07-24T03:46:05.9529765Z LF_FLEET_EXPERIMENT = "lf" 2025-07-24T03:46:05.9530318Z CANARY_FLEET_SUFFIX = ".c" 2025-07-24T03:46:05.9530813Z  2025-07-24T03:46:05.9531196Z  2025-07-24T03:46:05.9531609Z class Experiment(NamedTuple): 2025-07-24T03:46:05.9532163Z  rollout_perc: float = ( 2025-07-24T03:46:05.9532908Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-07-24T03:46:05.9533652Z  ) 2025-07-24T03:46:05.9534093Z  all_branches: bool = ( 2025-07-24T03:46:05.9534827Z  False # If True, the experiment is also enabled on the exception branches 2025-07-24T03:46:05.9535547Z  ) 2025-07-24T03:46:05.9535952Z  default: bool = ( 2025-07-24T03:46:05.9536619Z  True # If True, the experiment is enabled by default for all queries 2025-07-24T03:46:05.9537458Z  ) 2025-07-24T03:46:05.9537878Z  2025-07-24T03:46:05.9538298Z  # Add more fields as needed 2025-07-24T03:46:05.9538805Z  2025-07-24T03:46:05.9539166Z  2025-07-24T03:46:05.9539569Z class Settings(NamedTuple): 2025-07-24T03:46:05.9540090Z  """ 2025-07-24T03:46:05.9540626Z  Settings for the experiments that can be opted into. 2025-07-24T03:46:05.9541255Z  """ 2025-07-24T03:46:05.9541640Z  2025-07-24T03:46:05.9542075Z  experiments: dict[str, Experiment] = {} 2025-07-24T03:46:05.9542648Z  2025-07-24T03:46:05.9543162Z  2025-07-24T03:46:05.9543626Z class ColorFormatter(logging.Formatter): 2025-07-24T03:46:05.9544329Z  """Color codes the log messages based on the log level""" 2025-07-24T03:46:05.9544963Z  2025-07-24T03:46:05.9545358Z  COLORS = { 2025-07-24T03:46:05.9545834Z  "WARNING": "\033[33m", # Yellow 2025-07-24T03:46:05.9546397Z  "ERROR": "\033[31m", # Red 2025-07-24T03:46:05.9547282Z  "CRITICAL": "\033[31m", # Red 2025-07-24T03:46:05.9547847Z  "INFO": "\033[0m", # Reset 2025-07-24T03:46:05.9548390Z  "DEBUG": "\033[0m", # Reset 2025-07-24T03:46:05.9548906Z  } 2025-07-24T03:46:05.9549290Z  2025-07-24T03:46:05.9549745Z  def format(self, record: LogRecord) -> str: 2025-07-24T03:46:05.9550566Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-07-24T03:46:05.9551407Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-07-24T03:46:05.9552039Z  return super().format(record) 2025-07-24T03:46:05.9552574Z  2025-07-24T03:46:05.9552935Z  2025-07-24T03:46:05.9553344Z handler = logging.StreamHandler() 2025-07-24T03:46:05.9554139Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-07-24T03:46:05.9554926Z  2025-07-24T03:46:05.9555425Z log = logging.getLogger(os.path.basename(__file__)) 2025-07-24T03:46:05.9556072Z log.addHandler(handler) 2025-07-24T03:46:05.9556589Z log.setLevel(logging.INFO) 2025-07-24T03:46:05.9557187Z  2025-07-24T03:46:05.9557555Z  2025-07-24T03:46:05.9558049Z def set_github_output(key: str, value: str) -> None: 2025-07-24T03:46:05.9558659Z  """ 2025-07-24T03:46:05.9559236Z  Defines outputs of the github action that invokes this script 2025-07-24T03:46:05.9560046Z  """ 2025-07-24T03:46:05.9560470Z  if not GITHUB_OUTPUT: 2025-07-24T03:46:05.9561635Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-07-24T03:46:05.9562838Z  log.warning( 2025-07-24T03:46:05.9563793Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-07-24T03:46:05.9564761Z  ) 2025-07-24T03:46:05.9565254Z  print(f"::set-output name={key}::{value}") 2025-07-24T03:46:05.9565835Z  return 2025-07-24T03:46:05.9566259Z  2025-07-24T03:46:05.9566785Z  with open(GITHUB_OUTPUT, "a") as f: 2025-07-24T03:46:05.9567432Z  log.info(f"Setting output: {key}='{value}'") 2025-07-24T03:46:05.9568065Z  f.write(f"{key}={value}\n") 2025-07-24T03:46:05.9568593Z  2025-07-24T03:46:05.9568987Z  2025-07-24T03:46:05.9569540Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-07-24T03:46:05.9570259Z  return frozenset( 2025-07-24T03:46:05.9570966Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-07-24T03:46:05.9571696Z  ) 2025-07-24T03:46:05.9572103Z  2025-07-24T03:46:05.9572472Z  2025-07-24T03:46:05.9572876Z def parse_args() -> Any: 2025-07-24T03:46:05.9573546Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-07-24T03:46:05.9574484Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-07-24T03:46:05.9575299Z  parser.add_argument( 2025-07-24T03:46:05.9575827Z  "--github-issue-repo", 2025-07-24T03:46:05.9576356Z  type=str, 2025-07-24T03:46:05.9576934Z  required=False, 2025-07-24T03:46:05.9577605Z  default="pytorch/test-infra", 2025-07-24T03:46:05.9578221Z  help="GitHub repo to get the issue", 2025-07-24T03:46:05.9578790Z  ) 2025-07-24T03:46:05.9579205Z  parser.add_argument( 2025-07-24T03:46:05.9579716Z  "--github-repo", 2025-07-24T03:46:05.9580219Z  type=str, 2025-07-24T03:46:05.9580685Z  required=True, 2025-07-24T03:46:05.9581225Z  help="GitHub repo where CI is running", 2025-07-24T03:46:05.9581783Z  ) 2025-07-24T03:46:05.9582201Z  parser.add_argument( 2025-07-24T03:46:05.9582894Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-07-24T03:46:05.9583602Z  ) 2025-07-24T03:46:05.9584017Z  parser.add_argument( 2025-07-24T03:46:05.9584740Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-07-24T03:46:05.9585477Z  ) 2025-07-24T03:46:05.9585893Z  parser.add_argument( 2025-07-24T03:46:05.9586619Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-07-24T03:46:05.9587861Z  ) 2025-07-24T03:46:05.9588279Z  parser.add_argument( 2025-07-24T03:46:05.9589037Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-07-24T03:46:05.9589792Z  ) 2025-07-24T03:46:05.9590228Z  parser.add_argument( 2025-07-24T03:46:05.9590755Z  "--github-ref-type", 2025-07-24T03:46:05.9591277Z  type=str, 2025-07-24T03:46:05.9591750Z  required=True, 2025-07-24T03:46:05.9592344Z  help="Current GitHub ref type, branch or tag", 2025-07-24T03:46:05.9592940Z  ) 2025-07-24T03:46:05.9593360Z  parser.add_argument( 2025-07-24T03:46:05.9594055Z  "--eligible-experiments", 2025-07-24T03:46:05.9594645Z  type=_str_comma_separated_to_set, 2025-07-24T03:46:05.9595217Z  required=False, 2025-07-24T03:46:05.9595719Z  default="", 2025-07-24T03:46:05.9596661Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-07-24T03:46:05.9597763Z  ) 2025-07-24T03:46:05.9598191Z  parser.add_argument( 2025-07-24T03:46:05.9598731Z  "--opt-out-experiments", 2025-07-24T03:46:05.9599318Z  type=_str_comma_separated_to_set, 2025-07-24T03:46:05.9599890Z  required=False, 2025-07-24T03:46:05.9600394Z  default="", 2025-07-24T03:46:05.9600860Z  help=( 2025-07-24T03:46:05.9601622Z  "comma separated list of experiments to opt-out of. If unset, no opt-outs will occur. " 2025-07-24T03:46:05.9602838Z  "If the same experiment is listed both here and in '--eligible-experiments' opt-out will take priority." 2025-07-24T03:46:05.9603722Z  ), 2025-07-24T03:46:05.9604131Z  ) 2025-07-24T03:46:05.9604549Z  parser.add_argument( 2025-07-24T03:46:05.9605060Z  "--pr-number", 2025-07-24T03:46:05.9605550Z  type=str, 2025-07-24T03:46:05.9606028Z  required=False, 2025-07-24T03:46:05.9606518Z  default="", 2025-07-24T03:46:05.9607192Z  help="the optional PR number where this is run", 2025-07-24T03:46:05.9607805Z  ) 2025-07-24T03:46:05.9608190Z  2025-07-24T03:46:05.9608608Z  return parser.parse_args() 2025-07-24T03:46:05.9609128Z  2025-07-24T03:46:05.9609493Z  2025-07-24T03:46:05.9610139Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-07-24T03:46:05.9611091Z  auth = Auth.Token(github_token) 2025-07-24T03:46:05.9611688Z  return Github(auth=auth) 2025-07-24T03:46:05.9612205Z  2025-07-24T03:46:05.9612581Z  2025-07-24T03:46:05.9613291Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-07-24T03:46:05.9614157Z  repo = gh.get_repo(repo) 2025-07-24T03:46:05.9614737Z  return repo.get_issue(number=issue_num) 2025-07-24T03:46:05.9615317Z  2025-07-24T03:46:05.9615684Z  2025-07-24T03:46:05.9616096Z def get_potential_pr_author( 2025-07-24T03:46:05.9616942Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-07-24T03:46:05.9617672Z ) -> str: 2025-07-24T03:46:05.9618267Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-07-24T03:46:05.9619139Z  # Fetch the actual username from the original PR. The PR number is 2025-07-24T03:46:05.9619971Z  # embedded in the tag name: ciflow// 2025-07-24T03:46:05.9620584Z  2025-07-24T03:46:05.9621004Z  gh = get_gh_client(github_token) 2025-07-24T03:46:05.9621543Z  2025-07-24T03:46:05.9622053Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-07-24T03:46:05.9622752Z  split_tag = ref_name.split("/") 2025-07-24T03:46:05.9623303Z  if ( 2025-07-24T03:46:05.9623756Z  len(split_tag) == 3 2025-07-24T03:46:05.9624318Z  and split_tag[0] == "ciflow" 2025-07-24T03:46:05.9624918Z  and split_tag[2].isnumeric() 2025-07-24T03:46:05.9625460Z  ): 2025-07-24T03:46:05.9625923Z  pr_number = split_tag[2] 2025-07-24T03:46:05.9626475Z  try: 2025-07-24T03:46:05.9627074Z  repository = gh.get_repo(repo) 2025-07-24T03:46:05.9627899Z  pull = repository.get_pull(number=int(pr_number)) 2025-07-24T03:46:05.9628573Z  except Exception as e: 2025-07-24T03:46:05.9629181Z  raise Exception( # noqa: TRY002 2025-07-24T03:46:05.9629923Z  f"issue with pull request {pr_number} from repo {repository}" 2025-07-24T03:46:05.9630627Z  ) from e 2025-07-24T03:46:05.9631271Z  return pull.user.login # type: ignore[no-any-return] 2025-07-24T03:46:05.9632044Z  # In all other cases, return the original input username 2025-07-24T03:46:05.9632699Z  return username 2025-07-24T03:46:05.9633159Z  2025-07-24T03:46:05.9633541Z  2025-07-24T03:46:05.9634013Z def is_exception_branch(branch: str) -> bool: 2025-07-24T03:46:05.9634589Z  """ 2025-07-24T03:46:05.9635320Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-07-24T03:46:05.9636149Z  """ 2025-07-24T03:46:05.9636855Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-07-24T03:46:05.9637566Z  2025-07-24T03:46:05.9637940Z  2025-07-24T03:46:05.9638390Z def load_yaml(yaml_text: str) -> Any: 2025-07-24T03:46:05.9638946Z  try: 2025-07-24T03:46:05.9639398Z  data = yaml.safe_load(yaml_text) 2025-07-24T03:46:05.9639958Z  return data 2025-07-24T03:46:05.9640454Z  except yaml.YAMLError: 2025-07-24T03:46:05.9641018Z  log.exception("Error loading YAML") 2025-07-24T03:46:05.9641585Z  raise 2025-07-24T03:46:05.9642012Z  2025-07-24T03:46:05.9642382Z  2025-07-24T03:46:05.9643047Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-07-24T03:46:05.9643846Z  """ 2025-07-24T03:46:05.9644666Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-07-24T03:46:05.9645479Z  2025-07-24T03:46:05.9646071Z  If the issue body contains "---" then the text above that is the settings 2025-07-24T03:46:05.9647009Z  and the text below is the list of opted in users. 2025-07-24T03:46:05.9647616Z  2025-07-24T03:46:05.9648244Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-07-24T03:46:05.9648990Z  """ 2025-07-24T03:46:05.9649508Z  rollout_state_parts = rollout_state.split("---") 2025-07-24T03:46:05.9650158Z  if len(rollout_state_parts) >= 2: 2025-07-24T03:46:05.9650841Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-07-24T03:46:05.9651474Z  else: 2025-07-24T03:46:05.9651915Z  return "", rollout_state 2025-07-24T03:46:05.9652523Z  2025-07-24T03:46:05.9653044Z  2025-07-24T03:46:05.9653491Z class UserOptins(dict[str, list[str]]): 2025-07-24T03:46:05.9654050Z  """ 2025-07-24T03:46:05.9654648Z  Dictionary of users with a list of features they have opted into 2025-07-24T03:46:05.9655365Z  """ 2025-07-24T03:46:05.9655757Z  2025-07-24T03:46:05.9656115Z  2025-07-24T03:46:05.9656790Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-07-24T03:46:05.9657497Z  """ 2025-07-24T03:46:05.9658282Z  Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-07-24T03:46:05.9659166Z  2025-07-24T03:46:05.9660024Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-07-24T03:46:05.9661081Z  - Example line: "@User1,lf,split_build" 2025-07-24T03:46:05.9661956Z  - A "#" prefix indicates the user is opted out of all experiments 2025-07-24T03:46:05.9662636Z  2025-07-24T03:46:05.9662996Z  2025-07-24T03:46:05.9663353Z  """ 2025-07-24T03:46:05.9663769Z  optins = UserOptins() 2025-07-24T03:46:05.9664329Z  for user in user_optin_text.split("\n"): 2025-07-24T03:46:05.9664945Z  user = user.strip("\r\n\t -") 2025-07-24T03:46:05.9665553Z  if not user or not user.startswith("@"): 2025-07-24T03:46:05.9666163Z  # Not a valid user. Skip 2025-07-24T03:46:05.9666806Z  continue 2025-07-24T03:46:05.9667256Z  2025-07-24T03:46:05.9667639Z  if user: 2025-07-24T03:46:05.9668152Z  usr_name = user.split(",")[0].strip("@") 2025-07-24T03:46:05.9668933Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-07-24T03:46:05.9669610Z  2025-07-24T03:46:05.9669999Z  return optins 2025-07-24T03:46:05.9670442Z  2025-07-24T03:46:05.9670802Z  2025-07-24T03:46:05.9671336Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-07-24T03:46:05.9671995Z  """ 2025-07-24T03:46:05.9672457Z  Check if the experiment name is valid. 2025-07-24T03:46:05.9673023Z  A valid name: 2025-07-24T03:46:05.9673751Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-07-24T03:46:05.9674738Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-07-24T03:46:05.9675500Z  - Cannot contain spaces 2025-07-24T03:46:05.9676009Z  """ 2025-07-24T03:46:05.9676392Z  2025-07-24T03:46:05.9676992Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-07-24T03:46:05.9677774Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-07-24T03:46:05.9678536Z  2025-07-24T03:46:05.9678918Z  if valid: 2025-07-24T03:46:05.9679364Z  return True 2025-07-24T03:46:05.9679805Z  2025-07-24T03:46:05.9680175Z  log.error( 2025-07-24T03:46:05.9681707Z  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-24T03:46:05.9683327Z  ) 2025-07-24T03:46:05.9683730Z  return False 2025-07-24T03:46:05.9684164Z  2025-07-24T03:46:05.9684525Z  2025-07-24T03:46:05.9685073Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-07-24T03:46:05.9685757Z  """ 2025-07-24T03:46:05.9686413Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-07-24T03:46:05.9687280Z  """ 2025-07-24T03:46:05.9687678Z  try: 2025-07-24T03:46:05.9688094Z  if settings_text: 2025-07-24T03:46:05.9688903Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-07-24T03:46:05.9689739Z  # for easy reading 2025-07-24T03:46:05.9690657Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-07-24T03:46:05.9691617Z  # the backtick character in shell commands. 2025-07-24T03:46:05.9692279Z  backtick = chr(96) # backtick character 2025-07-24T03:46:05.9693021Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-07-24T03:46:05.9693739Z  settings = load_yaml(settings_text) 2025-07-24T03:46:05.9694290Z  2025-07-24T03:46:05.9694935Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-07-24T03:46:05.9695861Z  experiments = {} 2025-07-24T03:46:05.9696362Z  2025-07-24T03:46:05.9697064Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-07-24T03:46:05.9697894Z  if not is_valid_experiment_name(exp_name): 2025-07-24T03:46:05.9699062Z  # 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-24T03:46:05.9700153Z  continue 2025-07-24T03:46:05.9700642Z  2025-07-24T03:46:05.9701045Z  valid_settings = {} 2025-07-24T03:46:05.9701630Z  for setting in exp_settings: 2025-07-24T03:46:05.9702248Z  if setting not in Experiment._fields: 2025-07-24T03:46:05.9702868Z  log.warning( 2025-07-24T03:46:05.9703654Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-07-24T03:46:05.9704402Z  ) 2025-07-24T03:46:05.9704886Z  else: 2025-07-24T03:46:05.9705476Z  valid_settings[setting] = exp_settings[setting] 2025-07-24T03:46:05.9706089Z  2025-07-24T03:46:05.9706589Z  experiments[exp_name] = Experiment(**valid_settings) 2025-07-24T03:46:05.9707383Z  return Settings(experiments) 2025-07-24T03:46:05.9707927Z  2025-07-24T03:46:05.9708311Z  except Exception: 2025-07-24T03:46:05.9708872Z  log.exception("Failed to parse settings") 2025-07-24T03:46:05.9709438Z  2025-07-24T03:46:05.9709819Z  return Settings() 2025-07-24T03:46:05.9710276Z  2025-07-24T03:46:05.9710646Z  2025-07-24T03:46:05.9711301Z def parse_settings(rollout_state: str) -> Settings: 2025-07-24T03:46:05.9711942Z  """ 2025-07-24T03:46:05.9712451Z  Parse settings, if any, from the rollout state. 2025-07-24T03:46:05.9713039Z  2025-07-24T03:46:05.9713653Z  If the issue body contains "---" then the text above that is the settings 2025-07-24T03:46:05.9714475Z  and the text below is the list of opted in users. 2025-07-24T03:46:05.9715068Z  2025-07-24T03:46:05.9715705Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-07-24T03:46:05.9716480Z  """ 2025-07-24T03:46:05.9717215Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-24T03:46:05.9718041Z  return parse_settings_from_text(settings_text) 2025-07-24T03:46:05.9718627Z  2025-07-24T03:46:05.9718987Z  2025-07-24T03:46:05.9719488Z def parse_users(rollout_state: str) -> UserOptins: 2025-07-24T03:46:05.9720092Z  """ 2025-07-24T03:46:05.9720542Z  Parse users from the rollout state. 2025-07-24T03:46:05.9721087Z  2025-07-24T03:46:05.9721450Z  """ 2025-07-24T03:46:05.9722056Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-24T03:46:05.9722856Z  return parse_user_opt_in_from_text(users_text) 2025-07-24T03:46:05.9723450Z  2025-07-24T03:46:05.9723807Z  2025-07-24T03:46:05.9724478Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-24T03:46:05.9725269Z  """ 2025-07-24T03:46:05.9725758Z  Check if a user is opted into an experiment 2025-07-24T03:46:05.9726339Z  """ 2025-07-24T03:46:05.9726957Z  return experiment_name in user_optins.get(user, []) 2025-07-24T03:46:05.9727708Z  2025-07-24T03:46:05.9728069Z  2025-07-24T03:46:05.9728752Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-24T03:46:05.9729554Z  """ 2025-07-24T03:46:05.9730083Z  Check if a user explicitly opted out of an experiment 2025-07-24T03:46:05.9730705Z  """ 2025-07-24T03:46:05.9731273Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-07-24T03:46:05.9732038Z  experiment_optout = "-" + experiment_name 2025-07-24T03:46:05.9732734Z  if experiment_optout not in user_optins.get(user, []): 2025-07-24T03:46:05.9733379Z  return False 2025-07-24T03:46:05.9733835Z  2025-07-24T03:46:05.9734343Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-07-24T03:46:05.9734984Z  log.warning( 2025-07-24T03:46:05.9735877Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-07-24T03:46:05.9736911Z  ) 2025-07-24T03:46:05.9737315Z  2025-07-24T03:46:05.9737695Z  return True 2025-07-24T03:46:05.9738127Z  2025-07-24T03:46:05.9738493Z  2025-07-24T03:46:05.9738877Z def get_runner_prefix( 2025-07-24T03:46:05.9739383Z  rollout_state: str, 2025-07-24T03:46:05.9739924Z  workflow_requestors: Iterable[str], 2025-07-24T03:46:05.9740487Z  branch: str, 2025-07-24T03:46:05.9741063Z  eligible_experiments: frozenset[str] = frozenset(), 2025-07-24T03:46:05.9741794Z  opt_out_experiments: frozenset[str] = frozenset(), 2025-07-24T03:46:05.9742430Z  is_canary: bool = False, 2025-07-24T03:46:05.9742931Z ) -> str: 2025-07-24T03:46:05.9743426Z  settings = parse_settings(rollout_state) 2025-07-24T03:46:05.9744061Z  user_optins = parse_users(rollout_state) 2025-07-24T03:46:05.9744620Z  2025-07-24T03:46:05.9745135Z  fleet_prefix = "" 2025-07-24T03:46:05.9745626Z  prefixes = [] 2025-07-24T03:46:05.9746331Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-07-24T03:46:05.9747482Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-07-24T03:46:05.9748260Z  log.info( 2025-07-24T03:46:05.9749018Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-07-24T03:46:05.9749804Z  ) 2025-07-24T03:46:05.9750258Z  continue 2025-07-24T03:46:05.9750707Z  2025-07-24T03:46:05.9751117Z  if opt_out_experiments: 2025-07-24T03:46:05.9751721Z  if experiment_name in opt_out_experiments: 2025-07-24T03:46:05.9752434Z  opt_out_exp_list = ", ".join(opt_out_experiments) 2025-07-24T03:46:05.9753081Z  log.info( 2025-07-24T03:46:05.9754085Z  f"Skipping experiment '{experiment_name}', as this workflow has opted-out (opted out experiments are: {opt_out_exp_list})" 2025-07-24T03:46:05.9755124Z  ) 2025-07-24T03:46:05.9755613Z  continue 2025-07-24T03:46:05.9756095Z  2025-07-24T03:46:05.9756493Z  if eligible_experiments: 2025-07-24T03:46:05.9757214Z  if experiment_name not in eligible_experiments: 2025-07-24T03:46:05.9757906Z  exp_list = ", ".join(eligible_experiments) 2025-07-24T03:46:05.9758498Z  log.info( 2025-07-24T03:46:05.9759355Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-07-24T03:46:05.9760225Z  ) 2025-07-24T03:46:05.9760820Z  continue 2025-07-24T03:46:05.9761373Z  elif not experiment_settings.default: 2025-07-24T03:46:05.9761955Z  log.info( 2025-07-24T03:46:05.9762687Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-07-24T03:46:05.9763455Z  ) 2025-07-24T03:46:05.9763888Z  continue 2025-07-24T03:46:05.9764333Z  2025-07-24T03:46:05.9764841Z  # Is any workflow_requestor opted out to this experiment? 2025-07-24T03:46:05.9765506Z  opted_out_users = [ 2025-07-24T03:46:05.9766021Z  requestor 2025-07-24T03:46:05.9766556Z  for requestor in workflow_requestors 2025-07-24T03:46:05.9767390Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-07-24T03:46:05.9768064Z  ] 2025-07-24T03:46:05.9768462Z  2025-07-24T03:46:05.9768861Z  if opted_out_users: 2025-07-24T03:46:05.9769388Z  log.info( 2025-07-24T03:46:05.9770091Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-07-24T03:46:05.9770824Z  ) 2025-07-24T03:46:05.9771256Z  continue 2025-07-24T03:46:05.9771708Z  2025-07-24T03:46:05.9772203Z  # Is any workflow_requestor opted in to this experiment? 2025-07-24T03:46:05.9772858Z  opted_in_users = [ 2025-07-24T03:46:05.9773371Z  requestor 2025-07-24T03:46:05.9773907Z  for requestor in workflow_requestors 2025-07-24T03:46:05.9774632Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-07-24T03:46:05.9775290Z  ] 2025-07-24T03:46:05.9775693Z  2025-07-24T03:46:05.9776078Z  enabled = False 2025-07-24T03:46:05.9776584Z  if opted_in_users: 2025-07-24T03:46:05.9777302Z  log.info( 2025-07-24T03:46:05.9778003Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-07-24T03:46:05.9778719Z  ) 2025-07-24T03:46:05.9779160Z  enabled = True 2025-07-24T03:46:05.9779647Z  2025-07-24T03:46:05.9780086Z  elif experiment_settings.rollout_perc: 2025-07-24T03:46:05.9780983Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-07-24T03:46:05.9781986Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-07-24T03:46:05.9782710Z  log.info( 2025-07-24T03:46:05.9783648Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-07-24T03:46:05.9784604Z  ) 2025-07-24T03:46:05.9785084Z  enabled = True 2025-07-24T03:46:05.9785613Z  2025-07-24T03:46:05.9786241Z  if enabled: 2025-07-24T03:46:05.9786859Z  label = experiment_name 2025-07-24T03:46:05.9787484Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-07-24T03:46:05.9788369Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-07-24T03:46:05.9789298Z  # - If it's enabled, then we always list it's prefix first 2025-07-24T03:46:05.9790117Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-07-24T03:46:05.9791067Z  if is_canary: 2025-07-24T03:46:05.9791798Z  label += CANARY_FLEET_SUFFIX 2025-07-24T03:46:05.9792517Z  fleet_prefix = label 2025-07-24T03:46:05.9793378Z  else: 2025-07-24T03:46:05.9794008Z  prefixes.append(label) 2025-07-24T03:46:05.9794604Z  2025-07-24T03:46:05.9795095Z  if len(prefixes) > 1: 2025-07-24T03:46:05.9795711Z  log.error( 2025-07-24T03:46:05.9797205Z  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-24T03:46:05.9799316Z  ) 2025-07-24T03:46:05.9800028Z  prefixes = prefixes[:1] 2025-07-24T03:46:05.9800940Z  2025-07-24T03:46:05.9801634Z  # Fleet always comes first 2025-07-24T03:46:05.9802653Z  if fleet_prefix: 2025-07-24T03:46:05.9803614Z  prefixes.insert(0, fleet_prefix) 2025-07-24T03:46:05.9804688Z  2025-07-24T03:46:05.9805626Z  return ".".join(prefixes) + "." if prefixes else "" 2025-07-24T03:46:05.9807063Z  2025-07-24T03:46:05.9807815Z  2025-07-24T03:46:05.9808858Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-07-24T03:46:05.9809703Z  """ 2025-07-24T03:46:05.9810359Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-07-24T03:46:05.9811103Z  2025-07-24T03:46:05.9811716Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-07-24T03:46:05.9812466Z  """ 2025-07-24T03:46:05.9812913Z  gh = get_gh_client(github_token) 2025-07-24T03:46:05.9813516Z  issue = get_issue(gh, repo, issue_num) 2025-07-24T03:46:05.9814220Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-07-24T03:46:05.9814857Z  2025-07-24T03:46:05.9815222Z  2025-07-24T03:46:05.9815861Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-07-24T03:46:05.9816997Z  for _ in range(num_retries): 2025-07-24T03:46:05.9817557Z  try: 2025-07-24T03:46:05.9818052Z  req = Request(url=url, headers=headers) 2025-07-24T03:46:05.9818779Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-07-24T03:46:05.9819473Z  return json.loads(content) 2025-07-24T03:46:05.9820047Z  except Exception as e: 2025-07-24T03:46:05.9820653Z  log.warning(f"Could not download {url}: {e}") 2025-07-24T03:46:05.9821244Z  2025-07-24T03:46:05.9821859Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-07-24T03:46:05.9822602Z  return {} 2025-07-24T03:46:05.9823030Z  2025-07-24T03:46:05.9823382Z  2025-07-24T03:46:05.9823743Z @cache 2025-07-24T03:46:05.9824426Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-07-24T03:46:05.9825227Z  """ 2025-07-24T03:46:05.9825667Z  Dynamically get PR information 2025-07-24T03:46:05.9826197Z  """ 2025-07-24T03:46:05.9826854Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-07-24T03:46:05.9827519Z  headers = { 2025-07-24T03:46:05.9828051Z  "Accept": "application/vnd.github.v3+json", 2025-07-24T03:46:05.9828708Z  "Authorization": f"token {github_token}", 2025-07-24T03:46:05.9829277Z  } 2025-07-24T03:46:05.9829759Z  json_response: dict[str, Any] = download_json( 2025-07-24T03:46:05.9830424Z  url=f"{github_api}/issues/{pr_number}", 2025-07-24T03:46:05.9831007Z  headers=headers, 2025-07-24T03:46:05.9831484Z  ) 2025-07-24T03:46:05.9831868Z  2025-07-24T03:46:05.9832254Z  if not json_response: 2025-07-24T03:46:05.9832897Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-07-24T03:46:05.9833696Z  return {} 2025-07-24T03:46:05.9834145Z  2025-07-24T03:46:05.9834528Z  return json_response 2025-07-24T03:46:05.9835005Z  2025-07-24T03:46:05.9835354Z  2025-07-24T03:46:05.9835985Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-07-24T03:46:05.9836850Z  """ 2025-07-24T03:46:05.9837436Z  Dynamically get the latest list of labels from the pull request 2025-07-24T03:46:05.9838122Z  """ 2025-07-24T03:46:05.9838657Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-07-24T03:46:05.9839300Z  return { 2025-07-24T03:46:05.9839936Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-07-24T03:46:05.9840653Z  } 2025-07-24T03:46:05.9841037Z  2025-07-24T03:46:05.9841398Z  2025-07-24T03:46:05.9841782Z def main() -> None: 2025-07-24T03:46:05.9842259Z  args = parse_args() 2025-07-24T03:46:05.9842729Z  2025-07-24T03:46:05.9843174Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-07-24T03:46:05.9843744Z  2025-07-24T03:46:05.9844141Z  # Check if the PR is opt-out 2025-07-24T03:46:05.9844678Z  if args.pr_number: 2025-07-24T03:46:05.9845405Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-07-24T03:46:05.9846194Z  if OPT_OUT_LABEL in labels: 2025-07-24T03:46:05.9846827Z  log.info( 2025-07-24T03:46:05.9847594Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-07-24T03:46:05.9848396Z  ) 2025-07-24T03:46:05.9849020Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-24T03:46:05.9849745Z  sys.exit() 2025-07-24T03:46:05.9850336Z  2025-07-24T03:46:05.9850707Z  try: 2025-07-24T03:46:05.9851208Z  rollout_state = get_rollout_state_from_issue( 2025-07-24T03:46:05.9851970Z  args.github_token, args.github_issue_repo, args.github_issue 2025-07-24T03:46:05.9852652Z  ) 2025-07-24T03:46:05.9853049Z  2025-07-24T03:46:05.9853484Z  username = get_potential_pr_author( 2025-07-24T03:46:05.9854063Z  args.github_token, 2025-07-24T03:46:05.9854591Z  args.github_repo, 2025-07-24T03:46:05.9855171Z  args.github_actor, 2025-07-24T03:46:05.9855724Z  args.github_ref_type, 2025-07-24T03:46:05.9856283Z  args.github_branch, 2025-07-24T03:46:05.9856874Z  ) 2025-07-24T03:46:05.9857278Z  2025-07-24T03:46:05.9857796Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-07-24T03:46:05.9858443Z  2025-07-24T03:46:05.9858892Z  runner_label_prefix = get_runner_prefix( 2025-07-24T03:46:05.9859481Z  rollout_state, 2025-07-24T03:46:05.9860037Z  (args.github_issue_owner, username), 2025-07-24T03:46:05.9860619Z  args.github_branch, 2025-07-24T03:46:05.9861178Z  args.eligible_experiments, 2025-07-24T03:46:05.9861762Z  args.opt_out_experiments, 2025-07-24T03:46:05.9862308Z  is_canary, 2025-07-24T03:46:05.9862778Z  ) 2025-07-24T03:46:05.9863179Z  2025-07-24T03:46:05.9863578Z  except Exception as e: 2025-07-24T03:46:05.9864079Z  log.error( 2025-07-24T03:46:05.9864833Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-07-24T03:46:05.9865756Z  ) 2025-07-24T03:46:05.9866162Z  2025-07-24T03:46:05.9866821Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-24T03:46:05.9867521Z  2025-07-24T03:46:05.9867882Z  2025-07-24T03:46:05.9868266Z if __name__ == "__main__": 2025-07-24T03:46:05.9868762Z  main() 2025-07-24T03:46:05.9869160Z  2025-07-24T03:46:05.9869526Z EOF 2025-07-24T03:46:05.9869900Z  2025-07-24T03:46:05.9870305Z cat runner_determinator.py 2025-07-24T03:46:06.0143589Z shell: /usr/bin/bash -e {0} 2025-07-24T03:46:06.0144584Z env: 2025-07-24T03:46:06.0145353Z GITHUB_TOKEN: *** 2025-07-24T03:46:06.0145805Z ISSUE_NUMBER: 5132 2025-07-24T03:46:06.0146282Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-07-24T03:46:06.0146976Z ISSUE_OWNER: 2025-07-24T03:46:06.0147417Z CHECK_EXPERIMENTS: 2025-07-24T03:46:06.0147886Z OPT_OUT_EXPERIMENTS: 2025-07-24T03:46:06.0148371Z PR_NUMBER: 2025-07-24T03:46:06.0148809Z ##[endgroup] 2025-07-24T03:46:06.0364940Z # flake8: noqa: G004 2025-07-24T03:46:06.0365307Z 2025-07-24T03:46:06.0365740Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-07-24T03:46:06.0366961Z # must be kept in sync. You can do it easily by running the following command: 2025-07-24T03:46:06.0367835Z # python .github/scripts/update_runner_determinator.py 2025-07-24T03:46:06.0368310Z 2025-07-24T03:46:06.0368467Z """ 2025-07-24T03:46:06.0369064Z This runner determinator is used to determine which set of runners to run a 2025-07-24T03:46:06.0369973Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-07-24T03:46:06.0370910Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-07-24T03:46:06.0371736Z of which runners should be used to run which job. 2025-07-24T03:46:06.0372172Z 2025-07-24T03:46:06.0372557Z The configuration has two parts, the settings and a list of opted-in users, 2025-07-24T03:46:06.0373676Z separated by a line containing "---". If the line is not present, the 2025-07-24T03:46:06.0374596Z settings are considered to be empty with only the second part, the user 2025-07-24T03:46:06.0375319Z list, defined. 2025-07-24T03:46:06.0375580Z 2025-07-24T03:46:06.0375944Z The first part is a YAML block that defines the rollout settings. This can be 2025-07-24T03:46:06.0377304Z used to define any settings that are needed to determine which runners to use. 2025-07-24T03:46:06.0378168Z It's fields are defined by the RolloutSettings class below. 2025-07-24T03:46:06.0378642Z 2025-07-24T03:46:06.0379020Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-07-24T03:46:06.0379928Z The user list is also a comma separated list of additional features or 2025-07-24T03:46:06.0380690Z experiments which the user could be opted in to. 2025-07-24T03:46:06.0381120Z 2025-07-24T03:46:06.0381330Z The user list has the following rules: 2025-07-24T03:46:06.0381716Z 2025-07-24T03:46:06.0382031Z - Users are GitHub usernames, which must start with the @ prefix 2025-07-24T03:46:06.0382917Z - Each user is also a comma-separated list of features/experiments to enable 2025-07-24T03:46:06.0383706Z - A "#" prefix opts the user out of all experiments 2025-07-24T03:46:06.0384123Z 2025-07-24T03:46:06.0384301Z Example config: 2025-07-24T03:46:06.0384781Z # A list of experiments that can be opted into. 2025-07-24T03:46:06.0385477Z # This defines the behavior they'll induce when opted into. 2025-07-24T03:46:06.0386136Z # Expected syntax is: 2025-07-24T03:46:06.0387029Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-07-24T03:46:06.0388049Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-07-24T03:46:06.0388676Z 2025-07-24T03:46:06.0388855Z experiments: 2025-07-24T03:46:06.0389266Z lf: 2025-07-24T03:46:06.0389685Z rollout_percent: 25 2025-07-24T03:46:06.0390344Z all_branches: false 2025-07-24T03:46:06.0390869Z default: true 2025-07-24T03:46:06.0391309Z --- 2025-07-24T03:46:06.0391543Z 2025-07-24T03:46:06.0391715Z # Opt-ins: 2025-07-24T03:46:06.0392322Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-07-24T03:46:06.0393238Z # and specifying experiments to enable in a comma-separated list. 2025-07-24T03:46:06.0394040Z # To always opt out of an experiment, prefix it with a "-". 2025-07-24T03:46:06.0394728Z # Experiments should be from the above list. 2025-07-24T03:46:06.0395130Z 2025-07-24T03:46:06.0395322Z @User1,-lf,split_build 2025-07-24T03:46:06.0395792Z @User2,lf 2025-07-24T03:46:06.0396214Z @User3,split_build 2025-07-24T03:46:06.0396663Z """ 2025-07-24T03:46:06.0397080Z 2025-07-24T03:46:06.0397257Z import json 2025-07-24T03:46:06.0397660Z import logging 2025-07-24T03:46:06.0398081Z import os 2025-07-24T03:46:06.0398476Z import random 2025-07-24T03:46:06.0398899Z import re 2025-07-24T03:46:06.0399288Z import sys 2025-07-24T03:46:06.0399736Z from argparse import ArgumentParser 2025-07-24T03:46:06.0400308Z from collections.abc import Iterable 2025-07-24T03:46:06.0400853Z from functools import cache 2025-07-24T03:46:06.0401364Z from logging import LogRecord 2025-07-24T03:46:06.0401884Z from typing import Any, NamedTuple 2025-07-24T03:46:06.0402445Z from urllib.request import Request, urlopen 2025-07-24T03:46:06.0402844Z 2025-07-24T03:46:06.0403010Z import yaml 2025-07-24T03:46:06.0403429Z from github import Auth, Github 2025-07-24T03:46:06.0403949Z from github.Issue import Issue 2025-07-24T03:46:06.0404286Z 2025-07-24T03:46:06.0404293Z 2025-07-24T03:46:06.0404510Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-07-24T03:46:06.0405220Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-07-24T03:46:06.0406114Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-07-24T03:46:06.0407018Z 2025-07-24T03:46:06.0407284Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-07-24T03:46:06.0408027Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-07-24T03:46:06.0408582Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-07-24T03:46:06.0409154Z OPT_OUT_LABEL = "no-runner-experiments" 2025-07-24T03:46:06.0409541Z 2025-07-24T03:46:06.0409742Z SETTING_EXPERIMENTS = "experiments" 2025-07-24T03:46:06.0410099Z 2025-07-24T03:46:06.0410295Z LF_FLEET_EXPERIMENT = "lf" 2025-07-24T03:46:06.0410784Z CANARY_FLEET_SUFFIX = ".c" 2025-07-24T03:46:06.0411092Z 2025-07-24T03:46:06.0411106Z 2025-07-24T03:46:06.0411302Z class Experiment(NamedTuple): 2025-07-24T03:46:06.0411820Z rollout_perc: float = ( 2025-07-24T03:46:06.0412496Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-07-24T03:46:06.0413193Z ) 2025-07-24T03:46:06.0413595Z all_branches: bool = ( 2025-07-24T03:46:06.0414259Z False # If True, the experiment is also enabled on the exception branches 2025-07-24T03:46:06.0414962Z ) 2025-07-24T03:46:06.0415362Z default: bool = ( 2025-07-24T03:46:06.0415950Z True # If True, the experiment is enabled by default for all queries 2025-07-24T03:46:06.0416614Z ) 2025-07-24T03:46:06.0417004Z 2025-07-24T03:46:06.0417195Z # Add more fields as needed 2025-07-24T03:46:06.0417538Z 2025-07-24T03:46:06.0417545Z 2025-07-24T03:46:06.0417729Z class Settings(NamedTuple): 2025-07-24T03:46:06.0418202Z """ 2025-07-24T03:46:06.0418680Z Settings for the experiments that can be opted into. 2025-07-24T03:46:06.0419286Z """ 2025-07-24T03:46:06.0419508Z 2025-07-24T03:46:06.0419719Z experiments: dict[str, Experiment] = {} 2025-07-24T03:46:06.0420101Z 2025-07-24T03:46:06.0420114Z 2025-07-24T03:46:06.0420326Z class ColorFormatter(logging.Formatter): 2025-07-24T03:46:06.0420964Z """Color codes the log messages based on the log level""" 2025-07-24T03:46:06.0421424Z 2025-07-24T03:46:06.0421589Z COLORS = { 2025-07-24T03:46:06.0422020Z "WARNING": "\033[33m", # Yellow 2025-07-24T03:46:06.0422691Z "ERROR": "\033[31m", # Red 2025-07-24T03:46:06.0423235Z "CRITICAL": "\033[31m", # Red 2025-07-24T03:46:06.0423768Z "INFO": "\033[0m", # Reset 2025-07-24T03:46:06.0424285Z "DEBUG": "\033[0m", # Reset 2025-07-24T03:46:06.0424783Z } 2025-07-24T03:46:06.0425009Z 2025-07-24T03:46:06.0425234Z def format(self, record: LogRecord) -> str: 2025-07-24T03:46:06.0426008Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-07-24T03:46:06.0427019Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-07-24T03:46:06.0427654Z return super().format(record) 2025-07-24T03:46:06.0428019Z 2025-07-24T03:46:06.0428026Z 2025-07-24T03:46:06.0428224Z handler = logging.StreamHandler() 2025-07-24T03:46:06.0428945Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-07-24T03:46:06.0429516Z 2025-07-24T03:46:06.0429759Z log = logging.getLogger(os.path.basename(__file__)) 2025-07-24T03:46:06.0430375Z log.addHandler(handler) 2025-07-24T03:46:06.0430850Z log.setLevel(logging.INFO) 2025-07-24T03:46:06.0431163Z 2025-07-24T03:46:06.0431171Z 2025-07-24T03:46:06.0431419Z def set_github_output(key: str, value: str) -> None: 2025-07-24T03:46:06.0432000Z """ 2025-07-24T03:46:06.0432521Z Defines outputs of the github action that invokes this script 2025-07-24T03:46:06.0433178Z """ 2025-07-24T03:46:06.0433561Z if not GITHUB_OUTPUT: 2025-07-24T03:46:06.0434646Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-07-24T03:46:06.0435791Z log.warning( 2025-07-24T03:46:06.0436663Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-07-24T03:46:06.0437801Z ) 2025-07-24T03:46:06.0447865Z print(f"::set-output name={key}::{value}") 2025-07-24T03:46:06.0448511Z return 2025-07-24T03:46:06.0448788Z 2025-07-24T03:46:06.0449178Z with open(GITHUB_OUTPUT, "a") as f: 2025-07-24T03:46:06.0449825Z log.info(f"Setting output: {key}='{value}'") 2025-07-24T03:46:06.0450424Z f.write(f"{key}={value}\n") 2025-07-24T03:46:06.0450782Z 2025-07-24T03:46:06.0450788Z 2025-07-24T03:46:06.0451091Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-07-24T03:46:06.0451754Z return frozenset( 2025-07-24T03:46:06.0452390Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-07-24T03:46:06.0453103Z ) 2025-07-24T03:46:06.0453323Z 2025-07-24T03:46:06.0453330Z 2025-07-24T03:46:06.0453515Z def parse_args() -> Any: 2025-07-24T03:46:06.0454107Z parser = ArgumentParser("Get dynamic rollout settings") 2025-07-24T03:46:06.0454992Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-07-24T03:46:06.0455778Z parser.add_argument( 2025-07-24T03:46:06.0456271Z "--github-issue-repo", 2025-07-24T03:46:06.0456965Z type=str, 2025-07-24T03:46:06.0457412Z required=False, 2025-07-24T03:46:06.0457891Z default="pytorch/test-infra", 2025-07-24T03:46:06.0458464Z help="GitHub repo to get the issue", 2025-07-24T03:46:06.0459002Z ) 2025-07-24T03:46:06.0459404Z parser.add_argument( 2025-07-24T03:46:06.0459887Z "--github-repo", 2025-07-24T03:46:06.0460352Z type=str, 2025-07-24T03:46:06.0460769Z required=True, 2025-07-24T03:46:06.0461253Z help="GitHub repo where CI is running", 2025-07-24T03:46:06.0461799Z ) 2025-07-24T03:46:06.0462188Z parser.add_argument( 2025-07-24T03:46:06.0462826Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-07-24T03:46:06.0463501Z ) 2025-07-24T03:46:06.0463922Z parser.add_argument( 2025-07-24T03:46:06.0464562Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-07-24T03:46:06.0465265Z ) 2025-07-24T03:46:06.0465652Z parser.add_argument( 2025-07-24T03:46:06.0466465Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-07-24T03:46:06.0467458Z ) 2025-07-24T03:46:06.0467857Z parser.add_argument( 2025-07-24T03:46:06.0468532Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-07-24T03:46:06.0469271Z ) 2025-07-24T03:46:06.0469671Z parser.add_argument( 2025-07-24T03:46:06.0470148Z "--github-ref-type", 2025-07-24T03:46:06.0470628Z type=str, 2025-07-24T03:46:06.0471048Z required=True, 2025-07-24T03:46:06.0471552Z help="Current GitHub ref type, branch or tag", 2025-07-24T03:46:06.0472117Z ) 2025-07-24T03:46:06.0472512Z parser.add_argument( 2025-07-24T03:46:06.0472997Z "--eligible-experiments", 2025-07-24T03:46:06.0473538Z type=_str_comma_separated_to_set, 2025-07-24T03:46:06.0523553Z required=False, 2025-07-24T03:46:06.0524479Z default="", 2025-07-24T03:46:06.0525713Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-07-24T03:46:06.0526887Z ) 2025-07-24T03:46:06.0527346Z parser.add_argument( 2025-07-24T03:46:06.0527841Z "--opt-out-experiments", 2025-07-24T03:46:06.0528379Z type=_str_comma_separated_to_set, 2025-07-24T03:46:06.0528917Z required=False, 2025-07-24T03:46:06.0529360Z default="", 2025-07-24T03:46:06.0529779Z help=( 2025-07-24T03:46:06.0530479Z "comma separated list of experiments to opt-out of. If unset, no opt-outs will occur. " 2025-07-24T03:46:06.0531641Z "If the same experiment is listed both here and in '--eligible-experiments' opt-out will take priority." 2025-07-24T03:46:06.0532492Z ), 2025-07-24T03:46:06.0532880Z ) 2025-07-24T03:46:06.0533274Z parser.add_argument( 2025-07-24T03:46:06.0533749Z "--pr-number", 2025-07-24T03:46:06.0534185Z type=str, 2025-07-24T03:46:06.0534613Z required=False, 2025-07-24T03:46:06.0535071Z default="", 2025-07-24T03:46:06.0535789Z help="the optional PR number where this is run", 2025-07-24T03:46:06.0536397Z ) 2025-07-24T03:46:06.0536627Z 2025-07-24T03:46:06.0537015Z return parser.parse_args() 2025-07-24T03:46:06.0537348Z 2025-07-24T03:46:06.0537355Z 2025-07-24T03:46:06.0537765Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-07-24T03:46:06.0538540Z auth = Auth.Token(github_token) 2025-07-24T03:46:06.0539081Z return Github(auth=auth) 2025-07-24T03:46:06.0539403Z 2025-07-24T03:46:06.0539410Z 2025-07-24T03:46:06.0539864Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-07-24T03:46:06.0540674Z repo = gh.get_repo(repo) 2025-07-24T03:46:06.0541206Z return repo.get_issue(number=issue_num) 2025-07-24T03:46:06.0541590Z 2025-07-24T03:46:06.0541596Z 2025-07-24T03:46:06.0541787Z def get_potential_pr_author( 2025-07-24T03:46:06.0542458Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-07-24T03:46:06.0543146Z ) -> str: 2025-07-24T03:46:06.0543679Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-07-24T03:46:06.0544500Z # Fetch the actual username from the original PR. The PR number is 2025-07-24T03:46:06.0545252Z # embedded in the tag name: ciflow// 2025-07-24T03:46:06.0545683Z 2025-07-24T03:46:06.0545885Z gh = get_gh_client(github_token) 2025-07-24T03:46:06.0546241Z 2025-07-24T03:46:06.0546504Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-07-24T03:46:06.0547383Z split_tag = ref_name.split("/") 2025-07-24T03:46:06.0547920Z if ( 2025-07-24T03:46:06.0548344Z len(split_tag) == 3 2025-07-24T03:46:06.0548840Z and split_tag[0] == "ciflow" 2025-07-24T03:46:06.0549396Z and split_tag[2].isnumeric() 2025-07-24T03:46:06.0549917Z ): 2025-07-24T03:46:06.0550328Z pr_number = split_tag[2] 2025-07-24T03:46:06.0551001Z try: 2025-07-24T03:46:06.0551450Z repository = gh.get_repo(repo) 2025-07-24T03:46:06.0552087Z pull = repository.get_pull(number=int(pr_number)) 2025-07-24T03:46:06.0552726Z except Exception as e: 2025-07-24T03:46:06.0553280Z raise Exception( # noqa: TRY002 2025-07-24T03:46:06.0553974Z f"issue with pull request {pr_number} from repo {repository}" 2025-07-24T03:46:06.0554653Z ) from e 2025-07-24T03:46:06.0555217Z return pull.user.login # type: ignore[no-any-return] 2025-07-24T03:46:06.0555935Z # In all other cases, return the original input username 2025-07-24T03:46:06.0556551Z return username 2025-07-24T03:46:06.0556992Z 2025-07-24T03:46:06.0557000Z 2025-07-24T03:46:06.0557243Z def is_exception_branch(branch: str) -> bool: 2025-07-24T03:46:06.0557816Z """ 2025-07-24T03:46:06.0558480Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-07-24T03:46:06.0559292Z """ 2025-07-24T03:46:06.0559865Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-07-24T03:46:06.0560404Z 2025-07-24T03:46:06.0560411Z 2025-07-24T03:46:06.0560611Z def load_yaml(yaml_text: str) -> Any: 2025-07-24T03:46:06.0561141Z try: 2025-07-24T03:46:06.0561543Z data = yaml.safe_load(yaml_text) 2025-07-24T03:46:06.0562080Z return data 2025-07-24T03:46:06.0562513Z except yaml.YAMLError: 2025-07-24T03:46:06.0563024Z log.exception("Error loading YAML") 2025-07-24T03:46:06.0563557Z raise 2025-07-24T03:46:06.0563841Z 2025-07-24T03:46:06.0563848Z 2025-07-24T03:46:06.0564270Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-07-24T03:46:06.0565034Z """ 2025-07-24T03:46:06.0565660Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-07-24T03:46:06.0566277Z 2025-07-24T03:46:06.0566945Z If the issue body contains "---" then the text above that is the settings 2025-07-24T03:46:06.0567792Z and the text below is the list of opted in users. 2025-07-24T03:46:06.0568232Z 2025-07-24T03:46:06.0568616Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-07-24T03:46:06.0569342Z """ 2025-07-24T03:46:06.0569813Z rollout_state_parts = rollout_state.split("---") 2025-07-24T03:46:06.0570458Z if len(rollout_state_parts) >= 2: 2025-07-24T03:46:06.0571087Z return rollout_state_parts[0], rollout_state_parts[1] 2025-07-24T03:46:06.0571706Z else: 2025-07-24T03:46:06.0572107Z return "", rollout_state 2025-07-24T03:46:06.0572444Z 2025-07-24T03:46:06.0572452Z 2025-07-24T03:46:06.0572655Z class UserOptins(dict[str, list[str]]): 2025-07-24T03:46:06.0573182Z """ 2025-07-24T03:46:06.0573726Z Dictionary of users with a list of features they have opted into 2025-07-24T03:46:06.0574395Z """ 2025-07-24T03:46:06.0574621Z 2025-07-24T03:46:06.0574628Z 2025-07-24T03:46:06.0574967Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-07-24T03:46:06.0575647Z """ 2025-07-24T03:46:06.0576371Z Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-07-24T03:46:06.0577267Z 2025-07-24T03:46:06.0577880Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-07-24T03:46:06.0578904Z - Example line: "@User1,lf,split_build" 2025-07-24T03:46:06.0579604Z - A "#" prefix indicates the user is opted out of all experiments 2025-07-24T03:46:06.0580100Z 2025-07-24T03:46:06.0580107Z 2025-07-24T03:46:06.0580276Z """ 2025-07-24T03:46:06.0580668Z optins = UserOptins() 2025-07-24T03:46:06.0581182Z for user in user_optin_text.split("\n"): 2025-07-24T03:46:06.0581751Z user = user.strip("\r\n\t -") 2025-07-24T03:46:06.0582319Z if not user or not user.startswith("@"): 2025-07-24T03:46:06.0583046Z # Not a valid user. Skip 2025-07-24T03:46:06.0583565Z continue 2025-07-24T03:46:06.0583830Z 2025-07-24T03:46:06.0583999Z if user: 2025-07-24T03:46:06.0584460Z usr_name = user.split(",")[0].strip("@") 2025-07-24T03:46:06.0585186Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-07-24T03:46:06.0585706Z 2025-07-24T03:46:06.0585878Z return optins 2025-07-24T03:46:06.0586137Z 2025-07-24T03:46:06.0586144Z 2025-07-24T03:46:06.0586425Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-07-24T03:46:06.0587292Z """ 2025-07-24T03:46:06.0587714Z Check if the experiment name is valid. 2025-07-24T03:46:06.0588270Z A valid name: 2025-07-24T03:46:06.0588921Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-07-24T03:46:06.0589910Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-07-24T03:46:06.0590777Z - Cannot contain spaces 2025-07-24T03:46:06.0591323Z """ 2025-07-24T03:46:06.0591543Z 2025-07-24T03:46:06.0591804Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-07-24T03:46:06.0592533Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-07-24T03:46:06.0592998Z 2025-07-24T03:46:06.0593172Z if valid: 2025-07-24T03:46:06.0593573Z return True 2025-07-24T03:46:06.0593834Z 2025-07-24T03:46:06.0594006Z log.error( 2025-07-24T03:46:06.0595470Z 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-24T03:46:06.0597207Z ) 2025-07-24T03:46:06.0597585Z return False 2025-07-24T03:46:06.0597854Z 2025-07-24T03:46:06.0597861Z 2025-07-24T03:46:06.0598161Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-07-24T03:46:06.0598819Z """ 2025-07-24T03:46:06.0599552Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-07-24T03:46:06.0600308Z """ 2025-07-24T03:46:06.0600686Z try: 2025-07-24T03:46:06.0601074Z if settings_text: 2025-07-24T03:46:06.0601815Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-07-24T03:46:06.0602625Z # for easy reading 2025-07-24T03:46:06.0603430Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-07-24T03:46:06.0604333Z # the backtick character in shell commands. 2025-07-24T03:46:06.0604966Z backtick = chr(96) # backtick character 2025-07-24T03:46:06.0605649Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-07-24T03:46:06.0606342Z settings = load_yaml(settings_text) 2025-07-24T03:46:06.0606912Z 2025-07-24T03:46:06.0607335Z # For now we just load experiments. We can expand this if/when we add more settings 2025-07-24T03:46:06.0608132Z experiments = {} 2025-07-24T03:46:06.0608456Z 2025-07-24T03:46:06.0608842Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-07-24T03:46:06.0609629Z if not is_valid_experiment_name(exp_name): 2025-07-24T03:46:06.0610751Z # 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-24T03:46:06.0611814Z continue 2025-07-24T03:46:06.0612125Z 2025-07-24T03:46:06.0612305Z valid_settings = {} 2025-07-24T03:46:06.0612853Z for setting in exp_settings: 2025-07-24T03:46:06.0613443Z if setting not in Experiment._fields: 2025-07-24T03:46:06.0614028Z log.warning( 2025-07-24T03:46:06.0614740Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-07-24T03:46:06.0615638Z ) 2025-07-24T03:46:06.0616084Z else: 2025-07-24T03:46:06.0616619Z valid_settings[setting] = exp_settings[setting] 2025-07-24T03:46:06.0617251Z 2025-07-24T03:46:06.0617531Z experiments[exp_name] = Experiment(**valid_settings) 2025-07-24T03:46:06.0618198Z return Settings(experiments) 2025-07-24T03:46:06.0618566Z 2025-07-24T03:46:06.0618752Z except Exception: 2025-07-24T03:46:06.0619253Z log.exception("Failed to parse settings") 2025-07-24T03:46:06.0619656Z 2025-07-24T03:46:06.0619838Z return Settings() 2025-07-24T03:46:06.0620113Z 2025-07-24T03:46:06.0620121Z 2025-07-24T03:46:06.0620365Z def parse_settings(rollout_state: str) -> Settings: 2025-07-24T03:46:06.0620973Z """ 2025-07-24T03:46:06.0621432Z Parse settings, if any, from the rollout state. 2025-07-24T03:46:06.0621860Z 2025-07-24T03:46:06.0622204Z If the issue body contains "---" then the text above that is the settings 2025-07-24T03:46:06.0622993Z and the text below is the list of opted in users. 2025-07-24T03:46:06.0623422Z 2025-07-24T03:46:06.0623824Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-07-24T03:46:06.0624593Z """ 2025-07-24T03:46:06.0625167Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-24T03:46:06.0625952Z return parse_settings_from_text(settings_text) 2025-07-24T03:46:06.0626377Z 2025-07-24T03:46:06.0626385Z 2025-07-24T03:46:06.0626631Z def parse_users(rollout_state: str) -> UserOptins: 2025-07-24T03:46:06.0627440Z """ 2025-07-24T03:46:06.0627857Z Parse users from the rollout state. 2025-07-24T03:46:06.0628235Z 2025-07-24T03:46:06.0628396Z """ 2025-07-24T03:46:06.0628948Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-24T03:46:06.0629718Z return parse_user_opt_in_from_text(users_text) 2025-07-24T03:46:06.0630154Z 2025-07-24T03:46:06.0630161Z 2025-07-24T03:46:06.0630703Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-24T03:46:06.0631496Z """ 2025-07-24T03:46:06.0631938Z Check if a user is opted into an experiment 2025-07-24T03:46:06.0632500Z """ 2025-07-24T03:46:06.0632978Z return experiment_name in user_optins.get(user, []) 2025-07-24T03:46:06.0633434Z 2025-07-24T03:46:06.0633441Z 2025-07-24T03:46:06.0633869Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-24T03:46:06.0634744Z """ 2025-07-24T03:46:06.0635226Z Check if a user explicitly opted out of an experiment 2025-07-24T03:46:06.0635831Z """ 2025-07-24T03:46:06.0636349Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-07-24T03:46:06.0637290Z experiment_optout = "-" + experiment_name 2025-07-24T03:46:06.0637961Z if experiment_optout not in user_optins.get(user, []): 2025-07-24T03:46:06.0638603Z return False 2025-07-24T03:46:06.0638879Z 2025-07-24T03:46:06.0639159Z if is_user_opted_in(user, user_optins, experiment_name): 2025-07-24T03:46:06.0639782Z log.warning( 2025-07-24T03:46:06.0640592Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-07-24T03:46:06.0641505Z ) 2025-07-24T03:46:06.0641732Z 2025-07-24T03:46:06.0641905Z return True 2025-07-24T03:46:06.0642164Z 2025-07-24T03:46:06.0642170Z 2025-07-24T03:46:06.0642346Z def get_runner_prefix( 2025-07-24T03:46:06.0642807Z rollout_state: str, 2025-07-24T03:46:06.0643284Z workflow_requestors: Iterable[str], 2025-07-24T03:46:06.0643822Z branch: str, 2025-07-24T03:46:06.0644328Z eligible_experiments: frozenset[str] = frozenset(), 2025-07-24T03:46:06.0645017Z opt_out_experiments: frozenset[str] = frozenset(), 2025-07-24T03:46:06.0645627Z is_canary: bool = False, 2025-07-24T03:46:06.0646103Z ) -> str: 2025-07-24T03:46:06.0646799Z settings = parse_settings(rollout_state) 2025-07-24T03:46:06.0647410Z user_optins = parse_users(rollout_state) 2025-07-24T03:46:06.0647797Z 2025-07-24T03:46:06.0647976Z fleet_prefix = "" 2025-07-24T03:46:06.0648412Z prefixes = [] 2025-07-24T03:46:06.0649058Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-07-24T03:46:06.0650008Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-07-24T03:46:06.0650744Z log.info( 2025-07-24T03:46:06.0651445Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-07-24T03:46:06.0652214Z ) 2025-07-24T03:46:06.0652616Z continue 2025-07-24T03:46:06.0652878Z 2025-07-24T03:46:06.0653067Z if opt_out_experiments: 2025-07-24T03:46:06.0653634Z if experiment_name in opt_out_experiments: 2025-07-24T03:46:06.0654301Z opt_out_exp_list = ", ".join(opt_out_experiments) 2025-07-24T03:46:06.0654923Z log.info( 2025-07-24T03:46:06.0655866Z f"Skipping experiment '{experiment_name}', as this workflow has opted-out (opted out experiments are: {opt_out_exp_list})" 2025-07-24T03:46:06.0656956Z ) 2025-07-24T03:46:06.0657366Z continue 2025-07-24T03:46:06.0657658Z 2025-07-24T03:46:06.0657852Z if eligible_experiments: 2025-07-24T03:46:06.0658460Z if experiment_name not in eligible_experiments: 2025-07-24T03:46:06.0659116Z exp_list = ", ".join(eligible_experiments) 2025-07-24T03:46:06.0659696Z log.info( 2025-07-24T03:46:06.0660495Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-07-24T03:46:06.0661343Z ) 2025-07-24T03:46:06.0661764Z continue 2025-07-24T03:46:06.0662248Z elif not experiment_settings.default: 2025-07-24T03:46:06.0662806Z log.info( 2025-07-24T03:46:06.0663604Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-07-24T03:46:06.0664415Z ) 2025-07-24T03:46:06.0664814Z continue 2025-07-24T03:46:06.0665088Z 2025-07-24T03:46:06.0665362Z # Is any workflow_requestor opted out to this experiment? 2025-07-24T03:46:06.0665996Z opted_out_users = [ 2025-07-24T03:46:06.0666461Z requestor 2025-07-24T03:46:06.0667091Z for requestor in workflow_requestors 2025-07-24T03:46:06.0667775Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-07-24T03:46:06.0668429Z ] 2025-07-24T03:46:06.0668654Z 2025-07-24T03:46:06.0668832Z if opted_out_users: 2025-07-24T03:46:06.0669309Z log.info( 2025-07-24T03:46:06.0669933Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-07-24T03:46:06.0670643Z ) 2025-07-24T03:46:06.0671037Z continue 2025-07-24T03:46:06.0671316Z 2025-07-24T03:46:06.0671605Z # Is any workflow_requestor opted in to this experiment? 2025-07-24T03:46:06.0672253Z opted_in_users = [ 2025-07-24T03:46:06.0672722Z requestor 2025-07-24T03:46:06.0673196Z for requestor in workflow_requestors 2025-07-24T03:46:06.0673879Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-07-24T03:46:06.0674515Z ] 2025-07-24T03:46:06.0674733Z 2025-07-24T03:46:06.0674907Z enabled = False 2025-07-24T03:46:06.0675359Z if opted_in_users: 2025-07-24T03:46:06.0675822Z log.info( 2025-07-24T03:46:06.0676439Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-07-24T03:46:06.0677253Z ) 2025-07-24T03:46:06.0677659Z enabled = True 2025-07-24T03:46:06.0677959Z 2025-07-24T03:46:06.0678175Z elif experiment_settings.rollout_perc: 2025-07-24T03:46:06.0679023Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-07-24T03:46:06.0680157Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-07-24T03:46:06.0680830Z log.info( 2025-07-24T03:46:06.0681702Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-07-24T03:46:06.0682659Z ) 2025-07-24T03:46:06.0683077Z enabled = True 2025-07-24T03:46:06.0683400Z 2025-07-24T03:46:06.0683566Z if enabled: 2025-07-24T03:46:06.0684001Z label = experiment_name 2025-07-24T03:46:06.0684582Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-07-24T03:46:06.0685451Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-07-24T03:46:06.0686365Z # - If it's enabled, then we always list it's prefix first 2025-07-24T03:46:06.0687275Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-07-24T03:46:06.0687975Z if is_canary: 2025-07-24T03:46:06.0688489Z label += CANARY_FLEET_SUFFIX 2025-07-24T03:46:06.0689052Z fleet_prefix = label 2025-07-24T03:46:06.0689561Z else: 2025-07-24T03:46:06.0690002Z prefixes.append(label) 2025-07-24T03:46:06.0690371Z 2025-07-24T03:46:06.0690555Z if len(prefixes) > 1: 2025-07-24T03:46:06.0691020Z log.error( 2025-07-24T03:46:06.0692080Z 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-24T03:46:06.0693234Z ) 2025-07-24T03:46:06.0693634Z prefixes = prefixes[:1] 2025-07-24T03:46:06.0693970Z 2025-07-24T03:46:06.0694155Z # Fleet always comes first 2025-07-24T03:46:06.0694634Z if fleet_prefix: 2025-07-24T03:46:06.0695105Z prefixes.insert(0, fleet_prefix) 2025-07-24T03:46:06.0695498Z 2025-07-24T03:46:06.0695876Z return ".".join(prefixes) + "." if prefixes else "" 2025-07-24T03:46:06.0696321Z 2025-07-24T03:46:06.0696328Z 2025-07-24T03:46:06.0696867Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-07-24T03:46:06.0697680Z """ 2025-07-24T03:46:06.0698280Z Gets the first comment of the issue, which contains the desired rollout state. 2025-07-24T03:46:06.0698878Z 2025-07-24T03:46:06.0699262Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-07-24T03:46:06.0699989Z """ 2025-07-24T03:46:06.0700393Z gh = get_gh_client(github_token) 2025-07-24T03:46:06.0700969Z issue = get_issue(gh, repo, issue_num) 2025-07-24T03:46:06.0701613Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-07-24T03:46:06.0702074Z 2025-07-24T03:46:06.0702086Z 2025-07-24T03:46:06.0702487Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-07-24T03:46:06.0703266Z for _ in range(num_retries): 2025-07-24T03:46:06.0703763Z try: 2025-07-24T03:46:06.0704201Z req = Request(url=url, headers=headers) 2025-07-24T03:46:06.0704894Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-07-24T03:46:06.0705558Z return json.loads(content) 2025-07-24T03:46:06.0706108Z except Exception as e: 2025-07-24T03:46:06.0706668Z log.warning(f"Could not download {url}: {e}") 2025-07-24T03:46:06.0707260Z 2025-07-24T03:46:06.0707639Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-07-24T03:46:06.0708379Z return {} 2025-07-24T03:46:06.0708616Z 2025-07-24T03:46:06.0708623Z 2025-07-24T03:46:06.0708788Z @cache 2025-07-24T03:46:06.0709417Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-07-24T03:46:06.0710194Z """ 2025-07-24T03:46:06.0710602Z Dynamically get PR information 2025-07-24T03:46:06.0711249Z """ 2025-07-24T03:46:06.0711766Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-07-24T03:46:06.0712418Z headers = { 2025-07-24T03:46:06.0712890Z "Accept": "application/vnd.github.v3+json", 2025-07-24T03:46:06.0713525Z "Authorization": f"token {github_token}", 2025-07-24T03:46:06.0714078Z } 2025-07-24T03:46:06.0715122Z json_response: dict[str, Any] = download_json( 2025-07-24T03:46:06.0715758Z url=f"{github_api}/issues/{pr_number}", 2025-07-24T03:46:06.0716329Z headers=headers, 2025-07-24T03:46:06.0717172Z ) 2025-07-24T03:46:06.0717399Z 2025-07-24T03:46:06.0717585Z if not json_response: 2025-07-24T03:46:06.0718171Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-07-24T03:46:06.0719091Z return {} 2025-07-24T03:46:06.0719352Z 2025-07-24T03:46:06.0719801Z return json_response 2025-07-24T03:46:06.0720125Z 2025-07-24T03:46:06.0720132Z 2025-07-24T03:46:06.0720535Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-07-24T03:46:06.0721289Z """ 2025-07-24T03:46:06.0721831Z Dynamically get the latest list of labels from the pull request 2025-07-24T03:46:06.0722517Z """ 2025-07-24T03:46:06.0723029Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-07-24T03:46:06.0723669Z return { 2025-07-24T03:46:06.0724285Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-07-24T03:46:06.0725011Z } 2025-07-24T03:46:06.0725230Z 2025-07-24T03:46:06.0725237Z 2025-07-24T03:46:06.0725413Z def main() -> None: 2025-07-24T03:46:06.0725848Z args = parse_args() 2025-07-24T03:46:06.0726135Z 2025-07-24T03:46:06.0726358Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-07-24T03:46:06.0726987Z 2025-07-24T03:46:06.0727237Z # Check if the PR is opt-out 2025-07-24T03:46:06.0727760Z if args.pr_number: 2025-07-24T03:46:06.0728445Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-07-24T03:46:06.0729394Z if OPT_OUT_LABEL in labels: 2025-07-24T03:46:06.0729935Z log.info( 2025-07-24T03:46:06.0730644Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-07-24T03:46:06.0731432Z ) 2025-07-24T03:46:06.0731994Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-24T03:46:06.0732685Z sys.exit() 2025-07-24T03:46:06.0732967Z 2025-07-24T03:46:06.0733130Z try: 2025-07-24T03:46:06.0733579Z rollout_state = get_rollout_state_from_issue( 2025-07-24T03:46:06.0734314Z args.github_token, args.github_issue_repo, args.github_issue 2025-07-24T03:46:06.0734970Z ) 2025-07-24T03:46:06.0735199Z 2025-07-24T03:46:06.0735404Z username = get_potential_pr_author( 2025-07-24T03:46:06.0735969Z args.github_token, 2025-07-24T03:46:06.0736472Z args.github_repo, 2025-07-24T03:46:06.0737195Z args.github_actor, 2025-07-24T03:46:06.0737703Z args.github_ref_type, 2025-07-24T03:46:06.0738225Z args.github_branch, 2025-07-24T03:46:06.0738714Z ) 2025-07-24T03:46:06.0738940Z 2025-07-24T03:46:06.0739223Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-07-24T03:46:06.0739692Z 2025-07-24T03:46:06.0739908Z runner_label_prefix = get_runner_prefix( 2025-07-24T03:46:06.0740494Z rollout_state, 2025-07-24T03:46:06.0740999Z (args.github_issue_owner, username), 2025-07-24T03:46:06.0741575Z args.github_branch, 2025-07-24T03:46:06.0742097Z args.eligible_experiments, 2025-07-24T03:46:06.0742649Z args.opt_out_experiments, 2025-07-24T03:46:06.0743174Z is_canary, 2025-07-24T03:46:06.0743600Z ) 2025-07-24T03:46:06.0743828Z 2025-07-24T03:46:06.0744013Z except Exception as e: 2025-07-24T03:46:06.0744481Z log.error( 2025-07-24T03:46:06.0745172Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-07-24T03:46:06.0746112Z ) 2025-07-24T03:46:06.0746338Z 2025-07-24T03:46:06.0746665Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-24T03:46:06.0747298Z 2025-07-24T03:46:06.0747312Z 2025-07-24T03:46:06.0747494Z if __name__ == "__main__": 2025-07-24T03:46:06.0747950Z main() 2025-07-24T03:46:06.0748185Z 2025-07-24T03:46:06.0837823Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-07-24T03:46:06.0838712Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-07-24T03:46:06.0867363Z shell: /usr/bin/bash -e {0} 2025-07-24T03:46:06.0867834Z env: 2025-07-24T03:46:06.0868461Z GITHUB_TOKEN: *** 2025-07-24T03:46:06.0868884Z ISSUE_NUMBER: 5132 2025-07-24T03:46:06.0869319Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-07-24T03:46:06.0869833Z ISSUE_OWNER: 2025-07-24T03:46:06.0870229Z CHECK_EXPERIMENTS: 2025-07-24T03:46:06.0870647Z OPT_OUT_EXPERIMENTS: 2025-07-24T03:46:06.0871081Z PR_NUMBER: 2025-07-24T03:46:06.0871460Z ##[endgroup] 2025-07-24T03:46:07.4732733Z Defaulting to user installation because normal site-packages is not writeable 2025-07-24T03:46:08.3392795Z Collecting urllib3==1.26.18 2025-07-24T03:46:08.4070109Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-07-24T03:46:08.4318306Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 3.5 MB/s eta 0:00:00 2025-07-24T03:46:08.4617095Z Collecting PyGithub==2.3.0 2025-07-24T03:46:08.4743819Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-07-24T03:46:08.5271494Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-07-24T03:46:08.5373203Z 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-24T03:46:08.5426080Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-07-24T03:46:08.5444526Z 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-24T03:46:08.5460107Z Requirement already satisfied: typing-extensions>=4.0.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (4.10.0) 2025-07-24T03:46:08.5793046Z Collecting Deprecated (from PyGithub==2.3.0) 2025-07-24T03:46:08.5902541Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl.metadata (5.7 kB) 2025-07-24T03:46:08.6130885Z 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-24T03:46:08.7405936Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-07-24T03:46:08.7524319Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-07-24T03:46:08.8654248Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-07-24T03:46:08.8784976Z 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-24T03:46:08.9040932Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-07-24T03:46:08.9145766Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-07-24T03:46:08.9443551Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-07-24T03:46:08.9630422Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 7.9 MB/s eta 0:00:00 2025-07-24T03:46:08.9739083Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-07-24T03:46:09.0083084Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 10.8 MB/s eta 0:00:00 2025-07-24T03:46:09.0185482Z 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-24T03:46:09.0829376Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 13.6 MB/s eta 0:00:00 2025-07-24T03:46:09.0936154Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl (10.0 kB) 2025-07-24T03:46:09.1059460Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-07-24T03:46:09.1353301Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 17.0 MB/s eta 0:00:00 2025-07-24T03:46:09.1460154Z 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-24T03:46:09.1516251Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 19.8 MB/s eta 0:00:00 2025-07-24T03:46:09.1617636Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-07-24T03:46:09.1699203Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 16.4 MB/s eta 0:00:00 2025-07-24T03:46:09.4934838Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-07-24T03:46:10.0310994Z 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-24T03:46:10.1161527Z ##[group]Run curr_branch="ciflow/trunk/149961" 2025-07-24T03:46:10.1161923Z curr_branch="ciflow/trunk/149961" 2025-07-24T03:46:10.1162188Z curr_ref_type="tag" 2025-07-24T03:46:10.1162435Z echo "Current branch is '$curr_branch'" 2025-07-24T03:46:10.1162691Z  2025-07-24T03:46:10.1162878Z python3 runner_determinator.py \ 2025-07-24T03:46:10.1163160Z  --github-token "$GITHUB_TOKEN" \ 2025-07-24T03:46:10.1163425Z  --github-issue "$ISSUE_NUMBER" \ 2025-07-24T03:46:10.1163697Z  --github-branch "$curr_branch" \ 2025-07-24T03:46:10.1163965Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-07-24T03:46:10.1164249Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-07-24T03:46:10.1164534Z  --github-ref-type "$curr_ref_type" \ 2025-07-24T03:46:10.1164806Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-07-24T03:46:10.1165139Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-07-24T03:46:10.1165462Z  --opt-out-experiments "$OPT_OUT_EXPERIMENTS" \ 2025-07-24T03:46:10.1165763Z  --pr-number "${PR_NUMBER}" 2025-07-24T03:46:10.1194484Z shell: /usr/bin/bash -e {0} 2025-07-24T03:46:10.1194711Z env: 2025-07-24T03:46:10.1195262Z GITHUB_TOKEN: *** 2025-07-24T03:46:10.1195458Z ISSUE_NUMBER: 5132 2025-07-24T03:46:10.1195667Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-07-24T03:46:10.1195894Z ISSUE_OWNER: 2025-07-24T03:46:10.1196075Z CHECK_EXPERIMENTS: 2025-07-24T03:46:10.1196270Z OPT_OUT_EXPERIMENTS: 2025-07-24T03:46:10.1196458Z PR_NUMBER: 2025-07-24T03:46:10.1196626Z ##[endgroup] 2025-07-24T03:46:10.1244118Z Current branch is 'ciflow/trunk/149961' 2025-07-24T03:46:12.8077158Z INFO : Setting output: label-type='' 2025-07-24T03:46:12.8403504Z Evaluate and set job outputs 2025-07-24T03:46:12.8410652Z Cleaning up orphan processes