2025-07-24T03:40:54.2073278Z Current runner version: '2.326.0' 2025-07-24T03:40:54.2100160Z ##[group]Runner Image Provisioner 2025-07-24T03:40:54.2101558Z Hosted Compute Agent 2025-07-24T03:40:54.2102523Z Version: 20250711.363 2025-07-24T03:40:54.2103505Z Commit: 6785254374ce925a23743850c1cb91912ce5c14c 2025-07-24T03:40:54.2104763Z Build Date: 2025-07-11T20:04:25Z 2025-07-24T03:40:54.2106064Z ##[endgroup] 2025-07-24T03:40:54.2107212Z ##[group]Operating System 2025-07-24T03:40:54.2108458Z Ubuntu 2025-07-24T03:40:54.2109417Z 24.04.2 2025-07-24T03:40:54.2110198Z LTS 2025-07-24T03:40:54.2110920Z ##[endgroup] 2025-07-24T03:40:54.2111965Z ##[group]Runner Image 2025-07-24T03:40:54.2112872Z Image: ubuntu-24.04 2025-07-24T03:40:54.2113820Z Version: 20250720.1.0 2025-07-24T03:40:54.2115954Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250720.1/images/ubuntu/Ubuntu2404-Readme.md 2025-07-24T03:40:54.2118632Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250720.1 2025-07-24T03:40:54.2120317Z ##[endgroup] 2025-07-24T03:40:54.2121845Z ##[group]GITHUB_TOKEN Permissions 2025-07-24T03:40:54.2125576Z Metadata: read 2025-07-24T03:40:54.2126423Z ##[endgroup] 2025-07-24T03:40:54.2129385Z Secret source: Actions 2025-07-24T03:40:54.2131043Z Prepare workflow directory 2025-07-24T03:40:54.2761423Z Prepare all required actions 2025-07-24T03:40:54.2842051Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (07df6ba7f5597488a93b3855d52d2ead55675125) 2025-07-24T03:40:54.2849404Z ##[group] Inputs 2025-07-24T03:40:54.2850248Z check_experiments: 2025-07-24T03:40:54.2851253Z opt_out_experiments: 2025-07-24T03:40:54.2852142Z triggering_actor: pytorch-bot[bot] 2025-07-24T03:40:54.2853090Z issue_owner: 2025-07-24T03:40:54.2853945Z curr_branch: ciflow/trunk/149961 2025-07-24T03:40:54.2855270Z curr_ref_type: tag 2025-07-24T03:40:54.2856088Z issue_number: 5132 2025-07-24T03:40:54.2857099Z ##[endgroup] 2025-07-24T03:40:54.2858107Z Complete job name: get-label-type / runner-determinator 2025-07-24T03:40:54.8673917Z ##[group]Run cat < runner_determinator.py 2025-07-24T03:40:54.8676674Z cat < runner_determinator.py 2025-07-24T03:40:54.8677409Z # flake8: noqa: G004 2025-07-24T03:40:54.8678081Z  2025-07-24T03:40:54.8678927Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-07-24T03:40:54.8680093Z # must be kept in sync. You can do it easily by running the following command: 2025-07-24T03:40:54.8681194Z # python .github/scripts/update_runner_determinator.py 2025-07-24T03:40:54.8681978Z  2025-07-24T03:40:54.8682499Z """ 2025-07-24T03:40:54.8683243Z This runner determinator is used to determine which set of runners to run a 2025-07-24T03:40:54.8684424Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-07-24T03:40:54.8686087Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-07-24T03:40:54.8687230Z of which runners should be used to run which job. 2025-07-24T03:40:54.8687984Z  2025-07-24T03:40:54.8688728Z The configuration has two parts, the settings and a list of opted-in users, 2025-07-24T03:40:54.8689941Z separated by a line containing "---". If the line is not present, the 2025-07-24T03:40:54.8691021Z settings are considered to be empty with only the second part, the user 2025-07-24T03:40:54.8691963Z list, defined. 2025-07-24T03:40:54.8692598Z  2025-07-24T03:40:54.8693310Z The first part is a YAML block that defines the rollout settings. This can be 2025-07-24T03:40:54.8694458Z used to define any settings that are needed to determine which runners to use. 2025-07-24T03:40:54.8695731Z It's fields are defined by the RolloutSettings class below. 2025-07-24T03:40:54.8696578Z  2025-07-24T03:40:54.8697351Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-07-24T03:40:54.8698777Z The user list is also a comma separated list of additional features or 2025-07-24T03:40:54.8699774Z experiments which the user could be opted in to. 2025-07-24T03:40:54.8700492Z  2025-07-24T03:40:54.8701099Z The user list has the following rules: 2025-07-24T03:40:54.8701777Z  2025-07-24T03:40:54.8702567Z - Users are GitHub usernames, which must start with the @ prefix 2025-07-24T03:40:54.8703716Z - Each user is also a comma-separated list of features/experiments to enable 2025-07-24T03:40:54.8704719Z - A "#" prefix opts the user out of all experiments 2025-07-24T03:40:54.8705753Z  2025-07-24T03:40:54.8706241Z Example config: 2025-07-24T03:40:54.8706998Z  # A list of experiments that can be opted into. 2025-07-24T03:40:54.8707891Z  # This defines the behavior they'll induce when opted into. 2025-07-24T03:40:54.8708716Z  # Expected syntax is: 2025-07-24T03:40:54.8709652Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-07-24T03:40:54.8710815Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-07-24T03:40:54.8711750Z  2025-07-24T03:40:54.8712316Z  experiments: 2025-07-24T03:40:54.8712948Z  lf: 2025-07-24T03:40:54.8713503Z  rollout_percent: 25 2025-07-24T03:40:54.8714205Z  all_branches: false 2025-07-24T03:40:54.8714979Z  default: true 2025-07-24T03:40:54.8715568Z  --- 2025-07-24T03:40:54.8716162Z  2025-07-24T03:40:54.8716692Z  # Opt-ins: 2025-07-24T03:40:54.8717495Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-07-24T03:40:54.8718799Z  # and specifying experiments to enable in a comma-separated list. 2025-07-24T03:40:54.8719815Z  # To always opt out of an experiment, prefix it with a "-". 2025-07-24T03:40:54.8720702Z  # Experiments should be from the above list. 2025-07-24T03:40:54.8721440Z  2025-07-24T03:40:54.8722045Z  @User1,-lf,split_build 2025-07-24T03:40:54.8722671Z  @User2,lf 2025-07-24T03:40:54.8806657Z  @User3,split_build 2025-07-24T03:40:54.8807603Z """ 2025-07-24T03:40:54.8808079Z  2025-07-24T03:40:54.8808513Z import json 2025-07-24T03:40:54.8809001Z import logging 2025-07-24T03:40:54.8809531Z import os 2025-07-24T03:40:54.8810007Z import random 2025-07-24T03:40:54.8810493Z import re 2025-07-24T03:40:54.8810975Z import sys 2025-07-24T03:40:54.8811512Z from argparse import ArgumentParser 2025-07-24T03:40:54.8812248Z from collections.abc import Iterable 2025-07-24T03:40:54.8812926Z from functools import cache 2025-07-24T03:40:54.8813511Z from logging import LogRecord 2025-07-24T03:40:54.8814126Z from typing import Any, NamedTuple 2025-07-24T03:40:54.8814959Z from urllib.request import Request, urlopen 2025-07-24T03:40:54.8815627Z  2025-07-24T03:40:54.8816050Z import yaml 2025-07-24T03:40:54.8816549Z from github import Auth, Github 2025-07-24T03:40:54.8817151Z from github.Issue import Issue 2025-07-24T03:40:54.8817709Z  2025-07-24T03:40:54.8818110Z  2025-07-24T03:40:54.8818607Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-07-24T03:40:54.8819413Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-07-24T03:40:54.8820419Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-07-24T03:40:54.8821231Z  2025-07-24T03:40:54.8821749Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-07-24T03:40:54.8822797Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-07-24T03:40:54.8823429Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-07-24T03:40:54.8824109Z OPT_OUT_LABEL = "no-runner-experiments" 2025-07-24T03:40:54.8824718Z  2025-07-24T03:40:54.8825304Z SETTING_EXPERIMENTS = "experiments" 2025-07-24T03:40:54.8825894Z  2025-07-24T03:40:54.8826343Z LF_FLEET_EXPERIMENT = "lf" 2025-07-24T03:40:54.8826918Z CANARY_FLEET_SUFFIX = ".c" 2025-07-24T03:40:54.8827464Z  2025-07-24T03:40:54.8827875Z  2025-07-24T03:40:54.8828323Z class Experiment(NamedTuple): 2025-07-24T03:40:54.8828913Z  rollout_perc: float = ( 2025-07-24T03:40:54.8829691Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-07-24T03:40:54.8830471Z  ) 2025-07-24T03:40:54.8830937Z  all_branches: bool = ( 2025-07-24T03:40:54.8831702Z  False # If True, the experiment is also enabled on the exception branches 2025-07-24T03:40:54.8832476Z  ) 2025-07-24T03:40:54.8832966Z  default: bool = ( 2025-07-24T03:40:54.8833661Z  True # If True, the experiment is enabled by default for all queries 2025-07-24T03:40:54.8834400Z  ) 2025-07-24T03:40:54.8834934Z  2025-07-24T03:40:54.8835392Z  # Add more fields as needed 2025-07-24T03:40:54.8835955Z  2025-07-24T03:40:54.8836370Z  2025-07-24T03:40:54.8836822Z class Settings(NamedTuple): 2025-07-24T03:40:54.8837369Z  """ 2025-07-24T03:40:54.8837945Z  Settings for the experiments that can be opted into. 2025-07-24T03:40:54.8838628Z  """ 2025-07-24T03:40:54.8839066Z  2025-07-24T03:40:54.8839542Z  experiments: dict[str, Experiment] = {} 2025-07-24T03:40:54.8840138Z  2025-07-24T03:40:54.8840722Z  2025-07-24T03:40:54.8841250Z class ColorFormatter(logging.Formatter): 2025-07-24T03:40:54.8841991Z  """Color codes the log messages based on the log level""" 2025-07-24T03:40:54.8842677Z  2025-07-24T03:40:54.8843102Z  COLORS = { 2025-07-24T03:40:54.8843612Z  "WARNING": "\033[33m", # Yellow 2025-07-24T03:40:54.8844226Z  "ERROR": "\033[31m", # Red 2025-07-24T03:40:54.8844930Z  "CRITICAL": "\033[31m", # Red 2025-07-24T03:40:54.8845535Z  "INFO": "\033[0m", # Reset 2025-07-24T03:40:54.8846127Z  "DEBUG": "\033[0m", # Reset 2025-07-24T03:40:54.8846714Z  } 2025-07-24T03:40:54.8847149Z  2025-07-24T03:40:54.8847643Z  def format(self, record: LogRecord) -> str: 2025-07-24T03:40:54.8848513Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-07-24T03:40:54.8849396Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-07-24T03:40:54.8850079Z  return super().format(record) 2025-07-24T03:40:54.8850658Z  2025-07-24T03:40:54.8851068Z  2025-07-24T03:40:54.8851520Z handler = logging.StreamHandler() 2025-07-24T03:40:54.8852361Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-07-24T03:40:54.8853174Z  2025-07-24T03:40:54.8853708Z log = logging.getLogger(os.path.basename(__file__)) 2025-07-24T03:40:54.8854402Z log.addHandler(handler) 2025-07-24T03:40:54.8855105Z log.setLevel(logging.INFO) 2025-07-24T03:40:54.8855653Z  2025-07-24T03:40:54.8856056Z  2025-07-24T03:40:54.8856606Z def set_github_output(key: str, value: str) -> None: 2025-07-24T03:40:54.8857267Z  """ 2025-07-24T03:40:54.8857874Z  Defines outputs of the github action that invokes this script 2025-07-24T03:40:54.8858762Z  """ 2025-07-24T03:40:54.8859222Z  if not GITHUB_OUTPUT: 2025-07-24T03:40:54.8860417Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-07-24T03:40:54.8861673Z  log.warning( 2025-07-24T03:40:54.8862664Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-07-24T03:40:54.8863697Z  ) 2025-07-24T03:40:54.8864230Z  print(f"::set-output name={key}::{value}") 2025-07-24T03:40:54.8865266Z  return 2025-07-24T03:40:54.8865744Z  2025-07-24T03:40:54.8866209Z  with open(GITHUB_OUTPUT, "a") as f: 2025-07-24T03:40:54.8866880Z  log.info(f"Setting output: {key}='{value}'") 2025-07-24T03:40:54.8867549Z  f.write(f"{key}={value}\n") 2025-07-24T03:40:54.8868136Z  2025-07-24T03:40:54.8868567Z  2025-07-24T03:40:54.8869168Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-07-24T03:40:54.8869934Z  return frozenset( 2025-07-24T03:40:54.8870681Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-07-24T03:40:54.8871472Z  ) 2025-07-24T03:40:54.8871921Z  2025-07-24T03:40:54.8872334Z  2025-07-24T03:40:54.8872775Z def parse_args() -> Any: 2025-07-24T03:40:54.8873498Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-07-24T03:40:54.8874477Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-07-24T03:40:54.8875459Z  parser.add_argument( 2025-07-24T03:40:54.8876026Z  "--github-issue-repo", 2025-07-24T03:40:54.8876606Z  type=str, 2025-07-24T03:40:54.8877116Z  required=False, 2025-07-24T03:40:54.8877832Z  default="pytorch/test-infra", 2025-07-24T03:40:54.8878489Z  help="GitHub repo to get the issue", 2025-07-24T03:40:54.8879087Z  ) 2025-07-24T03:40:54.8879547Z  parser.add_argument( 2025-07-24T03:40:54.8880106Z  "--github-repo", 2025-07-24T03:40:54.8880645Z  type=str, 2025-07-24T03:40:54.8881154Z  required=True, 2025-07-24T03:40:54.8881745Z  help="GitHub repo where CI is running", 2025-07-24T03:40:54.8882365Z  ) 2025-07-24T03:40:54.8882829Z  parser.add_argument( 2025-07-24T03:40:54.8883571Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-07-24T03:40:54.8884333Z  ) 2025-07-24T03:40:54.8884900Z  parser.add_argument( 2025-07-24T03:40:54.8885650Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-07-24T03:40:54.8886439Z  ) 2025-07-24T03:40:54.8886889Z  parser.add_argument( 2025-07-24T03:40:54.8887655Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-07-24T03:40:54.8888446Z  ) 2025-07-24T03:40:54.8888903Z  parser.add_argument( 2025-07-24T03:40:54.8889691Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-07-24T03:40:54.8890494Z  ) 2025-07-24T03:40:54.8890974Z  parser.add_argument( 2025-07-24T03:40:54.8891532Z  "--github-ref-type", 2025-07-24T03:40:54.8892096Z  type=str, 2025-07-24T03:40:54.8892605Z  required=True, 2025-07-24T03:40:54.8893247Z  help="Current GitHub ref type, branch or tag", 2025-07-24T03:40:54.8893887Z  ) 2025-07-24T03:40:54.8894346Z  parser.add_argument( 2025-07-24T03:40:54.8895070Z  "--eligible-experiments", 2025-07-24T03:40:54.8895871Z  type=_str_comma_separated_to_set, 2025-07-24T03:40:54.8896483Z  required=False, 2025-07-24T03:40:54.8897025Z  default="", 2025-07-24T03:40:54.8898009Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-07-24T03:40:54.8899037Z  ) 2025-07-24T03:40:54.8899503Z  parser.add_argument( 2025-07-24T03:40:54.8900068Z  "--opt-out-experiments", 2025-07-24T03:40:54.8900691Z  type=_str_comma_separated_to_set, 2025-07-24T03:40:54.8901298Z  required=False, 2025-07-24T03:40:54.8901845Z  default="", 2025-07-24T03:40:54.8902346Z  help=( 2025-07-24T03:40:54.8903130Z  "comma separated list of experiments to opt-out of. If unset, no opt-outs will occur. " 2025-07-24T03:40:54.8904374Z  "If the same experiment is listed both here and in '--eligible-experiments' opt-out will take priority." 2025-07-24T03:40:54.8905427Z  ), 2025-07-24T03:40:54.8905884Z  ) 2025-07-24T03:40:54.8906333Z  parser.add_argument( 2025-07-24T03:40:54.8906892Z  "--pr-number", 2025-07-24T03:40:54.8907419Z  type=str, 2025-07-24T03:40:54.8907927Z  required=False, 2025-07-24T03:40:54.8908468Z  default="", 2025-07-24T03:40:54.8909064Z  help="the optional PR number where this is run", 2025-07-24T03:40:54.8909719Z  ) 2025-07-24T03:40:54.8910150Z  2025-07-24T03:40:54.8910608Z  return parser.parse_args() 2025-07-24T03:40:54.8911160Z  2025-07-24T03:40:54.8911569Z  2025-07-24T03:40:54.8912248Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-07-24T03:40:54.8913244Z  auth = Auth.Token(github_token) 2025-07-24T03:40:54.8913905Z  return Github(auth=auth) 2025-07-24T03:40:54.8914450Z  2025-07-24T03:40:54.8914964Z  2025-07-24T03:40:54.8915715Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-07-24T03:40:54.8916638Z  repo = gh.get_repo(repo) 2025-07-24T03:40:54.8917259Z  return repo.get_issue(number=issue_num) 2025-07-24T03:40:54.8917880Z  2025-07-24T03:40:54.8918295Z  2025-07-24T03:40:54.8918735Z def get_potential_pr_author( 2025-07-24T03:40:54.8919505Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-07-24T03:40:54.8920281Z ) -> str: 2025-07-24T03:40:54.8920911Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-07-24T03:40:54.8921829Z  # Fetch the actual username from the original PR. The PR number is 2025-07-24T03:40:54.8922710Z  # embedded in the tag name: ciflow// 2025-07-24T03:40:54.8923384Z  2025-07-24T03:40:54.8923842Z  gh = get_gh_client(github_token) 2025-07-24T03:40:54.8924425Z  2025-07-24T03:40:54.8925073Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-07-24T03:40:54.8925813Z  split_tag = ref_name.split("/") 2025-07-24T03:40:54.8926405Z  if ( 2025-07-24T03:40:54.8926902Z  len(split_tag) == 3 2025-07-24T03:40:54.8927506Z  and split_tag[0] == "ciflow" 2025-07-24T03:40:54.8928132Z  and split_tag[2].isnumeric() 2025-07-24T03:40:54.8928722Z  ): 2025-07-24T03:40:54.8929221Z  pr_number = split_tag[2] 2025-07-24T03:40:54.8929807Z  try: 2025-07-24T03:40:54.8930345Z  repository = gh.get_repo(repo) 2025-07-24T03:40:54.8931065Z  pull = repository.get_pull(number=int(pr_number)) 2025-07-24T03:40:54.8931936Z  except Exception as e: 2025-07-24T03:40:54.8932569Z  raise Exception( # noqa: TRY002 2025-07-24T03:40:54.8933383Z  f"issue with pull request {pr_number} from repo {repository}" 2025-07-24T03:40:54.8934121Z  ) from e 2025-07-24T03:40:54.8934928Z  return pull.user.login # type: ignore[no-any-return] 2025-07-24T03:40:54.8935753Z  # In all other cases, return the original input username 2025-07-24T03:40:54.8936442Z  return username 2025-07-24T03:40:54.8936956Z  2025-07-24T03:40:54.8937362Z  2025-07-24T03:40:54.8937861Z def is_exception_branch(branch: str) -> bool: 2025-07-24T03:40:54.8938478Z  """ 2025-07-24T03:40:54.8939245Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-07-24T03:40:54.8940128Z  """ 2025-07-24T03:40:54.8940781Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-07-24T03:40:54.8941531Z  2025-07-24T03:40:54.8941929Z  2025-07-24T03:40:54.8942389Z def load_yaml(yaml_text: str) -> Any: 2025-07-24T03:40:54.8942974Z  try: 2025-07-24T03:40:54.8943468Z  data = yaml.safe_load(yaml_text) 2025-07-24T03:40:54.8944059Z  return data 2025-07-24T03:40:54.8944590Z  except yaml.YAMLError: 2025-07-24T03:40:54.8946006Z  log.exception("Error loading YAML") 2025-07-24T03:40:54.8946636Z  raise 2025-07-24T03:40:54.8947104Z  2025-07-24T03:40:54.8947504Z  2025-07-24T03:40:54.8948212Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-07-24T03:40:54.8949045Z  """ 2025-07-24T03:40:54.8949948Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-07-24T03:40:54.8950814Z  2025-07-24T03:40:54.8951426Z  If the issue body contains "---" then the text above that is the settings 2025-07-24T03:40:54.8952310Z  and the text below is the list of opted in users. 2025-07-24T03:40:54.8952957Z  2025-07-24T03:40:54.8953616Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-07-24T03:40:54.8954398Z  """ 2025-07-24T03:40:54.8955060Z  rollout_state_parts = rollout_state.split("---") 2025-07-24T03:40:54.8955750Z  if len(rollout_state_parts) >= 2: 2025-07-24T03:40:54.8956462Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-07-24T03:40:54.8957306Z  else: 2025-07-24T03:40:54.8957958Z  return "", rollout_state 2025-07-24T03:40:54.8958514Z  2025-07-24T03:40:54.8958910Z  2025-07-24T03:40:54.8959398Z class UserOptins(dict[str, list[str]]): 2025-07-24T03:40:54.8959989Z  """ 2025-07-24T03:40:54.8960607Z  Dictionary of users with a list of features they have opted into 2025-07-24T03:40:54.8961334Z  """ 2025-07-24T03:40:54.8961765Z  2025-07-24T03:40:54.8962167Z  2025-07-24T03:40:54.8962771Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-07-24T03:40:54.8963526Z  """ 2025-07-24T03:40:54.8964339Z  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:40:54.8965381Z  2025-07-24T03:40:54.8966292Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-07-24T03:40:54.8967420Z  - Example line: "@User1,lf,split_build" 2025-07-24T03:40:54.8968218Z  - A "#" prefix indicates the user is opted out of all experiments 2025-07-24T03:40:54.8969168Z  2025-07-24T03:40:54.8969576Z  2025-07-24T03:40:54.8969972Z  """ 2025-07-24T03:40:54.8970430Z  optins = UserOptins() 2025-07-24T03:40:54.8971022Z  for user in user_optin_text.split("\n"): 2025-07-24T03:40:54.8971685Z  user = user.strip("\r\n\t -") 2025-07-24T03:40:54.8972336Z  if not user or not user.startswith("@"): 2025-07-24T03:40:54.8972976Z  # Not a valid user. Skip 2025-07-24T03:40:54.8973556Z  continue 2025-07-24T03:40:54.8974043Z  2025-07-24T03:40:54.8974492Z  if user: 2025-07-24T03:40:54.8975158Z  usr_name = user.split(",")[0].strip("@") 2025-07-24T03:40:54.8975951Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-07-24T03:40:54.8976670Z  2025-07-24T03:40:54.8977098Z  return optins 2025-07-24T03:40:54.8977591Z  2025-07-24T03:40:54.8977991Z  2025-07-24T03:40:54.8978561Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-07-24T03:40:54.8979261Z  """ 2025-07-24T03:40:54.8979758Z  Check if the experiment name is valid. 2025-07-24T03:40:54.8980360Z  A valid name: 2025-07-24T03:40:54.8981120Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-07-24T03:40:54.8982166Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-07-24T03:40:54.8982976Z  - Cannot contain spaces 2025-07-24T03:40:54.8983532Z  """ 2025-07-24T03:40:54.8983956Z  2025-07-24T03:40:54.8984497Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-07-24T03:40:54.8985415Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-07-24T03:40:54.8986257Z  2025-07-24T03:40:54.8986948Z  if valid: 2025-07-24T03:40:54.8987429Z  return True 2025-07-24T03:40:54.8987925Z  2025-07-24T03:40:54.8988331Z  log.error( 2025-07-24T03:40:54.8989940Z  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:40:54.8991646Z  ) 2025-07-24T03:40:54.8992092Z  return False 2025-07-24T03:40:54.8992596Z  2025-07-24T03:40:54.8993000Z  2025-07-24T03:40:54.8993597Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-07-24T03:40:54.8994320Z  """ 2025-07-24T03:40:54.8995127Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-07-24T03:40:54.8995928Z  """ 2025-07-24T03:40:54.8996389Z  try: 2025-07-24T03:40:54.8996833Z  if settings_text: 2025-07-24T03:40:54.8997677Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-07-24T03:40:54.8998563Z  # for easy reading 2025-07-24T03:40:54.8999521Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-07-24T03:40:54.9000521Z  # the backtick character in shell commands. 2025-07-24T03:40:54.9001217Z  backtick = chr(96) # backtick character 2025-07-24T03:40:54.9001994Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-07-24T03:40:54.9002753Z  settings = load_yaml(settings_text) 2025-07-24T03:40:54.9003346Z  2025-07-24T03:40:54.9004029Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-07-24T03:40:54.9005120Z  experiments = {} 2025-07-24T03:40:54.9005667Z  2025-07-24T03:40:54.9006307Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-07-24T03:40:54.9007161Z  if not is_valid_experiment_name(exp_name): 2025-07-24T03:40:54.9008376Z  # 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:40:54.9009527Z  continue 2025-07-24T03:40:54.9010064Z  2025-07-24T03:40:54.9010501Z  valid_settings = {} 2025-07-24T03:40:54.9011120Z  for setting in exp_settings: 2025-07-24T03:40:54.9011776Z  if setting not in Experiment._fields: 2025-07-24T03:40:54.9012425Z  log.warning( 2025-07-24T03:40:54.9013265Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-07-24T03:40:54.9014054Z  ) 2025-07-24T03:40:54.9014571Z  else: 2025-07-24T03:40:54.9015298Z  valid_settings[setting] = exp_settings[setting] 2025-07-24T03:40:54.9015952Z  2025-07-24T03:40:54.9016489Z  experiments[exp_name] = Experiment(**valid_settings) 2025-07-24T03:40:54.9017219Z  return Settings(experiments) 2025-07-24T03:40:54.9017797Z  2025-07-24T03:40:54.9018219Z  except Exception: 2025-07-24T03:40:54.9018809Z  log.exception("Failed to parse settings") 2025-07-24T03:40:54.9019427Z  2025-07-24T03:40:54.9019849Z  return Settings() 2025-07-24T03:40:54.9020356Z  2025-07-24T03:40:54.9020750Z  2025-07-24T03:40:54.9021410Z def parse_settings(rollout_state: str) -> Settings: 2025-07-24T03:40:54.9022086Z  """ 2025-07-24T03:40:54.9022617Z  Parse settings, if any, from the rollout state. 2025-07-24T03:40:54.9023246Z  2025-07-24T03:40:54.9023864Z  If the issue body contains "---" then the text above that is the settings 2025-07-24T03:40:54.9024725Z  and the text below is the list of opted in users. 2025-07-24T03:40:54.9025466Z  2025-07-24T03:40:54.9026148Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-07-24T03:40:54.9026959Z  """ 2025-07-24T03:40:54.9027615Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-24T03:40:54.9028489Z  return parse_settings_from_text(settings_text) 2025-07-24T03:40:54.9029119Z  2025-07-24T03:40:54.9029516Z  2025-07-24T03:40:54.9030042Z def parse_users(rollout_state: str) -> UserOptins: 2025-07-24T03:40:54.9030697Z  """ 2025-07-24T03:40:54.9031176Z  Parse users from the rollout state. 2025-07-24T03:40:54.9031763Z  2025-07-24T03:40:54.9032163Z  """ 2025-07-24T03:40:54.9032799Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-24T03:40:54.9033681Z  return parse_user_opt_in_from_text(users_text) 2025-07-24T03:40:54.9034302Z  2025-07-24T03:40:54.9034703Z  2025-07-24T03:40:54.9035490Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-24T03:40:54.9036335Z  """ 2025-07-24T03:40:54.9036840Z  Check if a user is opted into an experiment 2025-07-24T03:40:54.9037457Z  """ 2025-07-24T03:40:54.9038010Z  return experiment_name in user_optins.get(user, []) 2025-07-24T03:40:54.9038672Z  2025-07-24T03:40:54.9039075Z  2025-07-24T03:40:54.9039973Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-24T03:40:54.9040819Z  """ 2025-07-24T03:40:54.9041373Z  Check if a user explicitly opted out of an experiment 2025-07-24T03:40:54.9042041Z  """ 2025-07-24T03:40:54.9042636Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-07-24T03:40:54.9043423Z  experiment_optout = "-" + experiment_name 2025-07-24T03:40:54.9044154Z  if experiment_optout not in user_optins.get(user, []): 2025-07-24T03:40:54.9044959Z  return False 2025-07-24T03:40:54.9045462Z  2025-07-24T03:40:54.9045992Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-07-24T03:40:54.9046670Z  log.warning( 2025-07-24T03:40:54.9047617Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-07-24T03:40:54.9048618Z  ) 2025-07-24T03:40:54.9049052Z  2025-07-24T03:40:54.9049475Z  return True 2025-07-24T03:40:54.9049951Z  2025-07-24T03:40:54.9050345Z  2025-07-24T03:40:54.9050775Z def get_runner_prefix( 2025-07-24T03:40:54.9051301Z  rollout_state: str, 2025-07-24T03:40:54.9051876Z  workflow_requestors: Iterable[str], 2025-07-24T03:40:54.9052471Z  branch: str, 2025-07-24T03:40:54.9053078Z  eligible_experiments: frozenset[str] = frozenset(), 2025-07-24T03:40:54.9053845Z  opt_out_experiments: frozenset[str] = frozenset(), 2025-07-24T03:40:54.9054527Z  is_canary: bool = False, 2025-07-24T03:40:54.9055165Z ) -> str: 2025-07-24T03:40:54.9055684Z  settings = parse_settings(rollout_state) 2025-07-24T03:40:54.9056360Z  user_optins = parse_users(rollout_state) 2025-07-24T03:40:54.9056968Z  2025-07-24T03:40:54.9057524Z  fleet_prefix = "" 2025-07-24T03:40:54.9058053Z  prefixes = [] 2025-07-24T03:40:54.9058795Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-07-24T03:40:54.9059830Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-07-24T03:40:54.9060630Z  log.info( 2025-07-24T03:40:54.9061419Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-07-24T03:40:54.9062246Z  ) 2025-07-24T03:40:54.9062745Z  continue 2025-07-24T03:40:54.9063233Z  2025-07-24T03:40:54.9063670Z  if opt_out_experiments: 2025-07-24T03:40:54.9064327Z  if experiment_name in opt_out_experiments: 2025-07-24T03:40:54.9065170Z  opt_out_exp_list = ", ".join(opt_out_experiments) 2025-07-24T03:40:54.9065856Z  log.info( 2025-07-24T03:40:54.9066886Z  f"Skipping experiment '{experiment_name}', as this workflow has opted-out (opted out experiments are: {opt_out_exp_list})" 2025-07-24T03:40:54.9067994Z  ) 2025-07-24T03:40:54.9068492Z  continue 2025-07-24T03:40:54.9069002Z  2025-07-24T03:40:54.9069450Z  if eligible_experiments: 2025-07-24T03:40:54.9070111Z  if experiment_name not in eligible_experiments: 2025-07-24T03:40:54.9070845Z  exp_list = ", ".join(eligible_experiments) 2025-07-24T03:40:54.9071478Z  log.info( 2025-07-24T03:40:54.9072366Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-07-24T03:40:54.9073296Z  ) 2025-07-24T03:40:54.9073785Z  continue 2025-07-24T03:40:54.9074558Z  elif not experiment_settings.default: 2025-07-24T03:40:54.9075278Z  log.info( 2025-07-24T03:40:54.9076051Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-07-24T03:40:54.9076868Z  ) 2025-07-24T03:40:54.9077342Z  continue 2025-07-24T03:40:54.9077835Z  2025-07-24T03:40:54.9078369Z  # Is any workflow_requestor opted out to this experiment? 2025-07-24T03:40:54.9079073Z  opted_out_users = [ 2025-07-24T03:40:54.9079626Z  requestor 2025-07-24T03:40:54.9080192Z  for requestor in workflow_requestors 2025-07-24T03:40:54.9080952Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-07-24T03:40:54.9081668Z  ] 2025-07-24T03:40:54.9082104Z  2025-07-24T03:40:54.9082535Z  if opted_out_users: 2025-07-24T03:40:54.9083117Z  log.info( 2025-07-24T03:40:54.9083850Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-07-24T03:40:54.9084637Z  ) 2025-07-24T03:40:54.9085256Z  continue 2025-07-24T03:40:54.9085759Z  2025-07-24T03:40:54.9086294Z  # Is any workflow_requestor opted in to this experiment? 2025-07-24T03:40:54.9087006Z  opted_in_users = [ 2025-07-24T03:40:54.9087567Z  requestor 2025-07-24T03:40:54.9088131Z  for requestor in workflow_requestors 2025-07-24T03:40:54.9088898Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-07-24T03:40:54.9089602Z  ] 2025-07-24T03:40:54.9090049Z  2025-07-24T03:40:54.9090471Z  enabled = False 2025-07-24T03:40:54.9091021Z  if opted_in_users: 2025-07-24T03:40:54.9091746Z  log.info( 2025-07-24T03:40:54.9092502Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-07-24T03:40:54.9093274Z  ) 2025-07-24T03:40:54.9093758Z  enabled = True 2025-07-24T03:40:54.9094283Z  2025-07-24T03:40:54.9094766Z  elif experiment_settings.rollout_perc: 2025-07-24T03:40:54.9095828Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-07-24T03:40:54.9096880Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-07-24T03:40:54.9097628Z  log.info( 2025-07-24T03:40:54.9098610Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-07-24T03:40:54.9099624Z  ) 2025-07-24T03:40:54.9100155Z  enabled = True 2025-07-24T03:40:54.9100698Z  2025-07-24T03:40:54.9101126Z  if enabled: 2025-07-24T03:40:54.9101664Z  label = experiment_name 2025-07-24T03:40:54.9102311Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-07-24T03:40:54.9103227Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-07-24T03:40:54.9104194Z  # - If it's enabled, then we always list it's prefix first 2025-07-24T03:40:54.9105178Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-07-24T03:40:54.9105934Z  if is_canary: 2025-07-24T03:40:54.9106517Z  label += CANARY_FLEET_SUFFIX 2025-07-24T03:40:54.9107158Z  fleet_prefix = label 2025-07-24T03:40:54.9107740Z  else: 2025-07-24T03:40:54.9108433Z  prefixes.append(label) 2025-07-24T03:40:54.9109019Z  2025-07-24T03:40:54.9109446Z  if len(prefixes) > 1: 2025-07-24T03:40:54.9109980Z  log.error( 2025-07-24T03:40:54.9111144Z  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:40:54.9112373Z  ) 2025-07-24T03:40:54.9112843Z  prefixes = prefixes[:1] 2025-07-24T03:40:54.9113428Z  2025-07-24T03:40:54.9113862Z  # Fleet always comes first 2025-07-24T03:40:54.9114439Z  if fleet_prefix: 2025-07-24T03:40:54.9115109Z  prefixes.insert(0, fleet_prefix) 2025-07-24T03:40:54.9115707Z  2025-07-24T03:40:54.9116221Z  return ".".join(prefixes) + "." if prefixes else "" 2025-07-24T03:40:54.9116865Z  2025-07-24T03:40:54.9117267Z  2025-07-24T03:40:54.9117988Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-07-24T03:40:54.9118841Z  """ 2025-07-24T03:40:54.9119509Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-07-24T03:40:54.9120296Z  2025-07-24T03:40:54.9120948Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-07-24T03:40:54.9121873Z  """ 2025-07-24T03:40:54.9122356Z  gh = get_gh_client(github_token) 2025-07-24T03:40:54.9122997Z  issue = get_issue(gh, repo, issue_num) 2025-07-24T03:40:54.9123730Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-07-24T03:40:54.9124406Z  2025-07-24T03:40:54.9124911Z  2025-07-24T03:40:54.9125584Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-07-24T03:40:54.9126574Z  for _ in range(num_retries): 2025-07-24T03:40:54.9127147Z  try: 2025-07-24T03:40:54.9127669Z  req = Request(url=url, headers=headers) 2025-07-24T03:40:54.9128423Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-07-24T03:40:54.9129157Z  return json.loads(content) 2025-07-24T03:40:54.9129772Z  except Exception as e: 2025-07-24T03:40:54.9130422Z  log.warning(f"Could not download {url}: {e}") 2025-07-24T03:40:54.9131049Z  2025-07-24T03:40:54.9131699Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-07-24T03:40:54.9132498Z  return {} 2025-07-24T03:40:54.9132965Z  2025-07-24T03:40:54.9133401Z  2025-07-24T03:40:54.9133806Z @cache 2025-07-24T03:40:54.9134526Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-07-24T03:40:54.9135487Z  """ 2025-07-24T03:40:54.9135968Z  Dynamically get PR information 2025-07-24T03:40:54.9136534Z  """ 2025-07-24T03:40:54.9137129Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-07-24T03:40:54.9137835Z  headers = { 2025-07-24T03:40:54.9138401Z  "Accept": "application/vnd.github.v3+json", 2025-07-24T03:40:54.9139093Z  "Authorization": f"token {github_token}", 2025-07-24T03:40:54.9139706Z  } 2025-07-24T03:40:54.9140224Z  json_response: dict[str, Any] = download_json( 2025-07-24T03:40:54.9140914Z  url=f"{github_api}/issues/{pr_number}", 2025-07-24T03:40:54.9141530Z  headers=headers, 2025-07-24T03:40:54.9142043Z  ) 2025-07-24T03:40:54.9142468Z  2025-07-24T03:40:54.9142892Z  if not json_response: 2025-07-24T03:40:54.9143573Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-07-24T03:40:54.9144444Z  return {} 2025-07-24T03:40:54.9145046Z  2025-07-24T03:40:54.9145490Z  return json_response 2025-07-24T03:40:54.9146010Z  2025-07-24T03:40:54.9146410Z  2025-07-24T03:40:54.9147066Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-07-24T03:40:54.9147894Z  """ 2025-07-24T03:40:54.9148509Z  Dynamically get the latest list of labels from the pull request 2025-07-24T03:40:54.9149246Z  """ 2025-07-24T03:40:54.9149818Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-07-24T03:40:54.9150511Z  return { 2025-07-24T03:40:54.9151196Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-07-24T03:40:54.9151964Z  } 2025-07-24T03:40:54.9152389Z  2025-07-24T03:40:54.9152785Z  2025-07-24T03:40:54.9153222Z def main() -> None: 2025-07-24T03:40:54.9153736Z  args = parse_args() 2025-07-24T03:40:54.9154251Z  2025-07-24T03:40:54.9154735Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-07-24T03:40:54.9155453Z  2025-07-24T03:40:54.9155899Z  # Check if the PR is opt-out 2025-07-24T03:40:54.9156482Z  if args.pr_number: 2025-07-24T03:40:54.9157255Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-07-24T03:40:54.9158091Z  if OPT_OUT_LABEL in labels: 2025-07-24T03:40:54.9158672Z  log.info( 2025-07-24T03:40:54.9159475Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-07-24T03:40:54.9160315Z  ) 2025-07-24T03:40:54.9160972Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-24T03:40:54.9161739Z  sys.exit() 2025-07-24T03:40:54.9162441Z  2025-07-24T03:40:54.9162859Z  try: 2025-07-24T03:40:54.9163388Z  rollout_state = get_rollout_state_from_issue( 2025-07-24T03:40:54.9164187Z  args.github_token, args.github_issue_repo, args.github_issue 2025-07-24T03:40:54.9165029Z  ) 2025-07-24T03:40:54.9165474Z  2025-07-24T03:40:54.9165937Z  username = get_potential_pr_author( 2025-07-24T03:40:54.9166560Z  args.github_token, 2025-07-24T03:40:54.9167131Z  args.github_repo, 2025-07-24T03:40:54.9167708Z  args.github_actor, 2025-07-24T03:40:54.9168285Z  args.github_ref_type, 2025-07-24T03:40:54.9168873Z  args.github_branch, 2025-07-24T03:40:54.9169420Z  ) 2025-07-24T03:40:54.9169851Z  2025-07-24T03:40:54.9170411Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-07-24T03:40:54.9171102Z  2025-07-24T03:40:54.9171585Z  runner_label_prefix = get_runner_prefix( 2025-07-24T03:40:54.9172213Z  rollout_state, 2025-07-24T03:40:54.9172812Z  (args.github_issue_owner, username), 2025-07-24T03:40:54.9173451Z  args.github_branch, 2025-07-24T03:40:54.9174051Z  args.eligible_experiments, 2025-07-24T03:40:54.9174678Z  args.opt_out_experiments, 2025-07-24T03:40:54.9175383Z  is_canary, 2025-07-24T03:40:54.9175891Z  ) 2025-07-24T03:40:54.9176321Z  2025-07-24T03:40:54.9176759Z  except Exception as e: 2025-07-24T03:40:54.9177306Z  log.error( 2025-07-24T03:40:54.9178109Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-07-24T03:40:54.9178950Z  ) 2025-07-24T03:40:54.9179533Z  2025-07-24T03:40:54.9180136Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-24T03:40:54.9180867Z  2025-07-24T03:40:54.9181265Z  2025-07-24T03:40:54.9181680Z if __name__ == "__main__": 2025-07-24T03:40:54.9182214Z  main() 2025-07-24T03:40:54.9182654Z  2025-07-24T03:40:54.9183055Z EOF 2025-07-24T03:40:54.9183463Z  2025-07-24T03:40:54.9183904Z cat runner_determinator.py 2025-07-24T03:40:54.9435268Z shell: /usr/bin/bash -e {0} 2025-07-24T03:40:54.9436844Z env: 2025-07-24T03:40:54.9437732Z GITHUB_TOKEN: *** 2025-07-24T03:40:54.9438242Z ISSUE_NUMBER: 5132 2025-07-24T03:40:54.9438767Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-07-24T03:40:54.9439370Z ISSUE_OWNER: 2025-07-24T03:40:54.9439838Z CHECK_EXPERIMENTS: 2025-07-24T03:40:54.9440344Z OPT_OUT_EXPERIMENTS: 2025-07-24T03:40:54.9440867Z PR_NUMBER: 2025-07-24T03:40:54.9441323Z ##[endgroup] 2025-07-24T03:40:54.9659006Z # flake8: noqa: G004 2025-07-24T03:40:54.9659566Z 2025-07-24T03:40:54.9660268Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-07-24T03:40:54.9661327Z # must be kept in sync. You can do it easily by running the following command: 2025-07-24T03:40:54.9662280Z # python .github/scripts/update_runner_determinator.py 2025-07-24T03:40:54.9662739Z 2025-07-24T03:40:54.9662907Z """ 2025-07-24T03:40:54.9663647Z This runner determinator is used to determine which set of runners to run a 2025-07-24T03:40:54.9665184Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-07-24T03:40:54.9666132Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-07-24T03:40:54.9666952Z of which runners should be used to run which job. 2025-07-24T03:40:54.9667361Z 2025-07-24T03:40:54.9667752Z The configuration has two parts, the settings and a list of opted-in users, 2025-07-24T03:40:54.9668889Z separated by a line containing "---". If the line is not present, the 2025-07-24T03:40:54.9669813Z settings are considered to be empty with only the second part, the user 2025-07-24T03:40:54.9670516Z list, defined. 2025-07-24T03:40:54.9670746Z 2025-07-24T03:40:54.9671115Z The first part is a YAML block that defines the rollout settings. This can be 2025-07-24T03:40:54.9672049Z used to define any settings that are needed to determine which runners to use. 2025-07-24T03:40:54.9672906Z It's fields are defined by the RolloutSettings class below. 2025-07-24T03:40:54.9673349Z 2025-07-24T03:40:54.9673724Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-07-24T03:40:54.9674585Z The user list is also a comma separated list of additional features or 2025-07-24T03:40:54.9675707Z experiments which the user could be opted in to. 2025-07-24T03:40:54.9676112Z 2025-07-24T03:40:54.9676312Z The user list has the following rules: 2025-07-24T03:40:54.9676664Z 2025-07-24T03:40:54.9676985Z - Users are GitHub usernames, which must start with the @ prefix 2025-07-24T03:40:54.9677860Z - Each user is also a comma-separated list of features/experiments to enable 2025-07-24T03:40:54.9678640Z - A "#" prefix opts the user out of all experiments 2025-07-24T03:40:54.9679035Z 2025-07-24T03:40:54.9679218Z Example config: 2025-07-24T03:40:54.9679752Z # A list of experiments that can be opted into. 2025-07-24T03:40:54.9680961Z # This defines the behavior they'll induce when opted into. 2025-07-24T03:40:54.9682121Z # Expected syntax is: 2025-07-24T03:40:54.9683249Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-07-24T03:40:54.9685474Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-07-24T03:40:54.9686673Z 2025-07-24T03:40:54.9686966Z experiments: 2025-07-24T03:40:54.9687696Z lf: 2025-07-24T03:40:54.9688455Z rollout_percent: 25 2025-07-24T03:40:54.9689454Z all_branches: false 2025-07-24T03:40:54.9690363Z default: true 2025-07-24T03:40:54.9690819Z --- 2025-07-24T03:40:54.9691217Z 2025-07-24T03:40:54.9691533Z # Opt-ins: 2025-07-24T03:40:54.9692253Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-07-24T03:40:54.9693184Z # and specifying experiments to enable in a comma-separated list. 2025-07-24T03:40:54.9694019Z # To always opt out of an experiment, prefix it with a "-". 2025-07-24T03:40:54.9694683Z # Experiments should be from the above list. 2025-07-24T03:40:54.9695302Z 2025-07-24T03:40:54.9695492Z @User1,-lf,split_build 2025-07-24T03:40:54.9695945Z @User2,lf 2025-07-24T03:40:54.9696335Z @User3,split_build 2025-07-24T03:40:54.9696754Z """ 2025-07-24T03:40:54.9696947Z 2025-07-24T03:40:54.9697113Z import json 2025-07-24T03:40:54.9697486Z import logging 2025-07-24T03:40:54.9697864Z import os 2025-07-24T03:40:54.9698236Z import random 2025-07-24T03:40:54.9698615Z import re 2025-07-24T03:40:54.9698978Z import sys 2025-07-24T03:40:54.9699409Z from argparse import ArgumentParser 2025-07-24T03:40:54.9699933Z from collections.abc import Iterable 2025-07-24T03:40:54.9700456Z from functools import cache 2025-07-24T03:40:54.9700935Z from logging import LogRecord 2025-07-24T03:40:54.9701435Z from typing import Any, NamedTuple 2025-07-24T03:40:54.9701964Z from urllib.request import Request, urlopen 2025-07-24T03:40:54.9702339Z 2025-07-24T03:40:54.9702507Z import yaml 2025-07-24T03:40:54.9702900Z from github import Auth, Github 2025-07-24T03:40:54.9703386Z from github.Issue import Issue 2025-07-24T03:40:54.9703686Z 2025-07-24T03:40:54.9703692Z 2025-07-24T03:40:54.9703916Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-07-24T03:40:54.9704602Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-07-24T03:40:54.9705943Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-07-24T03:40:54.9706555Z 2025-07-24T03:40:54.9706791Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-07-24T03:40:54.9707791Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-07-24T03:40:54.9708376Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-07-24T03:40:54.9708969Z OPT_OUT_LABEL = "no-runner-experiments" 2025-07-24T03:40:54.9709331Z 2025-07-24T03:40:54.9709533Z SETTING_EXPERIMENTS = "experiments" 2025-07-24T03:40:54.9709867Z 2025-07-24T03:40:54.9710055Z LF_FLEET_EXPERIMENT = "lf" 2025-07-24T03:40:54.9710516Z CANARY_FLEET_SUFFIX = ".c" 2025-07-24T03:40:54.9710799Z 2025-07-24T03:40:54.9710805Z 2025-07-24T03:40:54.9710992Z class Experiment(NamedTuple): 2025-07-24T03:40:54.9711485Z rollout_perc: float = ( 2025-07-24T03:40:54.9712127Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-07-24T03:40:54.9712828Z ) 2025-07-24T03:40:54.9713202Z all_branches: bool = ( 2025-07-24T03:40:54.9713827Z False # If True, the experiment is also enabled on the exception branches 2025-07-24T03:40:54.9714549Z ) 2025-07-24T03:40:54.9715167Z default: bool = ( 2025-07-24T03:40:54.9715788Z True # If True, the experiment is enabled by default for all queries 2025-07-24T03:40:54.9716433Z ) 2025-07-24T03:40:54.9716644Z 2025-07-24T03:40:54.9716834Z # Add more fields as needed 2025-07-24T03:40:54.9717137Z 2025-07-24T03:40:54.9717143Z 2025-07-24T03:40:54.9717340Z class Settings(NamedTuple): 2025-07-24T03:40:54.9717788Z """ 2025-07-24T03:40:54.9718247Z Settings for the experiments that can be opted into. 2025-07-24T03:40:54.9718825Z """ 2025-07-24T03:40:54.9719024Z 2025-07-24T03:40:54.9719238Z experiments: dict[str, Experiment] = {} 2025-07-24T03:40:54.9719602Z 2025-07-24T03:40:54.9719609Z 2025-07-24T03:40:54.9719818Z class ColorFormatter(logging.Formatter): 2025-07-24T03:40:54.9720451Z """Color codes the log messages based on the log level""" 2025-07-24T03:40:54.9720882Z 2025-07-24T03:40:54.9721051Z COLORS = { 2025-07-24T03:40:54.9721442Z "WARNING": "\033[33m", # Yellow 2025-07-24T03:40:54.9721959Z "ERROR": "\033[31m", # Red 2025-07-24T03:40:54.9722608Z "CRITICAL": "\033[31m", # Red 2025-07-24T03:40:54.9723118Z "INFO": "\033[0m", # Reset 2025-07-24T03:40:54.9723604Z "DEBUG": "\033[0m", # Reset 2025-07-24T03:40:54.9724159Z } 2025-07-24T03:40:54.9724547Z 2025-07-24T03:40:54.9725228Z def format(self, record: LogRecord) -> str: 2025-07-24T03:40:54.9726732Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-07-24T03:40:54.9728203Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-07-24T03:40:54.9729291Z return super().format(record) 2025-07-24T03:40:54.9729948Z 2025-07-24T03:40:54.9729964Z 2025-07-24T03:40:54.9730346Z handler = logging.StreamHandler() 2025-07-24T03:40:54.9731728Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-07-24T03:40:54.9732825Z 2025-07-24T03:40:54.9733288Z log = logging.getLogger(os.path.basename(__file__)) 2025-07-24T03:40:54.9734374Z log.addHandler(handler) 2025-07-24T03:40:54.9735496Z log.setLevel(logging.INFO) 2025-07-24T03:40:54.9736084Z 2025-07-24T03:40:54.9736100Z 2025-07-24T03:40:54.9736577Z def set_github_output(key: str, value: str) -> None: 2025-07-24T03:40:54.9737274Z """ 2025-07-24T03:40:54.9737797Z Defines outputs of the github action that invokes this script 2025-07-24T03:40:54.9738590Z """ 2025-07-24T03:40:54.9739212Z if not GITHUB_OUTPUT: 2025-07-24T03:40:54.9740519Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-07-24T03:40:54.9741681Z log.warning( 2025-07-24T03:40:54.9742534Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-07-24T03:40:54.9743470Z ) 2025-07-24T03:40:54.9759142Z print(f"::set-output name={key}::{value}") 2025-07-24T03:40:54.9760167Z return 2025-07-24T03:40:54.9760582Z 2025-07-24T03:40:54.9761204Z with open(GITHUB_OUTPUT, "a") as f: 2025-07-24T03:40:54.9762375Z log.info(f"Setting output: {key}='{value}'") 2025-07-24T03:40:54.9763520Z f.write(f"{key}={value}\n") 2025-07-24T03:40:54.9764145Z 2025-07-24T03:40:54.9764158Z 2025-07-24T03:40:54.9764702Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-07-24T03:40:54.9766042Z return frozenset( 2025-07-24T03:40:54.9767171Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-07-24T03:40:54.9768508Z ) 2025-07-24T03:40:54.9768883Z 2025-07-24T03:40:54.9768894Z 2025-07-24T03:40:54.9769212Z def parse_args() -> Any: 2025-07-24T03:40:54.9770173Z parser = ArgumentParser("Get dynamic rollout settings") 2025-07-24T03:40:54.9771802Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-07-24T03:40:54.9773288Z parser.add_argument( 2025-07-24T03:40:54.9774108Z "--github-issue-repo", 2025-07-24T03:40:54.9775154Z type=str, 2025-07-24T03:40:54.9775936Z required=False, 2025-07-24T03:40:54.9776802Z default="pytorch/test-infra", 2025-07-24T03:40:54.9777844Z help="GitHub repo to get the issue", 2025-07-24T03:40:54.9778849Z ) 2025-07-24T03:40:54.9779499Z parser.add_argument( 2025-07-24T03:40:54.9780306Z "--github-repo", 2025-07-24T03:40:54.9780889Z type=str, 2025-07-24T03:40:54.9781298Z required=True, 2025-07-24T03:40:54.9781767Z help="GitHub repo where CI is running", 2025-07-24T03:40:54.9782303Z ) 2025-07-24T03:40:54.9782683Z parser.add_argument( 2025-07-24T03:40:54.9783294Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-07-24T03:40:54.9783967Z ) 2025-07-24T03:40:54.9784334Z parser.add_argument( 2025-07-24T03:40:54.9785202Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-07-24T03:40:54.9785908Z ) 2025-07-24T03:40:54.9786284Z parser.add_argument( 2025-07-24T03:40:54.9786940Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-07-24T03:40:54.9787846Z ) 2025-07-24T03:40:54.9788223Z parser.add_argument( 2025-07-24T03:40:54.9788879Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-07-24T03:40:54.9789614Z ) 2025-07-24T03:40:54.9789990Z parser.add_argument( 2025-07-24T03:40:54.9790452Z "--github-ref-type", 2025-07-24T03:40:54.9790909Z type=str, 2025-07-24T03:40:54.9791308Z required=True, 2025-07-24T03:40:54.9791788Z help="Current GitHub ref type, branch or tag", 2025-07-24T03:40:54.9792356Z ) 2025-07-24T03:40:54.9792747Z parser.add_argument( 2025-07-24T03:40:54.9843956Z "--eligible-experiments", 2025-07-24T03:40:54.9845172Z type=_str_comma_separated_to_set, 2025-07-24T03:40:54.9846039Z required=False, 2025-07-24T03:40:54.9846494Z default="", 2025-07-24T03:40:54.9847373Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-07-24T03:40:54.9848308Z ) 2025-07-24T03:40:54.9848677Z parser.add_argument( 2025-07-24T03:40:54.9849143Z "--opt-out-experiments", 2025-07-24T03:40:54.9849650Z type=_str_comma_separated_to_set, 2025-07-24T03:40:54.9850169Z required=False, 2025-07-24T03:40:54.9850577Z default="", 2025-07-24T03:40:54.9850967Z help=( 2025-07-24T03:40:54.9851638Z "comma separated list of experiments to opt-out of. If unset, no opt-outs will occur. " 2025-07-24T03:40:54.9852770Z "If the same experiment is listed both here and in '--eligible-experiments' opt-out will take priority." 2025-07-24T03:40:54.9853604Z ), 2025-07-24T03:40:54.9853954Z ) 2025-07-24T03:40:54.9854328Z parser.add_argument( 2025-07-24T03:40:54.9854756Z "--pr-number", 2025-07-24T03:40:54.9855430Z type=str, 2025-07-24T03:40:54.9855831Z required=False, 2025-07-24T03:40:54.9856247Z default="", 2025-07-24T03:40:54.9856951Z help="the optional PR number where this is run", 2025-07-24T03:40:54.9857534Z ) 2025-07-24T03:40:54.9857739Z 2025-07-24T03:40:54.9857934Z return parser.parse_args() 2025-07-24T03:40:54.9858237Z 2025-07-24T03:40:54.9858243Z 2025-07-24T03:40:54.9858653Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-07-24T03:40:54.9859413Z auth = Auth.Token(github_token) 2025-07-24T03:40:54.9859912Z return Github(auth=auth) 2025-07-24T03:40:54.9860208Z 2025-07-24T03:40:54.9860215Z 2025-07-24T03:40:54.9860668Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-07-24T03:40:54.9861464Z repo = gh.get_repo(repo) 2025-07-24T03:40:54.9861954Z return repo.get_issue(number=issue_num) 2025-07-24T03:40:54.9862310Z 2025-07-24T03:40:54.9862316Z 2025-07-24T03:40:54.9862508Z def get_potential_pr_author( 2025-07-24T03:40:54.9863138Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-07-24T03:40:54.9863825Z ) -> str: 2025-07-24T03:40:54.9864337Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-07-24T03:40:54.9865380Z # Fetch the actual username from the original PR. The PR number is 2025-07-24T03:40:54.9866140Z # embedded in the tag name: ciflow// 2025-07-24T03:40:54.9866557Z 2025-07-24T03:40:54.9866746Z gh = get_gh_client(github_token) 2025-07-24T03:40:54.9867072Z 2025-07-24T03:40:54.9867346Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-07-24T03:40:54.9867965Z split_tag = ref_name.split("/") 2025-07-24T03:40:54.9868468Z if ( 2025-07-24T03:40:54.9868849Z len(split_tag) == 3 2025-07-24T03:40:54.9869329Z and split_tag[0] == "ciflow" 2025-07-24T03:40:54.9869853Z and split_tag[2].isnumeric() 2025-07-24T03:40:54.9870345Z ): 2025-07-24T03:40:54.9870730Z pr_number = split_tag[2] 2025-07-24T03:40:54.9871425Z try: 2025-07-24T03:40:54.9871864Z repository = gh.get_repo(repo) 2025-07-24T03:40:54.9872476Z pull = repository.get_pull(number=int(pr_number)) 2025-07-24T03:40:54.9873080Z except Exception as e: 2025-07-24T03:40:54.9873593Z raise Exception( # noqa: TRY002 2025-07-24T03:40:54.9874262Z f"issue with pull request {pr_number} from repo {repository}" 2025-07-24T03:40:54.9875086Z ) from e 2025-07-24T03:40:54.9875643Z return pull.user.login # type: ignore[no-any-return] 2025-07-24T03:40:54.9876345Z # In all other cases, return the original input username 2025-07-24T03:40:54.9876926Z return username 2025-07-24T03:40:54.9877160Z 2025-07-24T03:40:54.9877167Z 2025-07-24T03:40:54.9877391Z def is_exception_branch(branch: str) -> bool: 2025-07-24T03:40:54.9877911Z """ 2025-07-24T03:40:54.9878551Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-07-24T03:40:54.9879321Z """ 2025-07-24T03:40:54.9879863Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-07-24T03:40:54.9880381Z 2025-07-24T03:40:54.9880388Z 2025-07-24T03:40:54.9880587Z def load_yaml(yaml_text: str) -> Any: 2025-07-24T03:40:54.9881074Z try: 2025-07-24T03:40:54.9881460Z data = yaml.safe_load(yaml_text) 2025-07-24T03:40:54.9881961Z return data 2025-07-24T03:40:54.9882375Z except yaml.YAMLError: 2025-07-24T03:40:54.9882849Z log.exception("Error loading YAML") 2025-07-24T03:40:54.9883360Z raise 2025-07-24T03:40:54.9883576Z 2025-07-24T03:40:54.9883583Z 2025-07-24T03:40:54.9884014Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-07-24T03:40:54.9884750Z """ 2025-07-24T03:40:54.9885593Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-07-24T03:40:54.9886198Z 2025-07-24T03:40:54.9886684Z If the issue body contains "---" then the text above that is the settings 2025-07-24T03:40:54.9887453Z and the text below is the list of opted in users. 2025-07-24T03:40:54.9887854Z 2025-07-24T03:40:54.9888225Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-07-24T03:40:54.9888928Z """ 2025-07-24T03:40:54.9889372Z rollout_state_parts = rollout_state.split("---") 2025-07-24T03:40:54.9889989Z if len(rollout_state_parts) >= 2: 2025-07-24T03:40:54.9890590Z return rollout_state_parts[0], rollout_state_parts[1] 2025-07-24T03:40:54.9891177Z else: 2025-07-24T03:40:54.9891560Z return "", rollout_state 2025-07-24T03:40:54.9891871Z 2025-07-24T03:40:54.9891878Z 2025-07-24T03:40:54.9892081Z class UserOptins(dict[str, list[str]]): 2025-07-24T03:40:54.9892588Z """ 2025-07-24T03:40:54.9893099Z Dictionary of users with a list of features they have opted into 2025-07-24T03:40:54.9893744Z """ 2025-07-24T03:40:54.9893940Z 2025-07-24T03:40:54.9893948Z 2025-07-24T03:40:54.9894296Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-07-24T03:40:54.9895145Z """ 2025-07-24T03:40:54.9895865Z 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:40:54.9896550Z 2025-07-24T03:40:54.9897173Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-07-24T03:40:54.9898169Z - Example line: "@User1,lf,split_build" 2025-07-24T03:40:54.9898855Z - A "#" prefix indicates the user is opted out of all experiments 2025-07-24T03:40:54.9899334Z 2025-07-24T03:40:54.9899341Z 2025-07-24T03:40:54.9899503Z """ 2025-07-24T03:40:54.9899877Z optins = UserOptins() 2025-07-24T03:40:54.9900361Z for user in user_optin_text.split("\n"): 2025-07-24T03:40:54.9900915Z user = user.strip("\r\n\t -") 2025-07-24T03:40:54.9901453Z if not user or not user.startswith("@"): 2025-07-24T03:40:54.9902175Z # Not a valid user. Skip 2025-07-24T03:40:54.9902664Z continue 2025-07-24T03:40:54.9902910Z 2025-07-24T03:40:54.9903078Z if user: 2025-07-24T03:40:54.9903521Z usr_name = user.split(",")[0].strip("@") 2025-07-24T03:40:54.9904213Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-07-24T03:40:54.9904710Z 2025-07-24T03:40:54.9905405Z return optins 2025-07-24T03:40:54.9905668Z 2025-07-24T03:40:54.9905674Z 2025-07-24T03:40:54.9905962Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-07-24T03:40:54.9906567Z """ 2025-07-24T03:40:54.9906970Z Check if the experiment name is valid. 2025-07-24T03:40:54.9907485Z A valid name: 2025-07-24T03:40:54.9908118Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-07-24T03:40:54.9909079Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-07-24T03:40:54.9909811Z - Cannot contain spaces 2025-07-24T03:40:54.9910281Z """ 2025-07-24T03:40:54.9910487Z 2025-07-24T03:40:54.9910749Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-07-24T03:40:54.9911439Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-07-24T03:40:54.9911885Z 2025-07-24T03:40:54.9912042Z if valid: 2025-07-24T03:40:54.9912415Z return True 2025-07-24T03:40:54.9912649Z 2025-07-24T03:40:54.9912814Z log.error( 2025-07-24T03:40:54.9914283Z 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:40:54.9916383Z ) 2025-07-24T03:40:54.9916749Z return False 2025-07-24T03:40:54.9916987Z 2025-07-24T03:40:54.9916993Z 2025-07-24T03:40:54.9917304Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-07-24T03:40:54.9917916Z """ 2025-07-24T03:40:54.9918661Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-07-24T03:40:54.9919414Z """ 2025-07-24T03:40:54.9919764Z try: 2025-07-24T03:40:54.9920131Z if settings_text: 2025-07-24T03:40:54.9920852Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-07-24T03:40:54.9921641Z # for easy reading 2025-07-24T03:40:54.9922412Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-07-24T03:40:54.9923311Z # the backtick character in shell commands. 2025-07-24T03:40:54.9923904Z backtick = chr(96) # backtick character 2025-07-24T03:40:54.9924567Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-07-24T03:40:54.9925366Z settings = load_yaml(settings_text) 2025-07-24T03:40:54.9925748Z 2025-07-24T03:40:54.9926157Z # For now we just load experiments. We can expand this if/when we add more settings 2025-07-24T03:40:54.9926929Z experiments = {} 2025-07-24T03:40:54.9927225Z 2025-07-24T03:40:54.9927611Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-07-24T03:40:54.9928386Z if not is_valid_experiment_name(exp_name): 2025-07-24T03:40:54.9929492Z # 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:40:54.9930545Z continue 2025-07-24T03:40:54.9930831Z 2025-07-24T03:40:54.9931020Z valid_settings = {} 2025-07-24T03:40:54.9931533Z for setting in exp_settings: 2025-07-24T03:40:54.9932101Z if setting not in Experiment._fields: 2025-07-24T03:40:54.9932659Z log.warning( 2025-07-24T03:40:54.9933365Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-07-24T03:40:54.9934228Z ) 2025-07-24T03:40:54.9934661Z else: 2025-07-24T03:40:54.9935290Z valid_settings[setting] = exp_settings[setting] 2025-07-24T03:40:54.9935720Z 2025-07-24T03:40:54.9935995Z experiments[exp_name] = Experiment(**valid_settings) 2025-07-24T03:40:54.9936634Z return Settings(experiments) 2025-07-24T03:40:54.9936983Z 2025-07-24T03:40:54.9937157Z except Exception: 2025-07-24T03:40:54.9937638Z log.exception("Failed to parse settings") 2025-07-24T03:40:54.9938021Z 2025-07-24T03:40:54.9938196Z return Settings() 2025-07-24T03:40:54.9938457Z 2025-07-24T03:40:54.9938464Z 2025-07-24T03:40:54.9938710Z def parse_settings(rollout_state: str) -> Settings: 2025-07-24T03:40:54.9939286Z """ 2025-07-24T03:40:54.9939723Z Parse settings, if any, from the rollout state. 2025-07-24T03:40:54.9940124Z 2025-07-24T03:40:54.9940478Z If the issue body contains "---" then the text above that is the settings 2025-07-24T03:40:54.9941251Z and the text below is the list of opted in users. 2025-07-24T03:40:54.9941653Z 2025-07-24T03:40:54.9942057Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-07-24T03:40:54.9942790Z """ 2025-07-24T03:40:54.9943332Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-24T03:40:54.9944085Z return parse_settings_from_text(settings_text) 2025-07-24T03:40:54.9944479Z 2025-07-24T03:40:54.9944486Z 2025-07-24T03:40:54.9944724Z def parse_users(rollout_state: str) -> UserOptins: 2025-07-24T03:40:54.9945387Z """ 2025-07-24T03:40:54.9945773Z Parse users from the rollout state. 2025-07-24T03:40:54.9946127Z 2025-07-24T03:40:54.9946286Z """ 2025-07-24T03:40:54.9946808Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-07-24T03:40:54.9947574Z return parse_user_opt_in_from_text(users_text) 2025-07-24T03:40:54.9947975Z 2025-07-24T03:40:54.9947981Z 2025-07-24T03:40:54.9948534Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-24T03:40:54.9949299Z """ 2025-07-24T03:40:54.9949716Z Check if a user is opted into an experiment 2025-07-24T03:40:54.9950248Z """ 2025-07-24T03:40:54.9950708Z return experiment_name in user_optins.get(user, []) 2025-07-24T03:40:54.9951127Z 2025-07-24T03:40:54.9951134Z 2025-07-24T03:40:54.9951559Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-07-24T03:40:54.9952304Z """ 2025-07-24T03:40:54.9952772Z Check if a user explicitly opted out of an experiment 2025-07-24T03:40:54.9953358Z """ 2025-07-24T03:40:54.9953854Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-07-24T03:40:54.9954542Z experiment_optout = "-" + experiment_name 2025-07-24T03:40:54.9955297Z if experiment_optout not in user_optins.get(user, []): 2025-07-24T03:40:54.9955911Z return False 2025-07-24T03:40:54.9956174Z 2025-07-24T03:40:54.9956448Z if is_user_opted_in(user, user_optins, experiment_name): 2025-07-24T03:40:54.9957053Z log.warning( 2025-07-24T03:40:54.9957849Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-07-24T03:40:54.9958738Z ) 2025-07-24T03:40:54.9958943Z 2025-07-24T03:40:54.9959104Z return True 2025-07-24T03:40:54.9959338Z 2025-07-24T03:40:54.9959344Z 2025-07-24T03:40:54.9959526Z def get_runner_prefix( 2025-07-24T03:40:54.9959953Z rollout_state: str, 2025-07-24T03:40:54.9960412Z workflow_requestors: Iterable[str], 2025-07-24T03:40:54.9960930Z branch: str, 2025-07-24T03:40:54.9961401Z eligible_experiments: frozenset[str] = frozenset(), 2025-07-24T03:40:54.9962063Z opt_out_experiments: frozenset[str] = frozenset(), 2025-07-24T03:40:54.9962638Z is_canary: bool = False, 2025-07-24T03:40:54.9963092Z ) -> str: 2025-07-24T03:40:54.9963504Z settings = parse_settings(rollout_state) 2025-07-24T03:40:54.9964235Z user_optins = parse_users(rollout_state) 2025-07-24T03:40:54.9964600Z 2025-07-24T03:40:54.9964776Z fleet_prefix = "" 2025-07-24T03:40:54.9965311Z prefixes = [] 2025-07-24T03:40:54.9965952Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-07-24T03:40:54.9966894Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-07-24T03:40:54.9967611Z log.info( 2025-07-24T03:40:54.9968287Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-07-24T03:40:54.9969044Z ) 2025-07-24T03:40:54.9969433Z continue 2025-07-24T03:40:54.9969685Z 2025-07-24T03:40:54.9969869Z if opt_out_experiments: 2025-07-24T03:40:54.9970411Z if experiment_name in opt_out_experiments: 2025-07-24T03:40:54.9971040Z opt_out_exp_list = ", ".join(opt_out_experiments) 2025-07-24T03:40:54.9971627Z log.info( 2025-07-24T03:40:54.9972568Z f"Skipping experiment '{experiment_name}', as this workflow has opted-out (opted out experiments are: {opt_out_exp_list})" 2025-07-24T03:40:54.9973565Z ) 2025-07-24T03:40:54.9973953Z continue 2025-07-24T03:40:54.9974225Z 2025-07-24T03:40:54.9974412Z if eligible_experiments: 2025-07-24T03:40:54.9975104Z if experiment_name not in eligible_experiments: 2025-07-24T03:40:54.9975738Z exp_list = ", ".join(eligible_experiments) 2025-07-24T03:40:54.9976305Z log.info( 2025-07-24T03:40:54.9977085Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-07-24T03:40:54.9977923Z ) 2025-07-24T03:40:54.9978313Z continue 2025-07-24T03:40:54.9978786Z elif not experiment_settings.default: 2025-07-24T03:40:54.9979315Z log.info( 2025-07-24T03:40:54.9980105Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-07-24T03:40:54.9980867Z ) 2025-07-24T03:40:54.9981243Z continue 2025-07-24T03:40:54.9981489Z 2025-07-24T03:40:54.9981769Z # Is any workflow_requestor opted out to this experiment? 2025-07-24T03:40:54.9982383Z opted_out_users = [ 2025-07-24T03:40:54.9982832Z requestor 2025-07-24T03:40:54.9983277Z for requestor in workflow_requestors 2025-07-24T03:40:54.9983942Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-07-24T03:40:54.9984571Z ] 2025-07-24T03:40:54.9984782Z 2025-07-24T03:40:54.9985065Z if opted_out_users: 2025-07-24T03:40:54.9985517Z log.info( 2025-07-24T03:40:54.9986123Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-07-24T03:40:54.9986822Z ) 2025-07-24T03:40:54.9987197Z continue 2025-07-24T03:40:54.9987448Z 2025-07-24T03:40:54.9987735Z # Is any workflow_requestor opted in to this experiment? 2025-07-24T03:40:54.9988367Z opted_in_users = [ 2025-07-24T03:40:54.9988825Z requestor 2025-07-24T03:40:54.9989276Z for requestor in workflow_requestors 2025-07-24T03:40:54.9989948Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-07-24T03:40:54.9990570Z ] 2025-07-24T03:40:54.9990773Z 2025-07-24T03:40:54.9990947Z enabled = False 2025-07-24T03:40:54.9991381Z if opted_in_users: 2025-07-24T03:40:54.9991818Z log.info( 2025-07-24T03:40:54.9992422Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-07-24T03:40:54.9993105Z ) 2025-07-24T03:40:54.9993506Z enabled = True 2025-07-24T03:40:54.9993787Z 2025-07-24T03:40:54.9994013Z elif experiment_settings.rollout_perc: 2025-07-24T03:40:54.9994941Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-07-24T03:40:54.9996028Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-07-24T03:40:54.9996687Z log.info( 2025-07-24T03:40:54.9997558Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-07-24T03:40:54.9998486Z ) 2025-07-24T03:40:54.9998899Z enabled = True 2025-07-24T03:40:54.9999197Z 2025-07-24T03:40:54.9999371Z if enabled: 2025-07-24T03:40:54.9999786Z label = experiment_name 2025-07-24T03:40:55.0000347Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-07-24T03:40:55.0001185Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-07-24T03:40:55.0002083Z # - If it's enabled, then we always list it's prefix first 2025-07-24T03:40:55.0002845Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-07-24T03:40:55.0003527Z if is_canary: 2025-07-24T03:40:55.0004016Z label += CANARY_FLEET_SUFFIX 2025-07-24T03:40:55.0004577Z fleet_prefix = label 2025-07-24T03:40:55.0005173Z else: 2025-07-24T03:40:55.0005596Z prefixes.append(label) 2025-07-24T03:40:55.0005945Z 2025-07-24T03:40:55.0006134Z if len(prefixes) > 1: 2025-07-24T03:40:55.0006575Z log.error( 2025-07-24T03:40:55.0007623Z 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:40:55.0008744Z ) 2025-07-24T03:40:55.0009134Z prefixes = prefixes[:1] 2025-07-24T03:40:55.0009440Z 2025-07-24T03:40:55.0009635Z # Fleet always comes first 2025-07-24T03:40:55.0010102Z if fleet_prefix: 2025-07-24T03:40:55.0010547Z prefixes.insert(0, fleet_prefix) 2025-07-24T03:40:55.0010907Z 2025-07-24T03:40:55.0011338Z return ".".join(prefixes) + "." if prefixes else "" 2025-07-24T03:40:55.0011765Z 2025-07-24T03:40:55.0011772Z 2025-07-24T03:40:55.0012215Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-07-24T03:40:55.0012995Z """ 2025-07-24T03:40:55.0013579Z Gets the first comment of the issue, which contains the desired rollout state. 2025-07-24T03:40:55.0014138Z 2025-07-24T03:40:55.0014557Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-07-24T03:40:55.0015368Z """ 2025-07-24T03:40:55.0015756Z gh = get_gh_client(github_token) 2025-07-24T03:40:55.0016286Z issue = get_issue(gh, repo, issue_num) 2025-07-24T03:40:55.0016919Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-07-24T03:40:55.0017362Z 2025-07-24T03:40:55.0017369Z 2025-07-24T03:40:55.0017768Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-07-24T03:40:55.0018533Z for _ in range(num_retries): 2025-07-24T03:40:55.0019023Z try: 2025-07-24T03:40:55.0019446Z req = Request(url=url, headers=headers) 2025-07-24T03:40:55.0020108Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-07-24T03:40:55.0020744Z return json.loads(content) 2025-07-24T03:40:55.0021274Z except Exception as e: 2025-07-24T03:40:55.0021806Z log.warning(f"Could not download {url}: {e}") 2025-07-24T03:40:55.0022219Z 2025-07-24T03:40:55.0022594Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-07-24T03:40:55.0023312Z return {} 2025-07-24T03:40:55.0023538Z 2025-07-24T03:40:55.0023544Z 2025-07-24T03:40:55.0023704Z @cache 2025-07-24T03:40:55.0024318Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-07-24T03:40:55.0025172Z """ 2025-07-24T03:40:55.0025573Z Dynamically get PR information 2025-07-24T03:40:55.0026055Z """ 2025-07-24T03:40:55.0026550Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-07-24T03:40:55.0027309Z headers = { 2025-07-24T03:40:55.0027762Z "Accept": "application/vnd.github.v3+json", 2025-07-24T03:40:55.0028370Z "Authorization": f"token {github_token}", 2025-07-24T03:40:55.0028898Z } 2025-07-24T03:40:55.0029333Z json_response: dict[str, Any] = download_json( 2025-07-24T03:40:55.0029933Z url=f"{github_api}/issues/{pr_number}", 2025-07-24T03:40:55.0030490Z headers=headers, 2025-07-24T03:40:55.0030910Z ) 2025-07-24T03:40:55.0031118Z 2025-07-24T03:40:55.0031301Z if not json_response: 2025-07-24T03:40:55.0031866Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-07-24T03:40:55.0032489Z return {} 2025-07-24T03:40:55.0032726Z 2025-07-24T03:40:55.0032912Z return json_response 2025-07-24T03:40:55.0033191Z 2025-07-24T03:40:55.0033198Z 2025-07-24T03:40:55.0033597Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-07-24T03:40:55.0034339Z """ 2025-07-24T03:40:55.0034970Z Dynamically get the latest list of labels from the pull request 2025-07-24T03:40:55.0035645Z """ 2025-07-24T03:40:55.0036120Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-07-24T03:40:55.0036732Z return { 2025-07-24T03:40:55.0037326Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-07-24T03:40:55.0038038Z } 2025-07-24T03:40:55.0038236Z 2025-07-24T03:40:55.0038243Z 2025-07-24T03:40:55.0038421Z def main() -> None: 2025-07-24T03:40:55.0038833Z args = parse_args() 2025-07-24T03:40:55.0039099Z 2025-07-24T03:40:55.0039320Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-07-24T03:40:55.0039705Z 2025-07-24T03:40:55.0039897Z # Check if the PR is opt-out 2025-07-24T03:40:55.0040397Z if args.pr_number: 2025-07-24T03:40:55.0041043Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-07-24T03:40:55.0041938Z if OPT_OUT_LABEL in labels: 2025-07-24T03:40:55.0042456Z log.info( 2025-07-24T03:40:55.0043140Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-07-24T03:40:55.0043897Z ) 2025-07-24T03:40:55.0044444Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-24T03:40:55.0045224Z sys.exit() 2025-07-24T03:40:55.0045481Z 2025-07-24T03:40:55.0045643Z try: 2025-07-24T03:40:55.0046080Z rollout_state = get_rollout_state_from_issue( 2025-07-24T03:40:55.0046781Z args.github_token, args.github_issue_repo, args.github_issue 2025-07-24T03:40:55.0047412Z ) 2025-07-24T03:40:55.0047616Z 2025-07-24T03:40:55.0047821Z username = get_potential_pr_author( 2025-07-24T03:40:55.0048352Z args.github_token, 2025-07-24T03:40:55.0048830Z args.github_repo, 2025-07-24T03:40:55.0049297Z args.github_actor, 2025-07-24T03:40:55.0049776Z args.github_ref_type, 2025-07-24T03:40:55.0050276Z args.github_branch, 2025-07-24T03:40:55.0050739Z ) 2025-07-24T03:40:55.0050942Z 2025-07-24T03:40:55.0051232Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-07-24T03:40:55.0051686Z 2025-07-24T03:40:55.0051899Z runner_label_prefix = get_runner_prefix( 2025-07-24T03:40:55.0052449Z rollout_state, 2025-07-24T03:40:55.0052927Z (args.github_issue_owner, username), 2025-07-24T03:40:55.0053476Z args.github_branch, 2025-07-24T03:40:55.0053954Z args.eligible_experiments, 2025-07-24T03:40:55.0054486Z args.opt_out_experiments, 2025-07-24T03:40:55.0055085Z is_canary, 2025-07-24T03:40:55.0055500Z ) 2025-07-24T03:40:55.0055704Z 2025-07-24T03:40:55.0055894Z except Exception as e: 2025-07-24T03:40:55.0056334Z log.error( 2025-07-24T03:40:55.0056999Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-07-24T03:40:55.0057900Z ) 2025-07-24T03:40:55.0058114Z 2025-07-24T03:40:55.0058448Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-07-24T03:40:55.0058948Z 2025-07-24T03:40:55.0058955Z 2025-07-24T03:40:55.0059136Z if __name__ == "__main__": 2025-07-24T03:40:55.0059571Z main() 2025-07-24T03:40:55.0059776Z 2025-07-24T03:40:55.0154636Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-07-24T03:40:55.0155901Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-07-24T03:40:55.0184463Z shell: /usr/bin/bash -e {0} 2025-07-24T03:40:55.0185144Z env: 2025-07-24T03:40:55.0185784Z GITHUB_TOKEN: *** 2025-07-24T03:40:55.0186197Z ISSUE_NUMBER: 5132 2025-07-24T03:40:55.0186637Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-07-24T03:40:55.0187155Z ISSUE_OWNER: 2025-07-24T03:40:55.0187560Z CHECK_EXPERIMENTS: 2025-07-24T03:40:55.0187996Z OPT_OUT_EXPERIMENTS: 2025-07-24T03:40:55.0188422Z PR_NUMBER: 2025-07-24T03:40:55.0188829Z ##[endgroup] 2025-07-24T03:40:56.0003473Z Defaulting to user installation because normal site-packages is not writeable 2025-07-24T03:40:56.6639917Z Collecting urllib3==1.26.18 2025-07-24T03:40:56.7956563Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-07-24T03:40:56.8240016Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 2.3 MB/s eta 0:00:00 2025-07-24T03:40:56.8630382Z Collecting PyGithub==2.3.0 2025-07-24T03:40:56.8823889Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-07-24T03:40:56.9408092Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-07-24T03:40:56.9643532Z 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:40:56.9692855Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-07-24T03:40:56.9713769Z 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:40:56.9730315Z 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:40:57.0110218Z Collecting Deprecated (from PyGithub==2.3.0) 2025-07-24T03:40:57.0269601Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl.metadata (5.7 kB) 2025-07-24T03:40:57.0508955Z 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:40:57.1981999Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-07-24T03:40:57.2144683Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-07-24T03:40:57.3445550Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-07-24T03:40:57.3620364Z 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:40:57.3937946Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-07-24T03:40:57.4130887Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-07-24T03:40:57.4498911Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-07-24T03:40:57.4701501Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 7.3 MB/s eta 0:00:00 2025-07-24T03:40:57.4951774Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-07-24T03:40:57.5138040Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 21.1 MB/s eta 0:00:00 2025-07-24T03:40:57.5302533Z 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:40:57.5500855Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 47.5 MB/s eta 0:00:00 2025-07-24T03:40:57.5661152Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl (10.0 kB) 2025-07-24T03:40:57.5854053Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-07-24T03:40:57.5923860Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 95.0 MB/s eta 0:00:00 2025-07-24T03:40:57.6109948Z 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:40:57.6157057Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 27.9 MB/s eta 0:00:00 2025-07-24T03:40:57.6439163Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-07-24T03:40:57.6488528Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 33.5 MB/s eta 0:00:00 2025-07-24T03:40:57.9751052Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-07-24T03:40:58.5255099Z 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:40:58.6361477Z ##[group]Run curr_branch="ciflow/trunk/149961" 2025-07-24T03:40:58.6361847Z curr_branch="ciflow/trunk/149961" 2025-07-24T03:40:58.6362136Z curr_ref_type="tag" 2025-07-24T03:40:58.6362369Z echo "Current branch is '$curr_branch'" 2025-07-24T03:40:58.6362618Z  2025-07-24T03:40:58.6362798Z python3 runner_determinator.py \ 2025-07-24T03:40:58.6363075Z  --github-token "$GITHUB_TOKEN" \ 2025-07-24T03:40:58.6363338Z  --github-issue "$ISSUE_NUMBER" \ 2025-07-24T03:40:58.6363588Z  --github-branch "$curr_branch" \ 2025-07-24T03:40:58.6363854Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-07-24T03:40:58.6364127Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-07-24T03:40:58.6364402Z  --github-ref-type "$curr_ref_type" \ 2025-07-24T03:40:58.6364667Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-07-24T03:40:58.6365164Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-07-24T03:40:58.6365489Z  --opt-out-experiments "$OPT_OUT_EXPERIMENTS" \ 2025-07-24T03:40:58.6365780Z  --pr-number "${PR_NUMBER}" 2025-07-24T03:40:58.6394642Z shell: /usr/bin/bash -e {0} 2025-07-24T03:40:58.6395016Z env: 2025-07-24T03:40:58.6395597Z GITHUB_TOKEN: *** 2025-07-24T03:40:58.6395793Z ISSUE_NUMBER: 5132 2025-07-24T03:40:58.6396000Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-07-24T03:40:58.6396235Z ISSUE_OWNER: 2025-07-24T03:40:58.6396406Z CHECK_EXPERIMENTS: 2025-07-24T03:40:58.6396595Z OPT_OUT_EXPERIMENTS: 2025-07-24T03:40:58.6396773Z PR_NUMBER: 2025-07-24T03:40:58.6396941Z ##[endgroup] 2025-07-24T03:40:58.6447505Z Current branch is 'ciflow/trunk/149961' 2025-07-24T03:41:00.9930485Z INFO : Setting output: label-type='' 2025-07-24T03:41:01.0330593Z Evaluate and set job outputs 2025-07-24T03:41:01.0337906Z Cleaning up orphan processes