2025-03-14T03:51:45.0784553Z Current runner version: '2.322.0' 2025-03-14T03:51:45.0810448Z ##[group]Operating System 2025-03-14T03:51:45.0811238Z Ubuntu 2025-03-14T03:51:45.0811857Z 24.04.2 2025-03-14T03:51:45.0812364Z LTS 2025-03-14T03:51:45.0812829Z ##[endgroup] 2025-03-14T03:51:45.0813410Z ##[group]Runner Image 2025-03-14T03:51:45.0813975Z Image: ubuntu-24.04 2025-03-14T03:51:45.0814484Z Version: 20250309.1.0 2025-03-14T03:51:45.0815905Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250309.1/images/ubuntu/Ubuntu2404-Readme.md 2025-03-14T03:51:45.0817347Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250309.1 2025-03-14T03:51:45.0818302Z ##[endgroup] 2025-03-14T03:51:45.0818836Z ##[group]Runner Image Provisioner 2025-03-14T03:51:45.0819430Z 2.0.422.1 2025-03-14T03:51:45.0819955Z ##[endgroup] 2025-03-14T03:51:45.0822086Z ##[group]GITHUB_TOKEN Permissions 2025-03-14T03:51:45.0824328Z Actions: read 2025-03-14T03:51:45.0825076Z Attestations: read 2025-03-14T03:51:45.0826066Z Checks: read 2025-03-14T03:51:45.0826582Z Contents: read 2025-03-14T03:51:45.0827074Z Deployments: read 2025-03-14T03:51:45.0827642Z Discussions: read 2025-03-14T03:51:45.0828153Z Issues: read 2025-03-14T03:51:45.0828641Z Metadata: read 2025-03-14T03:51:45.0829201Z Packages: read 2025-03-14T03:51:45.0829689Z Pages: read 2025-03-14T03:51:45.0830170Z PullRequests: read 2025-03-14T03:51:45.0830772Z RepositoryProjects: read 2025-03-14T03:51:45.0831359Z SecurityEvents: read 2025-03-14T03:51:45.0831935Z Statuses: read 2025-03-14T03:51:45.0832416Z ##[endgroup] 2025-03-14T03:51:45.0835753Z Secret source: Actions 2025-03-14T03:51:45.0836860Z Prepare workflow directory 2025-03-14T03:51:45.1324823Z Prepare all required actions 2025-03-14T03:51:45.1381605Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (aed0b7a742a2d7b7901790622829cbd2135049a4) 2025-03-14T03:51:45.1386648Z ##[group] Inputs 2025-03-14T03:51:45.1387324Z check_experiments: 2025-03-14T03:51:45.1388001Z triggering_actor: pytorchmergebot 2025-03-14T03:51:45.1388633Z issue_owner: 2025-03-14T03:51:45.1389214Z curr_branch: main 2025-03-14T03:51:45.1389725Z curr_ref_type: branch 2025-03-14T03:51:45.1390218Z issue_number: 5132 2025-03-14T03:51:45.1390807Z ##[endgroup] 2025-03-14T03:51:45.1391449Z Complete job name: unit-test / get-label-type / runner-determinator 2025-03-14T03:51:45.2101028Z ##[group]Run cat < runner_determinator.py 2025-03-14T03:51:45.2102703Z cat < runner_determinator.py 2025-03-14T03:51:45.2103268Z # flake8: noqa: G004 2025-03-14T03:51:45.2103723Z  2025-03-14T03:51:45.2104373Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-03-14T03:51:45.2105487Z # must be kept in sync. You can do it easily by running the following command: 2025-03-14T03:51:45.2106338Z # python .github/scripts/update_runner_determinator.py 2025-03-14T03:51:45.2106956Z  2025-03-14T03:51:45.2107341Z """ 2025-03-14T03:51:45.2107962Z This runner determinator is used to determine which set of runners to run a 2025-03-14T03:51:45.2108856Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-03-14T03:51:45.2109837Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-03-14T03:51:45.2110701Z of which runners should be used to run which job. 2025-03-14T03:51:45.2111268Z  2025-03-14T03:51:45.2111871Z The configuration has two parts, the settings and a list of opted-in users, 2025-03-14T03:51:45.2112793Z separated by a line containing "---". If the line is not present, the 2025-03-14T03:51:45.2113697Z settings are considered to be empty with only the second part, the user 2025-03-14T03:51:45.2114409Z list, defined. 2025-03-14T03:51:45.2114828Z  2025-03-14T03:51:45.2115510Z The first part is a YAML block that defines the rollout settings. This can be 2025-03-14T03:51:45.2116769Z used to define any settings that are needed to determine which runners to use. 2025-03-14T03:51:45.2117669Z It's fields are defined by the RolloutSettings class below. 2025-03-14T03:51:45.2118291Z  2025-03-14T03:51:45.2118889Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-03-14T03:51:45.2119795Z The user list is also a comma separated list of additional features or 2025-03-14T03:51:45.2120575Z experiments which the user could be opted in to. 2025-03-14T03:51:45.2121154Z  2025-03-14T03:51:45.2121577Z The user list has the following rules: 2025-03-14T03:51:45.2122104Z  2025-03-14T03:51:45.2122648Z - Users are GitHub usernames, which must start with the @ prefix 2025-03-14T03:51:45.2123514Z - Each user is also a comma-separated list of features/experiments to enable 2025-03-14T03:51:45.2124313Z - A "#" prefix opts the user out of all experiments 2025-03-14T03:51:45.2124887Z  2025-03-14T03:51:45.2125363Z Example config: 2025-03-14T03:51:45.2125880Z  # A list of experiments that can be opted into. 2025-03-14T03:51:45.2126577Z  # This defines the behavior they'll induce when opted into. 2025-03-14T03:51:45.2127218Z  # Expected syntax is: 2025-03-14T03:51:45.2127914Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-03-14T03:51:45.2128908Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-03-14T03:51:45.2129673Z  2025-03-14T03:51:45.2130047Z  experiments: 2025-03-14T03:51:45.2130475Z  lf: 2025-03-14T03:51:45.2130890Z  rollout_percent: 25 2025-03-14T03:51:45.2131393Z  all_branches: false 2025-03-14T03:51:45.2131880Z  default: true 2025-03-14T03:51:45.2132548Z  --- 2025-03-14T03:51:45.2133199Z  2025-03-14T03:51:45.2133796Z  # Opt-ins: 2025-03-14T03:51:45.2134510Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-03-14T03:51:45.2135981Z  # and specifying experiments to enable in a comma-separated list. 2025-03-14T03:51:45.2136835Z  # To always opt out of an experiment, prefix it with a "-". 2025-03-14T03:51:45.2137527Z  # Experiments should be from the above list. 2025-03-14T03:51:45.2138086Z  2025-03-14T03:51:45.2138545Z  @User1,-lf,split_build 2025-03-14T03:51:45.2139025Z  @User2,lf 2025-03-14T03:51:45.2139461Z  @User3,split_build 2025-03-14T03:51:45.2139920Z """ 2025-03-14T03:51:45.2140291Z  2025-03-14T03:51:45.2140661Z import json 2025-03-14T03:51:45.2141080Z import logging 2025-03-14T03:51:45.2141523Z import os 2025-03-14T03:51:45.2141932Z import random 2025-03-14T03:51:45.2142391Z import re 2025-03-14T03:51:45.2142800Z import sys 2025-03-14T03:51:45.2143269Z from argparse import ArgumentParser 2025-03-14T03:51:45.2143850Z from collections.abc import Iterable 2025-03-14T03:51:45.2144408Z from functools import cache 2025-03-14T03:51:45.2144919Z from logging import LogRecord 2025-03-14T03:51:45.2145715Z from typing import Any, NamedTuple 2025-03-14T03:51:45.2146315Z from urllib.request import Request, urlopen 2025-03-14T03:51:45.2146867Z  2025-03-14T03:51:45.2147230Z import yaml 2025-03-14T03:51:45.2147673Z from github import Auth, Github 2025-03-14T03:51:45.2148211Z from github.Issue import Issue 2025-03-14T03:51:45.2148694Z  2025-03-14T03:51:45.2149049Z  2025-03-14T03:51:45.2149493Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-03-14T03:51:45.2150385Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-03-14T03:51:45.2151276Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-03-14T03:51:45.2152014Z  2025-03-14T03:51:45.2152488Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-03-14T03:51:45.2153281Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-03-14T03:51:45.2154118Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-03-14T03:51:45.2154723Z OPT_OUT_LABEL = "no-runner-experiments" 2025-03-14T03:51:45.2155361Z  2025-03-14T03:51:45.2155777Z SETTING_EXPERIMENTS = "experiments" 2025-03-14T03:51:45.2156294Z  2025-03-14T03:51:45.2156678Z LF_FLEET_EXPERIMENT = "lf" 2025-03-14T03:51:45.2157192Z CANARY_FLEET_SUFFIX = ".c" 2025-03-14T03:51:45.2157664Z  2025-03-14T03:51:45.2158021Z  2025-03-14T03:51:45.2158407Z class Experiment(NamedTuple): 2025-03-14T03:51:45.2158940Z  rollout_perc: float = ( 2025-03-14T03:51:45.2159650Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-03-14T03:51:45.2160349Z  ) 2025-03-14T03:51:45.2160760Z  all_branches: bool = ( 2025-03-14T03:51:45.2161455Z  False # If True, the experiment is also enabled on the exception branches 2025-03-14T03:51:45.2162133Z  ) 2025-03-14T03:51:45.2162534Z  default: bool = ( 2025-03-14T03:51:45.2163161Z  True # If True, the experiment is enabled by default for all queries 2025-03-14T03:51:45.2163808Z  ) 2025-03-14T03:51:45.2164181Z  2025-03-14T03:51:45.2164566Z  # Add more fields as needed 2025-03-14T03:51:45.2165052Z  2025-03-14T03:51:45.2165617Z  2025-03-14T03:51:45.2166023Z class Settings(NamedTuple): 2025-03-14T03:51:45.2166509Z  """ 2025-03-14T03:51:45.2167024Z  Settings for the experiments that can be opted into. 2025-03-14T03:51:45.2167613Z  """ 2025-03-14T03:51:45.2167987Z  2025-03-14T03:51:45.2168410Z  experiments: dict[str, Experiment] = {} 2025-03-14T03:51:45.2168943Z  2025-03-14T03:51:45.2169448Z  2025-03-14T03:51:45.2169896Z class ColorFormatter(logging.Formatter): 2025-03-14T03:51:45.2170563Z  """Color codes the log messages based on the log level""" 2025-03-14T03:51:45.2171172Z  2025-03-14T03:51:45.2171540Z  COLORS = { 2025-03-14T03:51:45.2171995Z  "WARNING": "\033[33m", # Yellow 2025-03-14T03:51:45.2172536Z  "ERROR": "\033[31m", # Red 2025-03-14T03:51:45.2173066Z  "CRITICAL": "\033[31m", # Red 2025-03-14T03:51:45.2173600Z  "INFO": "\033[0m", # Reset 2025-03-14T03:51:45.2174136Z  "DEBUG": "\033[0m", # Reset 2025-03-14T03:51:45.2174638Z  } 2025-03-14T03:51:45.2175004Z  2025-03-14T03:51:45.2175708Z  def format(self, record: LogRecord) -> str: 2025-03-14T03:51:45.2176494Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-03-14T03:51:45.2177286Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-03-14T03:51:45.2177893Z  return super().format(record) 2025-03-14T03:51:45.2178399Z  2025-03-14T03:51:45.2178752Z  2025-03-14T03:51:45.2179154Z handler = logging.StreamHandler() 2025-03-14T03:51:45.2179912Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-03-14T03:51:45.2180633Z  2025-03-14T03:51:45.2181108Z log = logging.getLogger(os.path.basename(__file__)) 2025-03-14T03:51:45.2181721Z log.addHandler(handler) 2025-03-14T03:51:45.2182217Z log.setLevel(logging.INFO) 2025-03-14T03:51:45.2182832Z  2025-03-14T03:51:45.2183185Z  2025-03-14T03:51:45.2183657Z def set_github_output(key: str, value: str) -> None: 2025-03-14T03:51:45.2184239Z  """ 2025-03-14T03:51:45.2184782Z  Defines outputs of the github action that invokes this script 2025-03-14T03:51:45.2185543Z  """ 2025-03-14T03:51:45.2185951Z  if not GITHUB_OUTPUT: 2025-03-14T03:51:45.2187041Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-03-14T03:51:45.2188135Z  log.warning( 2025-03-14T03:51:45.2189008Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-03-14T03:51:45.2189920Z  ) 2025-03-14T03:51:45.2190392Z  print(f"::set-output name={key}::{value}") 2025-03-14T03:51:45.2190941Z  return 2025-03-14T03:51:45.2191360Z  2025-03-14T03:51:45.2191758Z  with open(GITHUB_OUTPUT, "a") as f: 2025-03-14T03:51:45.2192361Z  log.info(f"Setting output: {key}='{value}'") 2025-03-14T03:51:45.2192957Z  f.write(f"{key}={value}\n") 2025-03-14T03:51:45.2193457Z  2025-03-14T03:51:45.2193812Z  2025-03-14T03:51:45.2194341Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-03-14T03:51:45.2195004Z  return frozenset( 2025-03-14T03:51:45.2195766Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-03-14T03:51:45.2196444Z  ) 2025-03-14T03:51:45.2196815Z  2025-03-14T03:51:45.2197167Z  2025-03-14T03:51:45.2197538Z def parse_args() -> Any: 2025-03-14T03:51:45.2198151Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-03-14T03:51:45.2199034Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-03-14T03:51:45.2199820Z  parser.add_argument( 2025-03-14T03:51:45.2200321Z  "--github-issue-repo", 2025-03-14T03:51:45.2200817Z  type=str, 2025-03-14T03:51:45.2201268Z  required=False, 2025-03-14T03:51:45.2201895Z  default="pytorch/test-infra", 2025-03-14T03:51:45.2202475Z  help="GitHub repo to get the issue", 2025-03-14T03:51:45.2202997Z  ) 2025-03-14T03:51:45.2203389Z  parser.add_argument( 2025-03-14T03:51:45.2203873Z  "--github-repo", 2025-03-14T03:51:45.2204349Z  type=str, 2025-03-14T03:51:45.2204796Z  required=True, 2025-03-14T03:51:45.2205499Z  help="GitHub repo where CI is running", 2025-03-14T03:51:45.2206035Z  ) 2025-03-14T03:51:45.2206429Z  parser.add_argument( 2025-03-14T03:51:45.2207083Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-03-14T03:51:45.2207756Z  ) 2025-03-14T03:51:45.2208158Z  parser.add_argument( 2025-03-14T03:51:45.2208853Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-03-14T03:51:45.2209544Z  ) 2025-03-14T03:51:45.2209949Z  parser.add_argument( 2025-03-14T03:51:45.2210639Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-03-14T03:51:45.2211327Z  ) 2025-03-14T03:51:45.2211720Z  parser.add_argument( 2025-03-14T03:51:45.2212423Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-03-14T03:51:45.2213134Z  ) 2025-03-14T03:51:45.2213530Z  parser.add_argument( 2025-03-14T03:51:45.2214016Z  "--github-ref-type", 2025-03-14T03:51:45.2214501Z  type=str, 2025-03-14T03:51:45.2215116Z  required=True, 2025-03-14T03:51:45.2215901Z  help="Current GitHub ref type, branch or tag", 2025-03-14T03:51:45.2216467Z  ) 2025-03-14T03:51:45.2216864Z  parser.add_argument( 2025-03-14T03:51:45.2217399Z  "--eligible-experiments", 2025-03-14T03:51:45.2217954Z  type=_str_comma_separated_to_set, 2025-03-14T03:51:45.2218493Z  required=False, 2025-03-14T03:51:45.2218963Z  default="", 2025-03-14T03:51:45.2219850Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-03-14T03:51:45.2220763Z  ) 2025-03-14T03:51:45.2221164Z  parser.add_argument( 2025-03-14T03:51:45.2221652Z  "--pr-number", 2025-03-14T03:51:45.2222124Z  type=str, 2025-03-14T03:51:45.2222570Z  required=False, 2025-03-14T03:51:45.2223047Z  default="", 2025-03-14T03:51:45.2223578Z  help="the optional PR number where this is run", 2025-03-14T03:51:45.2224145Z  ) 2025-03-14T03:51:45.2224513Z  2025-03-14T03:51:45.2224916Z  return parser.parse_args() 2025-03-14T03:51:45.2225538Z  2025-03-14T03:51:45.2225886Z  2025-03-14T03:51:45.2226498Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-03-14T03:51:45.2227279Z  auth = Auth.Token(github_token) 2025-03-14T03:51:45.2227818Z  return Github(auth=auth) 2025-03-14T03:51:45.2228289Z  2025-03-14T03:51:45.2228629Z  2025-03-14T03:51:45.2229294Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-03-14T03:51:45.2230094Z  repo = gh.get_repo(repo) 2025-03-14T03:51:45.2230648Z  return repo.get_issue(number=issue_num) 2025-03-14T03:51:45.2231183Z  2025-03-14T03:51:45.2231529Z  2025-03-14T03:51:45.2231912Z def get_potential_pr_author( 2025-03-14T03:51:45.2232594Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-03-14T03:51:45.2233271Z ) -> str: 2025-03-14T03:51:45.2233961Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-03-14T03:51:45.2234790Z  # Fetch the actual username from the original PR. The PR number is 2025-03-14T03:51:45.2235653Z  # embedded in the tag name: ciflow// 2025-03-14T03:51:45.2236234Z  2025-03-14T03:51:45.2236634Z  gh = get_gh_client(github_token) 2025-03-14T03:51:45.2237137Z  2025-03-14T03:51:45.2237618Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-03-14T03:51:45.2238262Z  split_tag = ref_name.split("/") 2025-03-14T03:51:45.2238780Z  if ( 2025-03-14T03:51:45.2239213Z  len(split_tag) == 3 2025-03-14T03:51:45.2239738Z  and split_tag[0] == "ciflow" 2025-03-14T03:51:45.2240290Z  and split_tag[2].isnumeric() 2025-03-14T03:51:45.2240793Z  ): 2025-03-14T03:51:45.2241223Z  pr_number = split_tag[2] 2025-03-14T03:51:45.2241835Z  try: 2025-03-14T03:51:45.2242307Z  repository = gh.get_repo(repo) 2025-03-14T03:51:45.2242943Z  pull = repository.get_pull(number=int(pr_number)) 2025-03-14T03:51:45.2243566Z  except Exception as e: 2025-03-14T03:51:45.2244110Z  raise Exception( # noqa: TRY002 2025-03-14T03:51:45.2244818Z  f"issue with pull request {pr_number} from repo {repository}" 2025-03-14T03:51:45.2245661Z  ) from e 2025-03-14T03:51:45.2246256Z  return pull.user.login # type: ignore[no-any-return] 2025-03-14T03:51:45.2247114Z  # In all other cases, return the original input username 2025-03-14T03:51:45.2247710Z  return username 2025-03-14T03:51:45.2248136Z  2025-03-14T03:51:45.2248483Z  2025-03-14T03:51:45.2248924Z def is_exception_branch(branch: str) -> bool: 2025-03-14T03:51:45.2249460Z  """ 2025-03-14T03:51:45.2250132Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-03-14T03:51:45.2250901Z  """ 2025-03-14T03:51:45.2251462Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-03-14T03:51:45.2252133Z  2025-03-14T03:51:45.2252474Z  2025-03-14T03:51:45.2252866Z def load_yaml(yaml_text: str) -> Any: 2025-03-14T03:51:45.2253383Z  try: 2025-03-14T03:51:45.2253802Z  data = yaml.safe_load(yaml_text) 2025-03-14T03:51:45.2254333Z  return data 2025-03-14T03:51:45.2254795Z  except yaml.YAMLError: 2025-03-14T03:51:45.2255531Z  log.exception("Error loading YAML") 2025-03-14T03:51:45.2256071Z  raise 2025-03-14T03:51:45.2256465Z  2025-03-14T03:51:45.2256811Z  2025-03-14T03:51:45.2257436Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-03-14T03:51:45.2258167Z  """ 2025-03-14T03:51:45.2258814Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-03-14T03:51:45.2259549Z  2025-03-14T03:51:45.2260089Z  If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:45.2260853Z  and the text below is the list of opted in users. 2025-03-14T03:51:45.2261410Z  2025-03-14T03:51:45.2261986Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-03-14T03:51:45.2262690Z  """ 2025-03-14T03:51:45.2263165Z  rollout_state_parts = rollout_state.split("---") 2025-03-14T03:51:45.2263770Z  if len(rollout_state_parts) >= 2: 2025-03-14T03:51:45.2264547Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-03-14T03:51:45.2265320Z  else: 2025-03-14T03:51:45.2265760Z  return "", rollout_state 2025-03-14T03:51:45.2266243Z  2025-03-14T03:51:45.2266584Z  2025-03-14T03:51:45.2266989Z class UserOptins(dict[str, list[str]]): 2025-03-14T03:51:45.2267510Z  """ 2025-03-14T03:51:45.2268067Z  Dictionary of users with a list of features they have opted into 2025-03-14T03:51:45.2268712Z  """ 2025-03-14T03:51:45.2269075Z  2025-03-14T03:51:45.2269417Z  2025-03-14T03:51:45.2269957Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-03-14T03:51:45.2270625Z  """ 2025-03-14T03:51:45.2271364Z  Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-03-14T03:51:45.2272181Z  2025-03-14T03:51:45.2272985Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-03-14T03:51:45.2273970Z  - Example line: "@User1,lf,split_build" 2025-03-14T03:51:45.2274660Z  - A "#" prefix indicates the user is opted out of all experiments 2025-03-14T03:51:45.2275388Z  2025-03-14T03:51:45.2275735Z  2025-03-14T03:51:45.2276071Z  """ 2025-03-14T03:51:45.2276466Z  optins = UserOptins() 2025-03-14T03:51:45.2276999Z  for user in user_optin_text.split("\n"): 2025-03-14T03:51:45.2277570Z  user = user.strip("\r\n\t -") 2025-03-14T03:51:45.2278271Z  if not user or not user.startswith("@"): 2025-03-14T03:51:45.2278837Z  # Not a valid user. Skip 2025-03-14T03:51:45.2279340Z  continue 2025-03-14T03:51:45.2279762Z  2025-03-14T03:51:45.2280114Z  if user: 2025-03-14T03:51:45.2280606Z  usr_name = user.split(",")[0].strip("@") 2025-03-14T03:51:45.2281305Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-03-14T03:51:45.2281933Z  2025-03-14T03:51:45.2282295Z  return optins 2025-03-14T03:51:45.2282714Z  2025-03-14T03:51:45.2283059Z  2025-03-14T03:51:45.2283565Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-03-14T03:51:45.2284179Z  """ 2025-03-14T03:51:45.2284612Z  Check if the experiment name is valid. 2025-03-14T03:51:45.2285238Z  A valid name: 2025-03-14T03:51:45.2285924Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-03-14T03:51:45.2286861Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-03-14T03:51:45.2287565Z  - Cannot contain spaces 2025-03-14T03:51:45.2288050Z  """ 2025-03-14T03:51:45.2288421Z  2025-03-14T03:51:45.2288889Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-03-14T03:51:45.2289608Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-03-14T03:51:45.2290202Z  2025-03-14T03:51:45.2290550Z  if valid: 2025-03-14T03:51:45.2290963Z  return True 2025-03-14T03:51:45.2291492Z  2025-03-14T03:51:45.2291885Z  log.error( 2025-03-14T03:51:45.2293316Z  f"Invalid experiment name: {experiment_name}. Experiment names should only contain alphanumeric characters, '_', and '-'. They cannot contain spaces, and the special characters '_' and '-' cannot be the first or last characters." 2025-03-14T03:51:45.2294792Z  ) 2025-03-14T03:51:45.2295256Z  return False 2025-03-14T03:51:45.2295674Z  2025-03-14T03:51:45.2296011Z  2025-03-14T03:51:45.2296657Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-03-14T03:51:45.2297293Z  """ 2025-03-14T03:51:45.2297901Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-03-14T03:51:45.2298604Z  """ 2025-03-14T03:51:45.2298969Z  try: 2025-03-14T03:51:45.2299364Z  if settings_text: 2025-03-14T03:51:45.2300117Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-03-14T03:51:45.2300891Z  # for easy reading 2025-03-14T03:51:45.2301709Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-03-14T03:51:45.2302596Z  # the backtick character in shell commands. 2025-03-14T03:51:45.2303221Z  backtick = chr(96) # backtick character 2025-03-14T03:51:45.2303901Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-03-14T03:51:45.2304591Z  settings = load_yaml(settings_text) 2025-03-14T03:51:45.2305103Z  2025-03-14T03:51:45.2305800Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-03-14T03:51:45.2306537Z  experiments = {} 2025-03-14T03:51:45.2307002Z  2025-03-14T03:51:45.2307576Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-03-14T03:51:45.2308349Z  if not is_valid_experiment_name(exp_name): 2025-03-14T03:51:45.2309442Z  # Exclude invalid experiments from the list. We log an error, but don't raise an exception so that other experiments can still be processed. 2025-03-14T03:51:45.2310577Z  continue 2025-03-14T03:51:45.2311037Z  2025-03-14T03:51:45.2311422Z  valid_settings = {} 2025-03-14T03:51:45.2311978Z  for setting in exp_settings: 2025-03-14T03:51:45.2312569Z  if setting not in Experiment._fields: 2025-03-14T03:51:45.2313131Z  log.warning( 2025-03-14T03:51:45.2313855Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-03-14T03:51:45.2314560Z  ) 2025-03-14T03:51:45.2315015Z  else: 2025-03-14T03:51:45.2315665Z  valid_settings[setting] = exp_settings[setting] 2025-03-14T03:51:45.2316238Z  2025-03-14T03:51:45.2316720Z  experiments[exp_name] = Experiment(**valid_settings) 2025-03-14T03:51:45.2317391Z  return Settings(experiments) 2025-03-14T03:51:45.2317898Z  2025-03-14T03:51:45.2318268Z  except Exception: 2025-03-14T03:51:45.2318796Z  log.exception("Failed to parse settings") 2025-03-14T03:51:45.2319337Z  2025-03-14T03:51:45.2319697Z  return Settings() 2025-03-14T03:51:45.2320137Z  2025-03-14T03:51:45.2320475Z  2025-03-14T03:51:45.2320940Z def parse_settings(rollout_state: str) -> Settings: 2025-03-14T03:51:45.2321679Z  """ 2025-03-14T03:51:45.2322144Z  Parse settings, if any, from the rollout state. 2025-03-14T03:51:45.2322699Z  2025-03-14T03:51:45.2323249Z  If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:45.2324022Z  and the text below is the list of opted in users. 2025-03-14T03:51:45.2324585Z  2025-03-14T03:51:45.2325271Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-03-14T03:51:45.2326003Z  """ 2025-03-14T03:51:45.2326598Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:45.2327520Z  return parse_settings_from_text(settings_text) 2025-03-14T03:51:45.2328079Z  2025-03-14T03:51:45.2328421Z  2025-03-14T03:51:45.2328870Z def parse_users(rollout_state: str) -> UserOptins: 2025-03-14T03:51:45.2329435Z  """ 2025-03-14T03:51:45.2329858Z  Parse users from the rollout state. 2025-03-14T03:51:45.2330366Z  2025-03-14T03:51:45.2330712Z  """ 2025-03-14T03:51:45.2331274Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:45.2332024Z  return parse_user_opt_in_from_text(users_text) 2025-03-14T03:51:45.2332579Z  2025-03-14T03:51:45.2332923Z  2025-03-14T03:51:45.2333550Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:45.2334284Z  """ 2025-03-14T03:51:45.2334733Z  Check if a user is opted into an experiment 2025-03-14T03:51:45.2335379Z  """ 2025-03-14T03:51:45.2335875Z  return experiment_name in user_optins.get(user, []) 2025-03-14T03:51:45.2336446Z  2025-03-14T03:51:45.2336780Z  2025-03-14T03:51:45.2337401Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:45.2338143Z  """ 2025-03-14T03:51:45.2338630Z  Check if a user explicitly opted out of an experiment 2025-03-14T03:51:45.2339214Z  """ 2025-03-14T03:51:45.2339745Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-03-14T03:51:45.2340565Z  experiment_optout = "-" + experiment_name 2025-03-14T03:51:45.2341224Z  if experiment_optout not in user_optins.get(user, []): 2025-03-14T03:51:45.2341822Z  return False 2025-03-14T03:51:45.2342248Z  2025-03-14T03:51:45.2342724Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-03-14T03:51:45.2343327Z  log.warning( 2025-03-14T03:51:45.2344132Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-03-14T03:51:45.2344987Z  ) 2025-03-14T03:51:45.2345457Z  2025-03-14T03:51:45.2345818Z  return True 2025-03-14T03:51:45.2346221Z  2025-03-14T03:51:45.2346559Z  2025-03-14T03:51:45.2346929Z def get_runner_prefix( 2025-03-14T03:51:45.2347398Z  rollout_state: str, 2025-03-14T03:51:45.2347902Z  workflow_requestors: Iterable[str], 2025-03-14T03:51:45.2348431Z  branch: str, 2025-03-14T03:51:45.2348961Z  eligible_experiments: frozenset[str] = frozenset(), 2025-03-14T03:51:45.2349568Z  is_canary: bool = False, 2025-03-14T03:51:45.2350044Z ) -> str: 2025-03-14T03:51:45.2350505Z  settings = parse_settings(rollout_state) 2025-03-14T03:51:45.2351109Z  user_optins = parse_users(rollout_state) 2025-03-14T03:51:45.2351628Z  2025-03-14T03:51:45.2351998Z  fleet_prefix = "" 2025-03-14T03:51:45.2352450Z  prefixes = [] 2025-03-14T03:51:45.2353114Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-03-14T03:51:45.2354053Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-03-14T03:51:45.2354766Z  log.info( 2025-03-14T03:51:45.2355560Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-03-14T03:51:45.2356328Z  ) 2025-03-14T03:51:45.2356733Z  continue 2025-03-14T03:51:45.2357154Z  2025-03-14T03:51:45.2357539Z  if eligible_experiments: 2025-03-14T03:51:45.2358249Z  if experiment_name not in eligible_experiments: 2025-03-14T03:51:45.2358894Z  exp_list = ", ".join(eligible_experiments) 2025-03-14T03:51:45.2359451Z  log.info( 2025-03-14T03:51:45.2360247Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-03-14T03:51:45.2361062Z  ) 2025-03-14T03:51:45.2361489Z  continue 2025-03-14T03:51:45.2362002Z  elif not experiment_settings.default: 2025-03-14T03:51:45.2362533Z  log.info( 2025-03-14T03:51:45.2363224Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-03-14T03:51:45.2363951Z  ) 2025-03-14T03:51:45.2364366Z  continue 2025-03-14T03:51:45.2364788Z  2025-03-14T03:51:45.2365358Z  # Is any workflow_requestor opted out to this experiment? 2025-03-14T03:51:45.2365996Z  opted_out_users = [ 2025-03-14T03:51:45.2366478Z  requestor 2025-03-14T03:51:45.2366971Z  for requestor in workflow_requestors 2025-03-14T03:51:45.2367643Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-03-14T03:51:45.2368266Z  ] 2025-03-14T03:51:45.2368636Z  2025-03-14T03:51:45.2369003Z  if opted_out_users: 2025-03-14T03:51:45.2369483Z  log.info( 2025-03-14T03:51:45.2370132Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-03-14T03:51:45.2370947Z  ) 2025-03-14T03:51:45.2371352Z  continue 2025-03-14T03:51:45.2371774Z  2025-03-14T03:51:45.2372251Z  # Is any workflow_requestor opted in to this experiment? 2025-03-14T03:51:45.2372867Z  opted_in_users = [ 2025-03-14T03:51:45.2373351Z  requestor 2025-03-14T03:51:45.2373844Z  for requestor in workflow_requestors 2025-03-14T03:51:45.2374522Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-03-14T03:51:45.2375230Z  ] 2025-03-14T03:51:45.2375611Z  2025-03-14T03:51:45.2375971Z  enabled = False 2025-03-14T03:51:45.2376435Z  if opted_in_users: 2025-03-14T03:51:45.2376906Z  log.info( 2025-03-14T03:51:45.2377538Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-03-14T03:51:45.2378211Z  ) 2025-03-14T03:51:45.2378625Z  enabled = True 2025-03-14T03:51:45.2379081Z  2025-03-14T03:51:45.2379491Z  elif experiment_settings.rollout_perc: 2025-03-14T03:51:45.2380331Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-03-14T03:51:45.2381260Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-03-14T03:51:45.2381901Z  log.info( 2025-03-14T03:51:45.2382783Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-03-14T03:51:45.2383677Z  ) 2025-03-14T03:51:45.2384113Z  enabled = True 2025-03-14T03:51:45.2384592Z  2025-03-14T03:51:45.2384956Z  if enabled: 2025-03-14T03:51:45.2385517Z  label = experiment_name 2025-03-14T03:51:45.2386102Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-03-14T03:51:45.2386924Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-03-14T03:51:45.2387898Z  # - If it's enabled, then we always list it's prefix first 2025-03-14T03:51:45.2388672Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-03-14T03:51:45.2389326Z  if is_canary: 2025-03-14T03:51:45.2389850Z  label += CANARY_FLEET_SUFFIX 2025-03-14T03:51:45.2390407Z  fleet_prefix = label 2025-03-14T03:51:45.2390903Z  else: 2025-03-14T03:51:45.2391366Z  prefixes.append(label) 2025-03-14T03:51:45.2391861Z  2025-03-14T03:51:45.2392226Z  if len(prefixes) > 1: 2025-03-14T03:51:45.2392694Z  log.error( 2025-03-14T03:51:45.2393743Z  f"Only a fleet and one other experiment can be enabled for a job at any time. Enabling {prefixes[0]} and ignoring the rest, which are {', '.join(prefixes[1:])}" 2025-03-14T03:51:45.2394821Z  ) 2025-03-14T03:51:45.2395321Z  prefixes = prefixes[:1] 2025-03-14T03:51:45.2395807Z  2025-03-14T03:51:45.2396186Z  # Fleet always comes first 2025-03-14T03:51:45.2396680Z  if fleet_prefix: 2025-03-14T03:51:45.2397160Z  prefixes.insert(0, fleet_prefix) 2025-03-14T03:51:45.2397666Z  2025-03-14T03:51:45.2398120Z  return ".".join(prefixes) + "." if prefixes else "" 2025-03-14T03:51:45.2398677Z  2025-03-14T03:51:45.2399013Z  2025-03-14T03:51:45.2399644Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-03-14T03:51:45.2400391Z  """ 2025-03-14T03:51:45.2400989Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-03-14T03:51:45.2401801Z  2025-03-14T03:51:45.2402378Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-03-14T03:51:45.2403068Z  """ 2025-03-14T03:51:45.2403492Z  gh = get_gh_client(github_token) 2025-03-14T03:51:45.2404041Z  issue = get_issue(gh, repo, issue_num) 2025-03-14T03:51:45.2404691Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-03-14T03:51:45.2405375Z  2025-03-14T03:51:45.2405726Z  2025-03-14T03:51:45.2406324Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-03-14T03:51:45.2407062Z  for _ in range(num_retries): 2025-03-14T03:51:45.2407550Z  try: 2025-03-14T03:51:45.2408006Z  req = Request(url=url, headers=headers) 2025-03-14T03:51:45.2408665Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-03-14T03:51:45.2409321Z  return json.loads(content) 2025-03-14T03:51:45.2409854Z  except Exception as e: 2025-03-14T03:51:45.2410415Z  log.warning(f"Could not download {url}: {e}") 2025-03-14T03:51:45.2410971Z  2025-03-14T03:51:45.2411546Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-03-14T03:51:45.2412246Z  return {} 2025-03-14T03:51:45.2412646Z  2025-03-14T03:51:45.2412977Z  2025-03-14T03:51:45.2413318Z @cache 2025-03-14T03:51:45.2413960Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-03-14T03:51:45.2414696Z  """ 2025-03-14T03:51:45.2415106Z  Dynamically get PR information 2025-03-14T03:51:45.2415688Z  """ 2025-03-14T03:51:45.2416212Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-03-14T03:51:45.2416842Z  headers = { 2025-03-14T03:51:45.2417334Z  "Accept": "application/vnd.github.v3+json", 2025-03-14T03:51:45.2417975Z  "Authorization": f"token {github_token}", 2025-03-14T03:51:45.2418507Z  } 2025-03-14T03:51:45.2419074Z  json_response: dict[str, Any] = download_json( 2025-03-14T03:51:45.2419693Z  url=f"{github_api}/issues/{pr_number}", 2025-03-14T03:51:45.2420228Z  headers=headers, 2025-03-14T03:51:45.2420681Z  ) 2025-03-14T03:51:45.2421043Z  2025-03-14T03:51:45.2421403Z  if not json_response: 2025-03-14T03:51:45.2422009Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-03-14T03:51:45.2422627Z  return {} 2025-03-14T03:51:45.2423043Z  2025-03-14T03:51:45.2423410Z  return json_response 2025-03-14T03:51:45.2423851Z  2025-03-14T03:51:45.2424190Z  2025-03-14T03:51:45.2424774Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-03-14T03:51:45.2425601Z  """ 2025-03-14T03:51:45.2426150Z  Dynamically get the latest list of labels from the pull request 2025-03-14T03:51:45.2426797Z  """ 2025-03-14T03:51:45.2427300Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-03-14T03:51:45.2427906Z  return { 2025-03-14T03:51:45.2428510Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-03-14T03:51:45.2429184Z  } 2025-03-14T03:51:45.2429542Z  2025-03-14T03:51:45.2429875Z  2025-03-14T03:51:45.2430232Z def main() -> None: 2025-03-14T03:51:45.2430685Z  args = parse_args() 2025-03-14T03:51:45.2431128Z  2025-03-14T03:51:45.2431631Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-03-14T03:51:45.2432278Z  2025-03-14T03:51:45.2432653Z  # Check if the PR is opt-out 2025-03-14T03:51:45.2433161Z  if args.pr_number: 2025-03-14T03:51:45.2433844Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-03-14T03:51:45.2434603Z  if OPT_OUT_LABEL in labels: 2025-03-14T03:51:45.2435106Z  log.info( 2025-03-14T03:51:45.2435927Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-03-14T03:51:45.2436679Z  ) 2025-03-14T03:51:45.2437269Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:45.2437945Z  sys.exit() 2025-03-14T03:51:45.2438377Z  2025-03-14T03:51:45.2438721Z  try: 2025-03-14T03:51:45.2439189Z  rollout_state = get_rollout_state_from_issue( 2025-03-14T03:51:45.2439910Z  args.github_token, args.github_issue_repo, args.github_issue 2025-03-14T03:51:45.2440541Z  ) 2025-03-14T03:51:45.2440918Z  2025-03-14T03:51:45.2441325Z  username = get_potential_pr_author( 2025-03-14T03:51:45.2441866Z  args.github_token, 2025-03-14T03:51:45.2442374Z  args.github_repo, 2025-03-14T03:51:45.2442866Z  args.github_actor, 2025-03-14T03:51:45.2443375Z  args.github_ref_type, 2025-03-14T03:51:45.2443885Z  args.github_branch, 2025-03-14T03:51:45.2444357Z  ) 2025-03-14T03:51:45.2444731Z  2025-03-14T03:51:45.2445299Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-03-14T03:51:45.2445904Z  2025-03-14T03:51:45.2446323Z  runner_label_prefix = get_runner_prefix( 2025-03-14T03:51:45.2446871Z  rollout_state, 2025-03-14T03:51:45.2447392Z  (args.github_issue_owner, username), 2025-03-14T03:51:45.2447940Z  args.github_branch, 2025-03-14T03:51:45.2448624Z  args.eligible_experiments, 2025-03-14T03:51:45.2449409Z  is_canary, 2025-03-14T03:51:45.2450307Z  ) 2025-03-14T03:51:45.2450927Z  2025-03-14T03:51:45.2451312Z  except Exception as e: 2025-03-14T03:51:45.2451974Z  log.error( 2025-03-14T03:51:45.2453114Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-03-14T03:51:45.2453868Z  ) 2025-03-14T03:51:45.2454248Z  2025-03-14T03:51:45.2454787Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:45.2455648Z  2025-03-14T03:51:45.2455999Z  2025-03-14T03:51:45.2456362Z if __name__ == "__main__": 2025-03-14T03:51:45.2456844Z  main() 2025-03-14T03:51:45.2457227Z  2025-03-14T03:51:45.2457570Z EOF 2025-03-14T03:51:45.2457935Z  2025-03-14T03:51:45.2458307Z cat runner_determinator.py 2025-03-14T03:51:45.2942886Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:45.2943691Z env: 2025-03-14T03:51:45.2944386Z GITHUB_TOKEN: *** 2025-03-14T03:51:45.2944807Z ISSUE_NUMBER: 5132 2025-03-14T03:51:45.2945598Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:45.2946105Z ISSUE_OWNER: 2025-03-14T03:51:45.2946493Z CHECK_EXPERIMENTS: 2025-03-14T03:51:45.2946889Z PR_NUMBER: 2025-03-14T03:51:45.2947252Z ##[endgroup] 2025-03-14T03:51:45.3213288Z # flake8: noqa: G004 2025-03-14T03:51:45.3213613Z 2025-03-14T03:51:45.3214021Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-03-14T03:51:45.3214968Z # must be kept in sync. You can do it easily by running the following command: 2025-03-14T03:51:45.3215999Z # python .github/scripts/update_runner_determinator.py 2025-03-14T03:51:45.3216672Z 2025-03-14T03:51:45.3216825Z """ 2025-03-14T03:51:45.3217365Z This runner determinator is used to determine which set of runners to run a 2025-03-14T03:51:45.3218188Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-03-14T03:51:45.3219056Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-03-14T03:51:45.3219823Z of which runners should be used to run which job. 2025-03-14T03:51:45.3220205Z 2025-03-14T03:51:45.3220570Z The configuration has two parts, the settings and a list of opted-in users, 2025-03-14T03:51:45.3221398Z separated by a line containing "---". If the line is not present, the 2025-03-14T03:51:45.3222216Z settings are considered to be empty with only the second part, the user 2025-03-14T03:51:45.3222860Z list, defined. 2025-03-14T03:51:45.3223080Z 2025-03-14T03:51:45.3223430Z The first part is a YAML block that defines the rollout settings. This can be 2025-03-14T03:51:45.3224304Z used to define any settings that are needed to determine which runners to use. 2025-03-14T03:51:45.3225079Z It's fields are defined by the RolloutSettings class below. 2025-03-14T03:51:45.3225835Z 2025-03-14T03:51:45.3226194Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-03-14T03:51:45.3227024Z The user list is also a comma separated list of additional features or 2025-03-14T03:51:45.3227711Z experiments which the user could be opted in to. 2025-03-14T03:51:45.3228096Z 2025-03-14T03:51:45.3228285Z The user list has the following rules: 2025-03-14T03:51:45.3228618Z 2025-03-14T03:51:45.3228909Z - Users are GitHub usernames, which must start with the @ prefix 2025-03-14T03:51:45.3229719Z - Each user is also a comma-separated list of features/experiments to enable 2025-03-14T03:51:45.3230439Z - A "#" prefix opts the user out of all experiments 2025-03-14T03:51:45.3230812Z 2025-03-14T03:51:45.3230976Z Example config: 2025-03-14T03:51:45.3231398Z # A list of experiments that can be opted into. 2025-03-14T03:51:45.3232035Z # This defines the behavior they'll induce when opted into. 2025-03-14T03:51:45.3232626Z # Expected syntax is: 2025-03-14T03:51:45.3233228Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-03-14T03:51:45.3234291Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-03-14T03:51:45.3234874Z 2025-03-14T03:51:45.3235030Z experiments: 2025-03-14T03:51:45.3235647Z lf: 2025-03-14T03:51:45.3236011Z rollout_percent: 25 2025-03-14T03:51:45.3236452Z all_branches: false 2025-03-14T03:51:45.3236876Z default: true 2025-03-14T03:51:45.3237252Z --- 2025-03-14T03:51:45.3237448Z 2025-03-14T03:51:45.3237598Z # Opt-ins: 2025-03-14T03:51:45.3238137Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-03-14T03:51:45.3238944Z # and specifying experiments to enable in a comma-separated list. 2025-03-14T03:51:45.3239666Z # To always opt out of an experiment, prefix it with a "-". 2025-03-14T03:51:45.3240276Z # Experiments should be from the above list. 2025-03-14T03:51:45.3240639Z 2025-03-14T03:51:45.3240809Z @User1,-lf,split_build 2025-03-14T03:51:45.3241221Z @User2,lf 2025-03-14T03:51:45.3241591Z @User3,split_build 2025-03-14T03:51:45.3241966Z """ 2025-03-14T03:51:45.3242149Z 2025-03-14T03:51:45.3242299Z import json 2025-03-14T03:51:45.3242650Z import logging 2025-03-14T03:51:45.3243007Z import os 2025-03-14T03:51:45.3243350Z import random 2025-03-14T03:51:45.3243702Z import re 2025-03-14T03:51:45.3244037Z import sys 2025-03-14T03:51:45.3244414Z from argparse import ArgumentParser 2025-03-14T03:51:45.3244914Z from collections.abc import Iterable 2025-03-14T03:51:45.3245821Z from functools import cache 2025-03-14T03:51:45.3246274Z from logging import LogRecord 2025-03-14T03:51:45.3246740Z from typing import Any, NamedTuple 2025-03-14T03:51:45.3247249Z from urllib.request import Request, urlopen 2025-03-14T03:51:45.3247751Z 2025-03-14T03:51:45.3247907Z import yaml 2025-03-14T03:51:45.3248278Z from github import Auth, Github 2025-03-14T03:51:45.3248745Z from github.Issue import Issue 2025-03-14T03:51:45.3249032Z 2025-03-14T03:51:45.3249038Z 2025-03-14T03:51:45.3249245Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-03-14T03:51:45.3249888Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-03-14T03:51:45.3250691Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-03-14T03:51:45.3251214Z 2025-03-14T03:51:45.3251427Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-03-14T03:51:45.3251976Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-03-14T03:51:45.3252460Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-03-14T03:51:45.3252973Z OPT_OUT_LABEL = "no-runner-experiments" 2025-03-14T03:51:45.3253307Z 2025-03-14T03:51:45.3253489Z SETTING_EXPERIMENTS = "experiments" 2025-03-14T03:51:45.3253803Z 2025-03-14T03:51:45.3253988Z LF_FLEET_EXPERIMENT = "lf" 2025-03-14T03:51:45.3254426Z CANARY_FLEET_SUFFIX = ".c" 2025-03-14T03:51:45.3254690Z 2025-03-14T03:51:45.3254696Z 2025-03-14T03:51:45.3254881Z class Experiment(NamedTuple): 2025-03-14T03:51:45.3255597Z rollout_perc: float = ( 2025-03-14T03:51:45.3256203Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-03-14T03:51:45.3256841Z ) 2025-03-14T03:51:45.3257187Z all_branches: bool = ( 2025-03-14T03:51:45.3257768Z False # If True, the experiment is also enabled on the exception branches 2025-03-14T03:51:45.3258398Z ) 2025-03-14T03:51:45.3258734Z default: bool = ( 2025-03-14T03:51:45.3259267Z True # If True, the experiment is enabled by default for all queries 2025-03-14T03:51:45.3259865Z ) 2025-03-14T03:51:45.3260047Z 2025-03-14T03:51:45.3260224Z # Add more fields as needed 2025-03-14T03:51:45.3260502Z 2025-03-14T03:51:45.3260508Z 2025-03-14T03:51:45.3260688Z class Settings(NamedTuple): 2025-03-14T03:51:45.3261105Z """ 2025-03-14T03:51:45.3261534Z Settings for the experiments that can be opted into. 2025-03-14T03:51:45.3262067Z """ 2025-03-14T03:51:45.3262247Z 2025-03-14T03:51:45.3262448Z experiments: dict[str, Experiment] = {} 2025-03-14T03:51:45.3262788Z 2025-03-14T03:51:45.3262794Z 2025-03-14T03:51:45.3263125Z class ColorFormatter(logging.Formatter): 2025-03-14T03:51:45.3263722Z """Color codes the log messages based on the log level""" 2025-03-14T03:51:45.3264132Z 2025-03-14T03:51:45.3264285Z COLORS = { 2025-03-14T03:51:45.3264659Z "WARNING": "\033[33m", # Yellow 2025-03-14T03:51:45.3265309Z "ERROR": "\033[31m", # Red 2025-03-14T03:51:45.3265804Z "CRITICAL": "\033[31m", # Red 2025-03-14T03:51:45.3266272Z "INFO": "\033[0m", # Reset 2025-03-14T03:51:45.3266717Z "DEBUG": "\033[0m", # Reset 2025-03-14T03:51:45.3267157Z } 2025-03-14T03:51:45.3267350Z 2025-03-14T03:51:45.3267555Z def format(self, record: LogRecord) -> str: 2025-03-14T03:51:45.3268267Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-03-14T03:51:45.3268988Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-03-14T03:51:45.3269537Z return super().format(record) 2025-03-14T03:51:45.3269852Z 2025-03-14T03:51:45.3269858Z 2025-03-14T03:51:45.3270046Z handler = logging.StreamHandler() 2025-03-14T03:51:45.3270706Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-03-14T03:51:45.3271225Z 2025-03-14T03:51:45.3271454Z log = logging.getLogger(os.path.basename(__file__)) 2025-03-14T03:51:45.3271999Z log.addHandler(handler) 2025-03-14T03:51:45.3272416Z log.setLevel(logging.INFO) 2025-03-14T03:51:45.3272681Z 2025-03-14T03:51:45.3272686Z 2025-03-14T03:51:45.3272916Z def set_github_output(key: str, value: str) -> None: 2025-03-14T03:51:45.3273444Z """ 2025-03-14T03:51:45.3273921Z Defines outputs of the github action that invokes this script 2025-03-14T03:51:45.3274511Z """ 2025-03-14T03:51:45.3274993Z if not GITHUB_OUTPUT: 2025-03-14T03:51:45.3276181Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-03-14T03:51:45.3277225Z log.warning( 2025-03-14T03:51:45.3278026Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-03-14T03:51:45.3278898Z ) 2025-03-14T03:51:45.3288913Z print(f"::set-output name={key}::{value}") 2025-03-14T03:51:45.3289480Z return 2025-03-14T03:51:45.3289702Z 2025-03-14T03:51:45.3289894Z with open(GITHUB_OUTPUT, "a") as f: 2025-03-14T03:51:45.3290446Z log.info(f"Setting output: {key}='{value}'") 2025-03-14T03:51:45.3290988Z f.write(f"{key}={value}\n") 2025-03-14T03:51:45.3291299Z 2025-03-14T03:51:45.3291304Z 2025-03-14T03:51:45.3291595Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-03-14T03:51:45.3292212Z return frozenset( 2025-03-14T03:51:45.3292789Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-03-14T03:51:45.3293428Z ) 2025-03-14T03:51:45.3293618Z 2025-03-14T03:51:45.3293625Z 2025-03-14T03:51:45.3293802Z def parse_args() -> Any: 2025-03-14T03:51:45.3294335Z parser = ArgumentParser("Get dynamic rollout settings") 2025-03-14T03:51:45.3295322Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-03-14T03:51:45.3296132Z parser.add_argument( 2025-03-14T03:51:45.3296570Z "--github-issue-repo", 2025-03-14T03:51:45.3297001Z type=str, 2025-03-14T03:51:45.3297378Z required=False, 2025-03-14T03:51:45.3297807Z default="pytorch/test-infra", 2025-03-14T03:51:45.3298312Z help="GitHub repo to get the issue", 2025-03-14T03:51:45.3298793Z ) 2025-03-14T03:51:45.3299133Z parser.add_argument( 2025-03-14T03:51:45.3299546Z "--github-repo", 2025-03-14T03:51:45.3299945Z type=str, 2025-03-14T03:51:45.3300316Z required=True, 2025-03-14T03:51:45.3300749Z help="GitHub repo where CI is running", 2025-03-14T03:51:45.3301235Z ) 2025-03-14T03:51:45.3301584Z parser.add_argument( 2025-03-14T03:51:45.3302146Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-03-14T03:51:45.3302929Z ) 2025-03-14T03:51:45.3303279Z parser.add_argument( 2025-03-14T03:51:45.3303858Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-03-14T03:51:45.3304492Z ) 2025-03-14T03:51:45.3304831Z parser.add_argument( 2025-03-14T03:51:45.3305655Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-03-14T03:51:45.3306315Z ) 2025-03-14T03:51:45.3306660Z parser.add_argument( 2025-03-14T03:51:45.3307265Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-03-14T03:51:45.3307927Z ) 2025-03-14T03:51:45.3308271Z parser.add_argument( 2025-03-14T03:51:45.3308694Z "--github-ref-type", 2025-03-14T03:51:45.3309114Z type=str, 2025-03-14T03:51:45.3309481Z required=True, 2025-03-14T03:51:45.3309935Z help="Current GitHub ref type, branch or tag", 2025-03-14T03:51:45.3310450Z ) 2025-03-14T03:51:45.3310792Z parser.add_argument( 2025-03-14T03:51:45.3311328Z "--eligible-experiments", 2025-03-14T03:51:45.3312070Z type=_str_comma_separated_to_set, 2025-03-14T03:51:45.3312578Z required=False, 2025-03-14T03:51:45.3312978Z default="", 2025-03-14T03:51:45.3313767Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-03-14T03:51:45.3314671Z ) 2025-03-14T03:51:45.3315022Z parser.add_argument( 2025-03-14T03:51:45.3315769Z "--pr-number", 2025-03-14T03:51:45.3316174Z type=str, 2025-03-14T03:51:45.3316549Z required=False, 2025-03-14T03:51:45.3316948Z default="", 2025-03-14T03:51:45.3317578Z help="the optional PR number where this is run", 2025-03-14T03:51:45.3318100Z ) 2025-03-14T03:51:45.3318285Z 2025-03-14T03:51:45.3318466Z return parser.parse_args() 2025-03-14T03:51:45.3318751Z 2025-03-14T03:51:45.3318758Z 2025-03-14T03:51:45.3319150Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-03-14T03:51:45.3319876Z auth = Auth.Token(github_token) 2025-03-14T03:51:45.3320360Z return Github(auth=auth) 2025-03-14T03:51:45.3320644Z 2025-03-14T03:51:45.3320650Z 2025-03-14T03:51:45.3321072Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-03-14T03:51:45.3321833Z repo = gh.get_repo(repo) 2025-03-14T03:51:45.3322310Z return repo.get_issue(number=issue_num) 2025-03-14T03:51:45.3322649Z 2025-03-14T03:51:45.3322655Z 2025-03-14T03:51:45.3322837Z def get_potential_pr_author( 2025-03-14T03:51:45.3323430Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-03-14T03:51:45.3324078Z ) -> str: 2025-03-14T03:51:45.3324568Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-03-14T03:51:45.3325545Z # Fetch the actual username from the original PR. The PR number is 2025-03-14T03:51:45.3326250Z # embedded in the tag name: ciflow// 2025-03-14T03:51:45.3326636Z 2025-03-14T03:51:45.3326817Z gh = get_gh_client(github_token) 2025-03-14T03:51:45.3327130Z 2025-03-14T03:51:45.3327378Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-03-14T03:51:45.3327954Z split_tag = ref_name.split("/") 2025-03-14T03:51:45.3328428Z if ( 2025-03-14T03:51:45.3328790Z len(split_tag) == 3 2025-03-14T03:51:45.3329239Z and split_tag[0] == "ciflow" 2025-03-14T03:51:45.3329729Z and split_tag[2].isnumeric() 2025-03-14T03:51:45.3330192Z ): 2025-03-14T03:51:45.3330559Z pr_number = split_tag[2] 2025-03-14T03:51:45.3331015Z try: 2025-03-14T03:51:45.3331415Z repository = gh.get_repo(repo) 2025-03-14T03:51:45.3331994Z pull = repository.get_pull(number=int(pr_number)) 2025-03-14T03:51:45.3332557Z except Exception as e: 2025-03-14T03:51:45.3333047Z raise Exception( # noqa: TRY002 2025-03-14T03:51:45.3333797Z f"issue with pull request {pr_number} from repo {repository}" 2025-03-14T03:51:45.3334402Z ) from e 2025-03-14T03:51:45.3334907Z return pull.user.login # type: ignore[no-any-return] 2025-03-14T03:51:45.3335722Z # In all other cases, return the original input username 2025-03-14T03:51:45.3336275Z return username 2025-03-14T03:51:45.3336512Z 2025-03-14T03:51:45.3336518Z 2025-03-14T03:51:45.3336734Z def is_exception_branch(branch: str) -> bool: 2025-03-14T03:51:45.3337235Z """ 2025-03-14T03:51:45.3337830Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-03-14T03:51:45.3338558Z """ 2025-03-14T03:51:45.3339072Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-03-14T03:51:45.3339566Z 2025-03-14T03:51:45.3339572Z 2025-03-14T03:51:45.3339763Z def load_yaml(yaml_text: str) -> Any: 2025-03-14T03:51:45.3340232Z try: 2025-03-14T03:51:45.3340597Z data = yaml.safe_load(yaml_text) 2025-03-14T03:51:45.3341075Z return data 2025-03-14T03:51:45.3341462Z except yaml.YAMLError: 2025-03-14T03:51:45.3341914Z log.exception("Error loading YAML") 2025-03-14T03:51:45.3342392Z raise 2025-03-14T03:51:45.3342592Z 2025-03-14T03:51:45.3342598Z 2025-03-14T03:51:45.3342991Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-03-14T03:51:45.3343666Z """ 2025-03-14T03:51:45.3344249Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-03-14T03:51:45.3344813Z 2025-03-14T03:51:45.3345137Z If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:45.3346106Z and the text below is the list of opted in users. 2025-03-14T03:51:45.3346491Z 2025-03-14T03:51:45.3346845Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-03-14T03:51:45.3347503Z """ 2025-03-14T03:51:45.3347919Z rollout_state_parts = rollout_state.split("---") 2025-03-14T03:51:45.3348480Z if len(rollout_state_parts) >= 2: 2025-03-14T03:51:45.3349046Z return rollout_state_parts[0], rollout_state_parts[1] 2025-03-14T03:51:45.3349594Z else: 2025-03-14T03:51:45.3349949Z return "", rollout_state 2025-03-14T03:51:45.3350236Z 2025-03-14T03:51:45.3350242Z 2025-03-14T03:51:45.3350431Z class UserOptins(dict[str, list[str]]): 2025-03-14T03:51:45.3350905Z """ 2025-03-14T03:51:45.3351396Z Dictionary of users with a list of features they have opted into 2025-03-14T03:51:45.3351992Z """ 2025-03-14T03:51:45.3352174Z 2025-03-14T03:51:45.3352180Z 2025-03-14T03:51:45.3352507Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-03-14T03:51:45.3353114Z """ 2025-03-14T03:51:45.3353775Z Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-03-14T03:51:45.3354410Z 2025-03-14T03:51:45.3354990Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-03-14T03:51:45.3356197Z - Example line: "@User1,lf,split_build" 2025-03-14T03:51:45.3356840Z - A "#" prefix indicates the user is opted out of all experiments 2025-03-14T03:51:45.3357286Z 2025-03-14T03:51:45.3357293Z 2025-03-14T03:51:45.3357445Z """ 2025-03-14T03:51:45.3357796Z optins = UserOptins() 2025-03-14T03:51:45.3358246Z for user in user_optin_text.split("\n"): 2025-03-14T03:51:45.3358767Z user = user.strip("\r\n\t -") 2025-03-14T03:51:45.3359276Z if not user or not user.startswith("@"): 2025-03-14T03:51:45.3359800Z # Not a valid user. Skip 2025-03-14T03:51:45.3360250Z continue 2025-03-14T03:51:45.3360476Z 2025-03-14T03:51:45.3360627Z if user: 2025-03-14T03:51:45.3361029Z usr_name = user.split(",")[0].strip("@") 2025-03-14T03:51:45.3361674Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-03-14T03:51:45.3362266Z 2025-03-14T03:51:45.3362436Z return optins 2025-03-14T03:51:45.3362659Z 2025-03-14T03:51:45.3362665Z 2025-03-14T03:51:45.3362935Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-03-14T03:51:45.3363494Z """ 2025-03-14T03:51:45.3363867Z Check if the experiment name is valid. 2025-03-14T03:51:45.3364356Z A valid name: 2025-03-14T03:51:45.3364948Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-03-14T03:51:45.3365963Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-03-14T03:51:45.3366634Z - Cannot contain spaces 2025-03-14T03:51:45.3367059Z """ 2025-03-14T03:51:45.3367247Z 2025-03-14T03:51:45.3367490Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-03-14T03:51:45.3368144Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-03-14T03:51:45.3368555Z 2025-03-14T03:51:45.3368711Z if valid: 2025-03-14T03:51:45.3369072Z return True 2025-03-14T03:51:45.3369290Z 2025-03-14T03:51:45.3369443Z log.error( 2025-03-14T03:51:45.3370794Z f"Invalid experiment name: {experiment_name}. Experiment names should only contain alphanumeric characters, '_', and '-'. They cannot contain spaces, and the special characters '_' and '-' cannot be the first or last characters." 2025-03-14T03:51:45.3372225Z ) 2025-03-14T03:51:45.3372559Z return False 2025-03-14T03:51:45.3372775Z 2025-03-14T03:51:45.3372781Z 2025-03-14T03:51:45.3373069Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-03-14T03:51:45.3373644Z """ 2025-03-14T03:51:45.3374187Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-03-14T03:51:45.3374979Z """ 2025-03-14T03:51:45.3375419Z try: 2025-03-14T03:51:45.3375774Z if settings_text: 2025-03-14T03:51:45.3376455Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-03-14T03:51:45.3377198Z # for easy reading 2025-03-14T03:51:45.3377930Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-03-14T03:51:45.3378752Z # the backtick character in shell commands. 2025-03-14T03:51:45.3379311Z backtick = chr(96) # backtick character 2025-03-14T03:51:45.3379925Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-03-14T03:51:45.3380536Z settings = load_yaml(settings_text) 2025-03-14T03:51:45.3380879Z 2025-03-14T03:51:45.3381255Z # For now we just load experiments. We can expand this if/when we add more settings 2025-03-14T03:51:45.3381959Z experiments = {} 2025-03-14T03:51:45.3382236Z 2025-03-14T03:51:45.3382567Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-03-14T03:51:45.3383270Z if not is_valid_experiment_name(exp_name): 2025-03-14T03:51:45.3384301Z # Exclude invalid experiments from the list. We log an error, but don't raise an exception so that other experiments can still be processed. 2025-03-14T03:51:45.3385372Z continue 2025-03-14T03:51:45.3385642Z 2025-03-14T03:51:45.3385813Z valid_settings = {} 2025-03-14T03:51:45.3386297Z for setting in exp_settings: 2025-03-14T03:51:45.3386834Z if setting not in Experiment._fields: 2025-03-14T03:51:45.3387346Z log.warning( 2025-03-14T03:51:45.3387996Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-03-14T03:51:45.3388658Z ) 2025-03-14T03:51:45.3389060Z else: 2025-03-14T03:51:45.3389539Z valid_settings[setting] = exp_settings[setting] 2025-03-14T03:51:45.3389935Z 2025-03-14T03:51:45.3390186Z experiments[exp_name] = Experiment(**valid_settings) 2025-03-14T03:51:45.3390918Z return Settings(experiments) 2025-03-14T03:51:45.3391251Z 2025-03-14T03:51:45.3391422Z except Exception: 2025-03-14T03:51:45.3391856Z log.exception("Failed to parse settings") 2025-03-14T03:51:45.3392224Z 2025-03-14T03:51:45.3392384Z return Settings() 2025-03-14T03:51:45.3392622Z 2025-03-14T03:51:45.3392628Z 2025-03-14T03:51:45.3392859Z def parse_settings(rollout_state: str) -> Settings: 2025-03-14T03:51:45.3393379Z """ 2025-03-14T03:51:45.3393773Z Parse settings, if any, from the rollout state. 2025-03-14T03:51:45.3394142Z 2025-03-14T03:51:45.3394468Z If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:45.3395273Z and the text below is the list of opted in users. 2025-03-14T03:51:45.3395648Z 2025-03-14T03:51:45.3396046Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-03-14T03:51:45.3396732Z """ 2025-03-14T03:51:45.3397252Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:45.3397953Z return parse_settings_from_text(settings_text) 2025-03-14T03:51:45.3398322Z 2025-03-14T03:51:45.3398328Z 2025-03-14T03:51:45.3398563Z def parse_users(rollout_state: str) -> UserOptins: 2025-03-14T03:51:45.3399078Z """ 2025-03-14T03:51:45.3399439Z Parse users from the rollout state. 2025-03-14T03:51:45.3399764Z 2025-03-14T03:51:45.3399908Z """ 2025-03-14T03:51:45.3400400Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:45.3401072Z return parse_user_opt_in_from_text(users_text) 2025-03-14T03:51:45.3401439Z 2025-03-14T03:51:45.3401445Z 2025-03-14T03:51:45.3401961Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:45.3402642Z """ 2025-03-14T03:51:45.3403031Z Check if a user is opted into an experiment 2025-03-14T03:51:45.3403527Z """ 2025-03-14T03:51:45.3403943Z return experiment_name in user_optins.get(user, []) 2025-03-14T03:51:45.3404332Z 2025-03-14T03:51:45.3404338Z 2025-03-14T03:51:45.3404726Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:45.3405521Z """ 2025-03-14T03:51:45.3405949Z Check if a user explicitly opted out of an experiment 2025-03-14T03:51:45.3406484Z """ 2025-03-14T03:51:45.3406943Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-03-14T03:51:45.3407578Z experiment_optout = "-" + experiment_name 2025-03-14T03:51:45.3408160Z if experiment_optout not in user_optins.get(user, []): 2025-03-14T03:51:45.3408713Z return False 2025-03-14T03:51:45.3408958Z 2025-03-14T03:51:45.3409203Z if is_user_opted_in(user, user_optins, experiment_name): 2025-03-14T03:51:45.3409749Z log.warning( 2025-03-14T03:51:45.3410488Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-03-14T03:51:45.3411301Z ) 2025-03-14T03:51:45.3411492Z 2025-03-14T03:51:45.3411645Z return True 2025-03-14T03:51:45.3411863Z 2025-03-14T03:51:45.3411868Z 2025-03-14T03:51:45.3412033Z def get_runner_prefix( 2025-03-14T03:51:45.3412437Z rollout_state: str, 2025-03-14T03:51:45.3412863Z workflow_requestors: Iterable[str], 2025-03-14T03:51:45.3413336Z branch: str, 2025-03-14T03:51:45.3413777Z eligible_experiments: frozenset[str] = frozenset(), 2025-03-14T03:51:45.3414329Z is_canary: bool = False, 2025-03-14T03:51:45.3414777Z ) -> str: 2025-03-14T03:51:45.3415277Z settings = parse_settings(rollout_state) 2025-03-14T03:51:45.3415810Z user_optins = parse_users(rollout_state) 2025-03-14T03:51:45.3416154Z 2025-03-14T03:51:45.3416320Z fleet_prefix = "" 2025-03-14T03:51:45.3416711Z prefixes = [] 2025-03-14T03:51:45.3417283Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-03-14T03:51:45.3418145Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-03-14T03:51:45.3418957Z log.info( 2025-03-14T03:51:45.3419583Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-03-14T03:51:45.3420287Z ) 2025-03-14T03:51:45.3420638Z continue 2025-03-14T03:51:45.3420872Z 2025-03-14T03:51:45.3421049Z if eligible_experiments: 2025-03-14T03:51:45.3421554Z if experiment_name not in eligible_experiments: 2025-03-14T03:51:45.3422143Z exp_list = ", ".join(eligible_experiments) 2025-03-14T03:51:45.3422653Z log.info( 2025-03-14T03:51:45.3423369Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-03-14T03:51:45.3424147Z ) 2025-03-14T03:51:45.3424511Z continue 2025-03-14T03:51:45.3424949Z elif not experiment_settings.default: 2025-03-14T03:51:45.3425558Z log.info( 2025-03-14T03:51:45.3426180Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-03-14T03:51:45.3426865Z ) 2025-03-14T03:51:45.3427218Z continue 2025-03-14T03:51:45.3427446Z 2025-03-14T03:51:45.3427708Z # Is any workflow_requestor opted out to this experiment? 2025-03-14T03:51:45.3428280Z opted_out_users = [ 2025-03-14T03:51:45.3428694Z requestor 2025-03-14T03:51:45.3429107Z for requestor in workflow_requestors 2025-03-14T03:51:45.3429722Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-03-14T03:51:45.3430308Z ] 2025-03-14T03:51:45.3430500Z 2025-03-14T03:51:45.3430664Z if opted_out_users: 2025-03-14T03:51:45.3431202Z log.info( 2025-03-14T03:51:45.3431764Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-03-14T03:51:45.3432410Z ) 2025-03-14T03:51:45.3432757Z continue 2025-03-14T03:51:45.3432989Z 2025-03-14T03:51:45.3433242Z # Is any workflow_requestor opted in to this experiment? 2025-03-14T03:51:45.3433804Z opted_in_users = [ 2025-03-14T03:51:45.3434224Z requestor 2025-03-14T03:51:45.3434641Z for requestor in workflow_requestors 2025-03-14T03:51:45.3435367Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-03-14T03:51:45.3435940Z ] 2025-03-14T03:51:45.3436129Z 2025-03-14T03:51:45.3436289Z enabled = False 2025-03-14T03:51:45.3436693Z if opted_in_users: 2025-03-14T03:51:45.3437100Z log.info( 2025-03-14T03:51:45.3437645Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-03-14T03:51:45.3438282Z ) 2025-03-14T03:51:45.3438635Z enabled = True 2025-03-14T03:51:45.3438896Z 2025-03-14T03:51:45.3439100Z elif experiment_settings.rollout_perc: 2025-03-14T03:51:45.3439863Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-03-14T03:51:45.3440720Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-03-14T03:51:45.3441323Z log.info( 2025-03-14T03:51:45.3442119Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-03-14T03:51:45.3442962Z ) 2025-03-14T03:51:45.3443337Z enabled = True 2025-03-14T03:51:45.3443615Z 2025-03-14T03:51:45.3443766Z if enabled: 2025-03-14T03:51:45.3444152Z label = experiment_name 2025-03-14T03:51:45.3444655Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-03-14T03:51:45.3445538Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-03-14T03:51:45.3446352Z # - If it's enabled, then we always list it's prefix first 2025-03-14T03:51:45.3447174Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-03-14T03:51:45.3447791Z if is_canary: 2025-03-14T03:51:45.3448242Z label += CANARY_FLEET_SUFFIX 2025-03-14T03:51:45.3448748Z fleet_prefix = label 2025-03-14T03:51:45.3449200Z else: 2025-03-14T03:51:45.3449598Z prefixes.append(label) 2025-03-14T03:51:45.3449920Z 2025-03-14T03:51:45.3450100Z if len(prefixes) > 1: 2025-03-14T03:51:45.3450505Z log.error( 2025-03-14T03:51:45.3451452Z f"Only a fleet and one other experiment can be enabled for a job at any time. Enabling {prefixes[0]} and ignoring the rest, which are {', '.join(prefixes[1:])}" 2025-03-14T03:51:45.3452485Z ) 2025-03-14T03:51:45.3452845Z prefixes = prefixes[:1] 2025-03-14T03:51:45.3453139Z 2025-03-14T03:51:45.3453308Z # Fleet always comes first 2025-03-14T03:51:45.3453736Z if fleet_prefix: 2025-03-14T03:51:45.3454149Z prefixes.insert(0, fleet_prefix) 2025-03-14T03:51:45.3454480Z 2025-03-14T03:51:45.3454712Z return ".".join(prefixes) + "." if prefixes else "" 2025-03-14T03:51:45.3455097Z 2025-03-14T03:51:45.3455103Z 2025-03-14T03:51:45.3455609Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-03-14T03:51:45.3456328Z """ 2025-03-14T03:51:45.3456873Z Gets the first comment of the issue, which contains the desired rollout state. 2025-03-14T03:51:45.3457392Z 2025-03-14T03:51:45.3457751Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-03-14T03:51:45.3458396Z """ 2025-03-14T03:51:45.3458743Z gh = get_gh_client(github_token) 2025-03-14T03:51:45.3459235Z issue = get_issue(gh, repo, issue_num) 2025-03-14T03:51:45.3459940Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-03-14T03:51:45.3460342Z 2025-03-14T03:51:45.3460348Z 2025-03-14T03:51:45.3460716Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-03-14T03:51:45.3461411Z for _ in range(num_retries): 2025-03-14T03:51:45.3461851Z try: 2025-03-14T03:51:45.3462231Z req = Request(url=url, headers=headers) 2025-03-14T03:51:45.3462843Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-03-14T03:51:45.3463429Z return json.loads(content) 2025-03-14T03:51:45.3463921Z except Exception as e: 2025-03-14T03:51:45.3464415Z log.warning(f"Could not download {url}: {e}") 2025-03-14T03:51:45.3464783Z 2025-03-14T03:51:45.3465129Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-03-14T03:51:45.3465888Z return {} 2025-03-14T03:51:45.3466095Z 2025-03-14T03:51:45.3466108Z 2025-03-14T03:51:45.3466252Z @cache 2025-03-14T03:51:45.3466827Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-03-14T03:51:45.3467518Z """ 2025-03-14T03:51:45.3467872Z Dynamically get PR information 2025-03-14T03:51:45.3468321Z """ 2025-03-14T03:51:45.3468773Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-03-14T03:51:45.3469347Z headers = { 2025-03-14T03:51:45.3469764Z "Accept": "application/vnd.github.v3+json", 2025-03-14T03:51:45.3470321Z "Authorization": f"token {github_token}", 2025-03-14T03:51:45.3470820Z } 2025-03-14T03:51:45.3471214Z json_response: dict[str, Any] = download_json( 2025-03-14T03:51:45.3471777Z url=f"{github_api}/issues/{pr_number}", 2025-03-14T03:51:45.3472278Z headers=headers, 2025-03-14T03:51:45.3472665Z ) 2025-03-14T03:51:45.3472859Z 2025-03-14T03:51:45.3473029Z if not json_response: 2025-03-14T03:51:45.3473545Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-03-14T03:51:45.3474119Z return {} 2025-03-14T03:51:45.3474338Z 2025-03-14T03:51:45.3474508Z return json_response 2025-03-14T03:51:45.3474762Z 2025-03-14T03:51:45.3474769Z 2025-03-14T03:51:45.3475238Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-03-14T03:51:45.3476033Z """ 2025-03-14T03:51:45.3476522Z Dynamically get the latest list of labels from the pull request 2025-03-14T03:51:45.3477125Z """ 2025-03-14T03:51:45.3477567Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-03-14T03:51:45.3478132Z return { 2025-03-14T03:51:45.3478663Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-03-14T03:51:45.3479312Z } 2025-03-14T03:51:45.3479498Z 2025-03-14T03:51:45.3479504Z 2025-03-14T03:51:45.3479663Z def main() -> None: 2025-03-14T03:51:45.3480052Z args = parse_args() 2025-03-14T03:51:45.3480294Z 2025-03-14T03:51:45.3480503Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-03-14T03:51:45.3480855Z 2025-03-14T03:51:45.3481036Z # Check if the PR is opt-out 2025-03-14T03:51:45.3481478Z if args.pr_number: 2025-03-14T03:51:45.3482176Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-03-14T03:51:45.3482882Z if OPT_OUT_LABEL in labels: 2025-03-14T03:51:45.3483332Z log.info( 2025-03-14T03:51:45.3483964Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-03-14T03:51:45.3484659Z ) 2025-03-14T03:51:45.3485259Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:45.3485875Z sys.exit() 2025-03-14T03:51:45.3486110Z 2025-03-14T03:51:45.3486261Z try: 2025-03-14T03:51:45.3486664Z rollout_state = get_rollout_state_from_issue( 2025-03-14T03:51:45.3487303Z args.github_token, args.github_issue_repo, args.github_issue 2025-03-14T03:51:45.3487883Z ) 2025-03-14T03:51:45.3488209Z 2025-03-14T03:51:45.3488396Z username = get_potential_pr_author( 2025-03-14T03:51:45.3488896Z args.github_token, 2025-03-14T03:51:45.3489336Z args.github_repo, 2025-03-14T03:51:45.3489763Z args.github_actor, 2025-03-14T03:51:45.3490389Z args.github_ref_type, 2025-03-14T03:51:45.3490879Z args.github_branch, 2025-03-14T03:51:45.3491302Z ) 2025-03-14T03:51:45.3491492Z 2025-03-14T03:51:45.3491753Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-03-14T03:51:45.3492170Z 2025-03-14T03:51:45.3492371Z runner_label_prefix = get_runner_prefix( 2025-03-14T03:51:45.3492873Z rollout_state, 2025-03-14T03:51:45.3493464Z (args.github_issue_owner, username), 2025-03-14T03:51:45.3493976Z args.github_branch, 2025-03-14T03:51:45.3494434Z args.eligible_experiments, 2025-03-14T03:51:45.3494903Z is_canary, 2025-03-14T03:51:45.3495468Z ) 2025-03-14T03:51:45.3495669Z 2025-03-14T03:51:45.3495838Z except Exception as e: 2025-03-14T03:51:45.3496259Z log.error( 2025-03-14T03:51:45.3496872Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-03-14T03:51:45.3497569Z ) 2025-03-14T03:51:45.3497754Z 2025-03-14T03:51:45.3498054Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:45.3498513Z 2025-03-14T03:51:45.3498519Z 2025-03-14T03:51:45.3498682Z if __name__ == "__main__": 2025-03-14T03:51:45.3499093Z main() 2025-03-14T03:51:45.3499287Z 2025-03-14T03:51:45.3587201Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-03-14T03:51:45.3588033Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-03-14T03:51:45.3634779Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:45.3635502Z env: 2025-03-14T03:51:45.3636148Z GITHUB_TOKEN: *** 2025-03-14T03:51:45.3636543Z ISSUE_NUMBER: 5132 2025-03-14T03:51:45.3636971Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:45.3637443Z ISSUE_OWNER: 2025-03-14T03:51:45.3637814Z CHECK_EXPERIMENTS: 2025-03-14T03:51:45.3638213Z PR_NUMBER: 2025-03-14T03:51:45.3638574Z ##[endgroup] 2025-03-14T03:51:45.7250544Z Defaulting to user installation because normal site-packages is not writeable 2025-03-14T03:51:46.0648614Z Collecting urllib3==1.26.18 2025-03-14T03:51:46.1010332Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-03-14T03:51:46.1224140Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 4.9 MB/s eta 0:00:00 2025-03-14T03:51:46.1566126Z Collecting PyGithub==2.3.0 2025-03-14T03:51:46.1613637Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-03-14T03:51:46.2031428Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-03-14T03:51:46.2065121Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl.metadata (8.6 kB) 2025-03-14T03:51:46.2113781Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-03-14T03:51:46.2131973Z Requirement already satisfied: pyjwt>=2.4.0 in /usr/lib/python3/dist-packages (from pyjwt[crypto]>=2.4.0->PyGithub==2.3.0) (2.7.0) 2025-03-14T03:51:46.2180536Z Requirement already satisfied: typing-extensions>=4.0.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (4.10.0) 2025-03-14T03:51:46.2421181Z Collecting Deprecated (from PyGithub==2.3.0) 2025-03-14T03:51:46.2458481Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl.metadata (5.7 kB) 2025-03-14T03:51:46.2701334Z Requirement already satisfied: cryptography>=3.4.0 in /usr/lib/python3/dist-packages (from pyjwt[crypto]>=2.4.0->PyGithub==2.3.0) (41.0.7) 2025-03-14T03:51:46.3789159Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-03-14T03:51:46.3828838Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-03-14T03:51:46.4924922Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-03-14T03:51:46.4960225Z Downloading wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.4 kB) 2025-03-14T03:51:46.5138233Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-03-14T03:51:46.5170831Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-03-14T03:51:46.5398657Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-03-14T03:51:46.5473216Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 23.6 MB/s eta 0:00:00 2025-03-14T03:51:46.5534433Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-03-14T03:51:46.5727015Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 19.6 MB/s eta 0:00:00 2025-03-14T03:51:46.5762756Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (856 kB) 2025-03-14T03:51:46.5848126Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 122.2 MB/s eta 0:00:00 2025-03-14T03:51:46.5891688Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl (10.0 kB) 2025-03-14T03:51:46.5944792Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-03-14T03:51:46.6008906Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 99.0 MB/s eta 0:00:00 2025-03-14T03:51:46.6041378Z Downloading wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (89 kB) 2025-03-14T03:51:46.6082463Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 31.4 MB/s eta 0:00:00 2025-03-14T03:51:46.6115377Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-03-14T03:51:46.6159067Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 39.7 MB/s eta 0:00:00 2025-03-14T03:51:46.9043312Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-03-14T03:51:47.4490357Z Successfully installed Deprecated-1.2.18 PyGithub-2.3.0 cffi-1.17.1 pycparser-2.22 pynacl-1.5.0 urllib3-1.26.18 wrapt-1.17.2 2025-03-14T03:51:47.5259807Z ##[group]Run curr_branch="main" 2025-03-14T03:51:47.5261174Z curr_branch="main" 2025-03-14T03:51:47.5262414Z curr_ref_type="branch" 2025-03-14T03:51:47.5263799Z echo "Current branch is '$curr_branch'" 2025-03-14T03:51:47.5265375Z  2025-03-14T03:51:47.5266459Z python3 runner_determinator.py \ 2025-03-14T03:51:47.5267955Z  --github-token "$GITHUB_TOKEN" \ 2025-03-14T03:51:47.5269443Z  --github-issue "$ISSUE_NUMBER" \ 2025-03-14T03:51:47.5270920Z  --github-branch "$curr_branch" \ 2025-03-14T03:51:47.5272428Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-03-14T03:51:47.5274013Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-03-14T03:51:47.5275664Z  --github-ref-type "$curr_ref_type" \ 2025-03-14T03:51:47.5277241Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-03-14T03:51:47.5278948Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-03-14T03:51:47.5280640Z  --pr-number "${PR_NUMBER}" 2025-03-14T03:51:47.5330069Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:47.5331243Z env: 2025-03-14T03:51:47.5332669Z GITHUB_TOKEN: *** 2025-03-14T03:51:47.5333653Z ISSUE_NUMBER: 5132 2025-03-14T03:51:47.5334696Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:47.5336038Z ISSUE_OWNER: 2025-03-14T03:51:47.5336978Z CHECK_EXPERIMENTS: 2025-03-14T03:51:47.5337967Z PR_NUMBER: 2025-03-14T03:51:47.5338844Z ##[endgroup] 2025-03-14T03:51:47.5414717Z Current branch is 'main' 2025-03-14T03:51:48.9827412Z INFO : Branch main is an exception branch. Not enabling experiment ephemeral. 2025-03-14T03:51:48.9828727Z INFO : Setting output: label-type='' 2025-03-14T03:51:49.0139093Z Evaluate and set job outputs 2025-03-14T03:51:49.0146403Z Cleaning up orphan processes