2025-04-21T20:23:39.5983637Z Current runner version: '2.323.0' 2025-04-21T20:23:39.6008303Z ##[group]Operating System 2025-04-21T20:23:39.6009016Z Ubuntu 2025-04-21T20:23:39.6009600Z 24.04.2 2025-04-21T20:23:39.6010053Z LTS 2025-04-21T20:23:39.6010513Z ##[endgroup] 2025-04-21T20:23:39.6011101Z ##[group]Runner Image 2025-04-21T20:23:39.6011649Z Image: ubuntu-24.04 2025-04-21T20:23:39.6012154Z Version: 20250413.1.0 2025-04-21T20:23:39.6013194Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250413.1/images/ubuntu/Ubuntu2404-Readme.md 2025-04-21T20:23:39.6014743Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250413.1 2025-04-21T20:23:39.6015612Z ##[endgroup] 2025-04-21T20:23:39.6016177Z ##[group]Runner Image Provisioner 2025-04-21T20:23:39.6016770Z 2.0.422.1 2025-04-21T20:23:39.6017193Z ##[endgroup] 2025-04-21T20:23:39.6018205Z ##[group]GITHUB_TOKEN Permissions 2025-04-21T20:23:39.6020211Z Contents: read 2025-04-21T20:23:39.6020708Z Metadata: read 2025-04-21T20:23:39.6021488Z ##[endgroup] 2025-04-21T20:23:39.6023793Z Secret source: Actions 2025-04-21T20:23:39.6025072Z Prepare workflow directory 2025-04-21T20:23:39.6533951Z Prepare all required actions 2025-04-21T20:23:39.6588279Z Complete job name: get-label-type / runner-determinator 2025-04-21T20:23:40.1900169Z ##[group]Run cat < runner_determinator.py 2025-04-21T20:23:40.1902685Z cat < runner_determinator.py 2025-04-21T20:23:40.1903455Z # flake8: noqa: G004 2025-04-21T20:23:40.1904016Z  2025-04-21T20:23:40.1905058Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-04-21T20:23:40.1906264Z # must be kept in sync. You can do it easily by running the following command: 2025-04-21T20:23:40.1907211Z # python .github/scripts/update_runner_determinator.py 2025-04-21T20:23:40.1907951Z  2025-04-21T20:23:40.1908419Z """ 2025-04-21T20:23:40.1909200Z This runner determinator is used to determine which set of runners to run a 2025-04-21T20:23:40.1910243Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-04-21T20:23:40.1911494Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-04-21T20:23:40.1912475Z of which runners should be used to run which job. 2025-04-21T20:23:40.1913155Z  2025-04-21T20:23:40.1913993Z The configuration has two parts, the settings and a list of opted-in users, 2025-04-21T20:23:40.1915218Z separated by a line containing "---". If the line is not present, the 2025-04-21T20:23:40.1916260Z settings are considered to be empty with only the second part, the user 2025-04-21T20:23:40.1917161Z list, defined. 2025-04-21T20:23:40.1917699Z  2025-04-21T20:23:40.1918405Z The first part is a YAML block that defines the rollout settings. This can be 2025-04-21T20:23:40.1919526Z used to define any settings that are needed to determine which runners to use. 2025-04-21T20:23:40.1920547Z It's fields are defined by the RolloutSettings class below. 2025-04-21T20:23:40.1921281Z  2025-04-21T20:23:40.1922052Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-04-21T20:23:40.1923099Z The user list is also a comma separated list of additional features or 2025-04-21T20:23:40.1923995Z experiments which the user could be opted in to. 2025-04-21T20:23:40.1924850Z  2025-04-21T20:23:40.1925350Z The user list has the following rules: 2025-04-21T20:23:40.1926013Z  2025-04-21T20:23:40.1926699Z - Users are GitHub usernames, which must start with the @ prefix 2025-04-21T20:23:40.1927751Z - Each user is also a comma-separated list of features/experiments to enable 2025-04-21T20:23:40.1928707Z - A "#" prefix opts the user out of all experiments 2025-04-21T20:23:40.1929390Z  2025-04-21T20:23:40.1930287Z Example config: 2025-04-21T20:23:40.1930932Z  # A list of experiments that can be opted into. 2025-04-21T20:23:40.1931788Z  # This defines the behavior they'll induce when opted into. 2025-04-21T20:23:40.1932604Z  # Expected syntax is: 2025-04-21T20:23:40.1933395Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-04-21T20:23:40.1934729Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-04-21T20:23:40.1935849Z  2025-04-21T20:23:40.1936339Z  experiments: 2025-04-21T20:23:40.1936934Z  lf: 2025-04-21T20:23:40.1937465Z  rollout_percent: 25 2025-04-21T20:23:40.1938086Z  all_branches: false 2025-04-21T20:23:40.1938645Z  default: true 2025-04-21T20:23:40.1939293Z  --- 2025-04-21T20:23:40.1939930Z  2025-04-21T20:23:40.1940411Z  # Opt-ins: 2025-04-21T20:23:40.1941256Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-04-21T20:23:40.1942429Z  # and specifying experiments to enable in a comma-separated list. 2025-04-21T20:23:40.1943383Z  # To always opt out of an experiment, prefix it with a "-". 2025-04-21T20:23:40.1944254Z  # Experiments should be from the above list. 2025-04-21T20:23:40.1945104Z  2025-04-21T20:23:40.1945602Z  @User1,-lf,split_build 2025-04-21T20:23:40.1946237Z  @User2,lf 2025-04-21T20:23:40.1946778Z  @User3,split_build 2025-04-21T20:23:40.1947326Z """ 2025-04-21T20:23:40.1947850Z  2025-04-21T20:23:40.1948289Z import json 2025-04-21T20:23:40.1948829Z import logging 2025-04-21T20:23:40.1949363Z import os 2025-04-21T20:23:40.1949898Z import random 2025-04-21T20:23:40.1950473Z import re 2025-04-21T20:23:40.1950996Z import sys 2025-04-21T20:23:40.1951595Z from argparse import ArgumentParser 2025-04-21T20:23:40.1952292Z from collections.abc import Iterable 2025-04-21T20:23:40.1953013Z from functools import cache 2025-04-21T20:23:40.1953644Z from logging import LogRecord 2025-04-21T20:23:40.1954330Z from typing import Any, NamedTuple 2025-04-21T20:23:40.2027580Z from urllib.request import Request, urlopen 2025-04-21T20:23:40.2028337Z  2025-04-21T20:23:40.2028768Z import yaml 2025-04-21T20:23:40.2029258Z from github import Auth, Github 2025-04-21T20:23:40.2029844Z from github.Issue import Issue 2025-04-21T20:23:40.2030373Z  2025-04-21T20:23:40.2030770Z  2025-04-21T20:23:40.2031263Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-04-21T20:23:40.2032055Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-04-21T20:23:40.2033052Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-04-21T20:23:40.2033809Z  2025-04-21T20:23:40.2034298Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-04-21T20:23:40.2035076Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-04-21T20:23:40.2035677Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-04-21T20:23:40.2036362Z OPT_OUT_LABEL = "no-runner-experiments" 2025-04-21T20:23:40.2036938Z  2025-04-21T20:23:40.2037377Z SETTING_EXPERIMENTS = "experiments" 2025-04-21T20:23:40.2037920Z  2025-04-21T20:23:40.2038327Z LF_FLEET_EXPERIMENT = "lf" 2025-04-21T20:23:40.2038862Z CANARY_FLEET_SUFFIX = ".c" 2025-04-21T20:23:40.2039357Z  2025-04-21T20:23:40.2039727Z  2025-04-21T20:23:40.2040132Z class Experiment(NamedTuple): 2025-04-21T20:23:40.2040684Z  rollout_perc: float = ( 2025-04-21T20:23:40.2041671Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-04-21T20:23:40.2042427Z  ) 2025-04-21T20:23:40.2042850Z  all_branches: bool = ( 2025-04-21T20:23:40.2043586Z  False # If True, the experiment is also enabled on the exception branches 2025-04-21T20:23:40.2044378Z  ) 2025-04-21T20:23:40.2044915Z  default: bool = ( 2025-04-21T20:23:40.2045592Z  True # If True, the experiment is enabled by default for all queries 2025-04-21T20:23:40.2046287Z  ) 2025-04-21T20:23:40.2046684Z  2025-04-21T20:23:40.2047100Z  # Add more fields as needed 2025-04-21T20:23:40.2047613Z  2025-04-21T20:23:40.2047987Z  2025-04-21T20:23:40.2048395Z class Settings(NamedTuple): 2025-04-21T20:23:40.2048907Z  """ 2025-04-21T20:23:40.2049433Z  Settings for the experiments that can be opted into. 2025-04-21T20:23:40.2050084Z  """ 2025-04-21T20:23:40.2050480Z  2025-04-21T20:23:40.2050924Z  experiments: dict[str, Experiment] = {} 2025-04-21T20:23:40.2051490Z  2025-04-21T20:23:40.2051990Z  2025-04-21T20:23:40.2052455Z class ColorFormatter(logging.Formatter): 2025-04-21T20:23:40.2053167Z  """Color codes the log messages based on the log level""" 2025-04-21T20:23:40.2053802Z  2025-04-21T20:23:40.2054187Z  COLORS = { 2025-04-21T20:23:40.2054917Z  "WARNING": "\033[33m", # Yellow 2025-04-21T20:23:40.2055570Z  "ERROR": "\033[31m", # Red 2025-04-21T20:23:40.2056153Z  "CRITICAL": "\033[31m", # Red 2025-04-21T20:23:40.2056735Z  "INFO": "\033[0m", # Reset 2025-04-21T20:23:40.2057298Z  "DEBUG": "\033[0m", # Reset 2025-04-21T20:23:40.2057825Z  } 2025-04-21T20:23:40.2058219Z  2025-04-21T20:23:40.2058701Z  def format(self, record: LogRecord) -> str: 2025-04-21T20:23:40.2059524Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-04-21T20:23:40.2060377Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-04-21T20:23:40.2061014Z  return super().format(record) 2025-04-21T20:23:40.2061548Z  2025-04-21T20:23:40.2061926Z  2025-04-21T20:23:40.2062350Z handler = logging.StreamHandler() 2025-04-21T20:23:40.2063150Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-04-21T20:23:40.2063914Z  2025-04-21T20:23:40.2064419Z log = logging.getLogger(os.path.basename(__file__)) 2025-04-21T20:23:40.2065310Z log.addHandler(handler) 2025-04-21T20:23:40.2065843Z log.setLevel(logging.INFO) 2025-04-21T20:23:40.2066348Z  2025-04-21T20:23:40.2066720Z  2025-04-21T20:23:40.2067240Z def set_github_output(key: str, value: str) -> None: 2025-04-21T20:23:40.2067872Z  """ 2025-04-21T20:23:40.2068452Z  Defines outputs of the github action that invokes this script 2025-04-21T20:23:40.2069137Z  """ 2025-04-21T20:23:40.2069564Z  if not GITHUB_OUTPUT: 2025-04-21T20:23:40.2070717Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-04-21T20:23:40.2071925Z  log.warning( 2025-04-21T20:23:40.2072870Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-04-21T20:23:40.2073873Z  ) 2025-04-21T20:23:40.2074384Z  print(f"::set-output name={key}::{value}") 2025-04-21T20:23:40.2075094Z  return 2025-04-21T20:23:40.2075536Z  2025-04-21T20:23:40.2075963Z  with open(GITHUB_OUTPUT, "a") as f: 2025-04-21T20:23:40.2076761Z  log.info(f"Setting output: {key}='{value}'") 2025-04-21T20:23:40.2077400Z  f.write(f"{key}={value}\n") 2025-04-21T20:23:40.2077930Z  2025-04-21T20:23:40.2078309Z  2025-04-21T20:23:40.2078876Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-04-21T20:23:40.2079608Z  return frozenset( 2025-04-21T20:23:40.2080323Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-04-21T20:23:40.2081059Z  ) 2025-04-21T20:23:40.2081465Z  2025-04-21T20:23:40.2081838Z  2025-04-21T20:23:40.2082241Z def parse_args() -> Any: 2025-04-21T20:23:40.2082912Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-04-21T20:23:40.2083864Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-04-21T20:23:40.2084835Z  parser.add_argument( 2025-04-21T20:23:40.2085386Z  "--github-issue-repo", 2025-04-21T20:23:40.2085927Z  type=str, 2025-04-21T20:23:40.2086406Z  required=False, 2025-04-21T20:23:40.2087080Z  default="pytorch/test-infra", 2025-04-21T20:23:40.2087708Z  help="GitHub repo to get the issue", 2025-04-21T20:23:40.2088273Z  ) 2025-04-21T20:23:40.2088695Z  parser.add_argument( 2025-04-21T20:23:40.2089216Z  "--github-repo", 2025-04-21T20:23:40.2089725Z  type=str, 2025-04-21T20:23:40.2090206Z  required=True, 2025-04-21T20:23:40.2090770Z  help="GitHub repo where CI is running", 2025-04-21T20:23:40.2091346Z  ) 2025-04-21T20:23:40.2091776Z  parser.add_argument( 2025-04-21T20:23:40.2092482Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-04-21T20:23:40.2093208Z  ) 2025-04-21T20:23:40.2093621Z  parser.add_argument( 2025-04-21T20:23:40.2094347Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-04-21T20:23:40.2095356Z  ) 2025-04-21T20:23:40.2095791Z  parser.add_argument( 2025-04-21T20:23:40.2096527Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-04-21T20:23:40.2097272Z  ) 2025-04-21T20:23:40.2097702Z  parser.add_argument( 2025-04-21T20:23:40.2098463Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-04-21T20:23:40.2099229Z  ) 2025-04-21T20:23:40.2099668Z  parser.add_argument( 2025-04-21T20:23:40.2100198Z  "--github-ref-type", 2025-04-21T20:23:40.2100730Z  type=str, 2025-04-21T20:23:40.2101202Z  required=True, 2025-04-21T20:23:40.2101792Z  help="Current GitHub ref type, branch or tag", 2025-04-21T20:23:40.2102388Z  ) 2025-04-21T20:23:40.2102810Z  parser.add_argument( 2025-04-21T20:23:40.2103356Z  "--eligible-experiments", 2025-04-21T20:23:40.2103951Z  type=_str_comma_separated_to_set, 2025-04-21T20:23:40.2104529Z  required=False, 2025-04-21T20:23:40.2105287Z  default="", 2025-04-21T20:23:40.2106247Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-04-21T20:23:40.2107231Z  ) 2025-04-21T20:23:40.2107650Z  parser.add_argument( 2025-04-21T20:23:40.2108159Z  "--pr-number", 2025-04-21T20:23:40.2108655Z  type=str, 2025-04-21T20:23:40.2109135Z  required=False, 2025-04-21T20:23:40.2109641Z  default="", 2025-04-21T20:23:40.2110223Z  help="the optional PR number where this is run", 2025-04-21T20:23:40.2110987Z  ) 2025-04-21T20:23:40.2111378Z  2025-04-21T20:23:40.2111794Z  return parser.parse_args() 2025-04-21T20:23:40.2112318Z  2025-04-21T20:23:40.2112688Z  2025-04-21T20:23:40.2113349Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-04-21T20:23:40.2114182Z  auth = Auth.Token(github_token) 2025-04-21T20:23:40.2114859Z  return Github(auth=auth) 2025-04-21T20:23:40.2115379Z  2025-04-21T20:23:40.2115751Z  2025-04-21T20:23:40.2116465Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-04-21T20:23:40.2117330Z  repo = gh.get_repo(repo) 2025-04-21T20:23:40.2117918Z  return repo.get_issue(number=issue_num) 2025-04-21T20:23:40.2118487Z  2025-04-21T20:23:40.2118851Z  2025-04-21T20:23:40.2119258Z def get_potential_pr_author( 2025-04-21T20:23:40.2119988Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-04-21T20:23:40.2120735Z ) -> str: 2025-04-21T20:23:40.2121463Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-04-21T20:23:40.2122397Z  # Fetch the actual username from the original PR. The PR number is 2025-04-21T20:23:40.2123244Z  # embedded in the tag name: ciflow// 2025-04-21T20:23:40.2123867Z  2025-04-21T20:23:40.2124285Z  gh = get_gh_client(github_token) 2025-04-21T20:23:40.2124930Z  2025-04-21T20:23:40.2125457Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-04-21T20:23:40.2126164Z  split_tag = ref_name.split("/") 2025-04-21T20:23:40.2126722Z  if ( 2025-04-21T20:23:40.2127178Z  len(split_tag) == 3 2025-04-21T20:23:40.2127751Z  and split_tag[0] == "ciflow" 2025-04-21T20:23:40.2128348Z  and split_tag[2].isnumeric() 2025-04-21T20:23:40.2128892Z  ): 2025-04-21T20:23:40.2129354Z  pr_number = split_tag[2] 2025-04-21T20:23:40.2129914Z  try: 2025-04-21T20:23:40.2130432Z  repository = gh.get_repo(repo) 2025-04-21T20:23:40.2131130Z  pull = repository.get_pull(number=int(pr_number)) 2025-04-21T20:23:40.2131811Z  except Exception as e: 2025-04-21T20:23:40.2132409Z  raise Exception( # noqa: TRY002 2025-04-21T20:23:40.2133166Z  f"issue with pull request {pr_number} from repo {repository}" 2025-04-21T20:23:40.2133880Z  ) from e 2025-04-21T20:23:40.2134513Z  return pull.user.login # type: ignore[no-any-return] 2025-04-21T20:23:40.2135384Z  # In all other cases, return the original input username 2025-04-21T20:23:40.2136039Z  return username 2025-04-21T20:23:40.2136528Z  2025-04-21T20:23:40.2136893Z  2025-04-21T20:23:40.2137357Z def is_exception_branch(branch: str) -> bool: 2025-04-21T20:23:40.2137934Z  """ 2025-04-21T20:23:40.2138668Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-04-21T20:23:40.2139503Z  """ 2025-04-21T20:23:40.2140123Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-04-21T20:23:40.2140857Z  2025-04-21T20:23:40.2141224Z  2025-04-21T20:23:40.2141653Z def load_yaml(yaml_text: str) -> Any: 2025-04-21T20:23:40.2142205Z  try: 2025-04-21T20:23:40.2142670Z  data = yaml.safe_load(yaml_text) 2025-04-21T20:23:40.2143242Z  return data 2025-04-21T20:23:40.2143896Z  except yaml.YAMLError: 2025-04-21T20:23:40.2144472Z  log.exception("Error loading YAML") 2025-04-21T20:23:40.2145151Z  raise 2025-04-21T20:23:40.2145582Z  2025-04-21T20:23:40.2145946Z  2025-04-21T20:23:40.2146643Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-04-21T20:23:40.2147448Z  """ 2025-04-21T20:23:40.2148160Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-04-21T20:23:40.2148968Z  2025-04-21T20:23:40.2149566Z  If the issue body contains "---" then the text above that is the settings 2025-04-21T20:23:40.2150407Z  and the text below is the list of opted in users. 2025-04-21T20:23:40.2151011Z  2025-04-21T20:23:40.2151642Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-04-21T20:23:40.2152406Z  """ 2025-04-21T20:23:40.2152930Z  rollout_state_parts = rollout_state.split("---") 2025-04-21T20:23:40.2153598Z  if len(rollout_state_parts) >= 2: 2025-04-21T20:23:40.2154433Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-04-21T20:23:40.2155200Z  else: 2025-04-21T20:23:40.2155649Z  return "", rollout_state 2025-04-21T20:23:40.2156176Z  2025-04-21T20:23:40.2156534Z  2025-04-21T20:23:40.2156971Z class UserOptins(dict[str, list[str]]): 2025-04-21T20:23:40.2157538Z  """ 2025-04-21T20:23:40.2158152Z  Dictionary of users with a list of features they have opted into 2025-04-21T20:23:40.2158867Z  """ 2025-04-21T20:23:40.2159264Z  2025-04-21T20:23:40.2159628Z  2025-04-21T20:23:40.2160382Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-04-21T20:23:40.2161186Z  """ 2025-04-21T20:23:40.2161997Z  Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-04-21T20:23:40.2162899Z  2025-04-21T20:23:40.2163779Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-04-21T20:23:40.2164977Z  - Example line: "@User1,lf,split_build" 2025-04-21T20:23:40.2165738Z  - A "#" prefix indicates the user is opted out of all experiments 2025-04-21T20:23:40.2166422Z  2025-04-21T20:23:40.2166785Z  2025-04-21T20:23:40.2167148Z  """ 2025-04-21T20:23:40.2167567Z  optins = UserOptins() 2025-04-21T20:23:40.2168140Z  for user in user_optin_text.split("\n"): 2025-04-21T20:23:40.2168760Z  user = user.strip("\r\n\t -") 2025-04-21T20:23:40.2169384Z  if not user or not user.startswith("@"): 2025-04-21T20:23:40.2170018Z  # Not a valid user. Skip 2025-04-21T20:23:40.2170568Z  continue 2025-04-21T20:23:40.2171023Z  2025-04-21T20:23:40.2171399Z  if user: 2025-04-21T20:23:40.2171917Z  usr_name = user.split(",")[0].strip("@") 2025-04-21T20:23:40.2172684Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-04-21T20:23:40.2173378Z  2025-04-21T20:23:40.2173761Z  return optins 2025-04-21T20:23:40.2174217Z  2025-04-21T20:23:40.2174668Z  2025-04-21T20:23:40.2175227Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-04-21T20:23:40.2175900Z  """ 2025-04-21T20:23:40.2176372Z  Check if the experiment name is valid. 2025-04-21T20:23:40.2176979Z  A valid name: 2025-04-21T20:23:40.2177729Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-04-21T20:23:40.2178895Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-04-21T20:23:40.2179667Z  - Cannot contain spaces 2025-04-21T20:23:40.2180183Z  """ 2025-04-21T20:23:40.2180568Z  2025-04-21T20:23:40.2181069Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-04-21T20:23:40.2181852Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-04-21T20:23:40.2182500Z  2025-04-21T20:23:40.2182872Z  if valid: 2025-04-21T20:23:40.2183309Z  return True 2025-04-21T20:23:40.2183763Z  2025-04-21T20:23:40.2184138Z  log.error( 2025-04-21T20:23:40.2185817Z  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-04-21T20:23:40.2187475Z  ) 2025-04-21T20:23:40.2187870Z  return False 2025-04-21T20:23:40.2188311Z  2025-04-21T20:23:40.2188665Z  2025-04-21T20:23:40.2189337Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-04-21T20:23:40.2190230Z  """ 2025-04-21T20:23:40.2190945Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-04-21T20:23:40.2191722Z  """ 2025-04-21T20:23:40.2192116Z  try: 2025-04-21T20:23:40.2192532Z  if settings_text: 2025-04-21T20:23:40.2193349Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-04-21T20:23:40.2194209Z  # for easy reading 2025-04-21T20:23:40.2195208Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-04-21T20:23:40.2196188Z  # the backtick character in shell commands. 2025-04-21T20:23:40.2196873Z  backtick = chr(96) # backtick character 2025-04-21T20:23:40.2197619Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-04-21T20:23:40.2198365Z  settings = load_yaml(settings_text) 2025-04-21T20:23:40.2198919Z  2025-04-21T20:23:40.2199567Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-04-21T20:23:40.2200369Z  experiments = {} 2025-04-21T20:23:40.2200873Z  2025-04-21T20:23:40.2201484Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-04-21T20:23:40.2202348Z  if not is_valid_experiment_name(exp_name): 2025-04-21T20:23:40.2203548Z  # 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-04-21T20:23:40.2204793Z  continue 2025-04-21T20:23:40.2205305Z  2025-04-21T20:23:40.2205714Z  valid_settings = {} 2025-04-21T20:23:40.2206309Z  for setting in exp_settings: 2025-04-21T20:23:40.2206944Z  if setting not in Experiment._fields: 2025-04-21T20:23:40.2207554Z  log.warning( 2025-04-21T20:23:40.2208357Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-04-21T20:23:40.2209130Z  ) 2025-04-21T20:23:40.2209617Z  else: 2025-04-21T20:23:40.2210214Z  valid_settings[setting] = exp_settings[setting] 2025-04-21T20:23:40.2210839Z  2025-04-21T20:23:40.2211362Z  experiments[exp_name] = Experiment(**valid_settings) 2025-04-21T20:23:40.2212206Z  return Settings(experiments) 2025-04-21T20:23:40.2212757Z  2025-04-21T20:23:40.2213141Z  except Exception: 2025-04-21T20:23:40.2213727Z  log.exception("Failed to parse settings") 2025-04-21T20:23:40.2214328Z  2025-04-21T20:23:40.2214819Z  return Settings() 2025-04-21T20:23:40.2215299Z  2025-04-21T20:23:40.2215656Z  2025-04-21T20:23:40.2216154Z def parse_settings(rollout_state: str) -> Settings: 2025-04-21T20:23:40.2216778Z  """ 2025-04-21T20:23:40.2217284Z  Parse settings, if any, from the rollout state. 2025-04-21T20:23:40.2217884Z  2025-04-21T20:23:40.2218475Z  If the issue body contains "---" then the text above that is the settings 2025-04-21T20:23:40.2219318Z  and the text below is the list of opted in users. 2025-04-21T20:23:40.2219923Z  2025-04-21T20:23:40.2220582Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-04-21T20:23:40.2221378Z  """ 2025-04-21T20:23:40.2222008Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-04-21T20:23:40.2223073Z  return parse_settings_from_text(settings_text) 2025-04-21T20:23:40.2223689Z  2025-04-21T20:23:40.2224049Z  2025-04-21T20:23:40.2224536Z def parse_users(rollout_state: str) -> UserOptins: 2025-04-21T20:23:40.2225261Z  """ 2025-04-21T20:23:40.2225712Z  Parse users from the rollout state. 2025-04-21T20:23:40.2226265Z  2025-04-21T20:23:40.2226626Z  """ 2025-04-21T20:23:40.2227231Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-04-21T20:23:40.2228056Z  return parse_user_opt_in_from_text(users_text) 2025-04-21T20:23:40.2228653Z  2025-04-21T20:23:40.2229006Z  2025-04-21T20:23:40.2229689Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-04-21T20:23:40.2230510Z  """ 2025-04-21T20:23:40.2230996Z  Check if a user is opted into an experiment 2025-04-21T20:23:40.2231587Z  """ 2025-04-21T20:23:40.2232114Z  return experiment_name in user_optins.get(user, []) 2025-04-21T20:23:40.2232740Z  2025-04-21T20:23:40.2233096Z  2025-04-21T20:23:40.2233775Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-04-21T20:23:40.2234693Z  """ 2025-04-21T20:23:40.2235233Z  Check if a user explicitly opted out of an experiment 2025-04-21T20:23:40.2235873Z  """ 2025-04-21T20:23:40.2236478Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-04-21T20:23:40.2237253Z  experiment_optout = "-" + experiment_name 2025-04-21T20:23:40.2237982Z  if experiment_optout not in user_optins.get(user, []): 2025-04-21T20:23:40.2238641Z  return False 2025-04-21T20:23:40.2239101Z  2025-04-21T20:23:40.2239606Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-04-21T20:23:40.2240263Z  log.warning( 2025-04-21T20:23:40.2241159Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-04-21T20:23:40.2242111Z  ) 2025-04-21T20:23:40.2242515Z  2025-04-21T20:23:40.2242887Z  return True 2025-04-21T20:23:40.2243325Z  2025-04-21T20:23:40.2243681Z  2025-04-21T20:23:40.2244074Z def get_runner_prefix( 2025-04-21T20:23:40.2244670Z  rollout_state: str, 2025-04-21T20:23:40.2245221Z  workflow_requestors: Iterable[str], 2025-04-21T20:23:40.2245784Z  branch: str, 2025-04-21T20:23:40.2246377Z  eligible_experiments: frozenset[str] = frozenset(), 2025-04-21T20:23:40.2247171Z  is_canary: bool = False, 2025-04-21T20:23:40.2247669Z ) -> str: 2025-04-21T20:23:40.2248158Z  settings = parse_settings(rollout_state) 2025-04-21T20:23:40.2248807Z  user_optins = parse_users(rollout_state) 2025-04-21T20:23:40.2249375Z  2025-04-21T20:23:40.2249757Z  fleet_prefix = "" 2025-04-21T20:23:40.2250243Z  prefixes = [] 2025-04-21T20:23:40.2250963Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-04-21T20:23:40.2251997Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-04-21T20:23:40.2252770Z  log.info( 2025-04-21T20:23:40.2253539Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-04-21T20:23:40.2254354Z  ) 2025-04-21T20:23:40.2254907Z  continue 2025-04-21T20:23:40.2255363Z  2025-04-21T20:23:40.2255773Z  if eligible_experiments: 2025-04-21T20:23:40.2256535Z  if experiment_name not in eligible_experiments: 2025-04-21T20:23:40.2257254Z  exp_list = ", ".join(eligible_experiments) 2025-04-21T20:23:40.2257857Z  log.info( 2025-04-21T20:23:40.2258741Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-04-21T20:23:40.2259640Z  ) 2025-04-21T20:23:40.2260102Z  continue 2025-04-21T20:23:40.2260667Z  elif not experiment_settings.default: 2025-04-21T20:23:40.2261255Z  log.info( 2025-04-21T20:23:40.2262010Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-04-21T20:23:40.2262807Z  ) 2025-04-21T20:23:40.2263245Z  continue 2025-04-21T20:23:40.2263692Z  2025-04-21T20:23:40.2264208Z  # Is any workflow_requestor opted out to this experiment? 2025-04-21T20:23:40.2265008Z  opted_out_users = [ 2025-04-21T20:23:40.2265530Z  requestor 2025-04-21T20:23:40.2266078Z  for requestor in workflow_requestors 2025-04-21T20:23:40.2266823Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-04-21T20:23:40.2267510Z  ] 2025-04-21T20:23:40.2267904Z  2025-04-21T20:23:40.2268296Z  if opted_out_users: 2025-04-21T20:23:40.2268825Z  log.info( 2025-04-21T20:23:40.2269545Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-04-21T20:23:40.2270305Z  ) 2025-04-21T20:23:40.2270744Z  continue 2025-04-21T20:23:40.2271210Z  2025-04-21T20:23:40.2271715Z  # Is any workflow_requestor opted in to this experiment? 2025-04-21T20:23:40.2272418Z  opted_in_users = [ 2025-04-21T20:23:40.2272934Z  requestor 2025-04-21T20:23:40.2273479Z  for requestor in workflow_requestors 2025-04-21T20:23:40.2274218Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-04-21T20:23:40.2274983Z  ] 2025-04-21T20:23:40.2275389Z  2025-04-21T20:23:40.2275765Z  enabled = False 2025-04-21T20:23:40.2276270Z  if opted_in_users: 2025-04-21T20:23:40.2276782Z  log.info( 2025-04-21T20:23:40.2277474Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-04-21T20:23:40.2278202Z  ) 2025-04-21T20:23:40.2278644Z  enabled = True 2025-04-21T20:23:40.2279262Z  2025-04-21T20:23:40.2279705Z  elif experiment_settings.rollout_perc: 2025-04-21T20:23:40.2280637Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-04-21T20:23:40.2281673Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-04-21T20:23:40.2282378Z  log.info( 2025-04-21T20:23:40.2283348Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-04-21T20:23:40.2284328Z  ) 2025-04-21T20:23:40.2284903Z  enabled = True 2025-04-21T20:23:40.2285410Z  2025-04-21T20:23:40.2285794Z  if enabled: 2025-04-21T20:23:40.2286292Z  label = experiment_name 2025-04-21T20:23:40.2286930Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-04-21T20:23:40.2287836Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-04-21T20:23:40.2288931Z  # - If it's enabled, then we always list it's prefix first 2025-04-21T20:23:40.2289779Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-04-21T20:23:40.2290494Z  if is_canary: 2025-04-21T20:23:40.2291079Z  label += CANARY_FLEET_SUFFIX 2025-04-21T20:23:40.2291681Z  fleet_prefix = label 2025-04-21T20:23:40.2292221Z  else: 2025-04-21T20:23:40.2292721Z  prefixes.append(label) 2025-04-21T20:23:40.2293258Z  2025-04-21T20:23:40.2293649Z  if len(prefixes) > 1: 2025-04-21T20:23:40.2294153Z  log.error( 2025-04-21T20:23:40.2295418Z  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-04-21T20:23:40.2296622Z  ) 2025-04-21T20:23:40.2297068Z  prefixes = prefixes[:1] 2025-04-21T20:23:40.2297600Z  2025-04-21T20:23:40.2297992Z  # Fleet always comes first 2025-04-21T20:23:40.2298534Z  if fleet_prefix: 2025-04-21T20:23:40.2299054Z  prefixes.insert(0, fleet_prefix) 2025-04-21T20:23:40.2299605Z  2025-04-21T20:23:40.2300097Z  return ".".join(prefixes) + "." if prefixes else "" 2025-04-21T20:23:40.2300709Z  2025-04-21T20:23:40.2301068Z  2025-04-21T20:23:40.2301760Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-04-21T20:23:40.2302592Z  """ 2025-04-21T20:23:40.2303246Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-04-21T20:23:40.2304022Z  2025-04-21T20:23:40.2304731Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-04-21T20:23:40.2305510Z  """ 2025-04-21T20:23:40.2305968Z  gh = get_gh_client(github_token) 2025-04-21T20:23:40.2306581Z  issue = get_issue(gh, repo, issue_num) 2025-04-21T20:23:40.2307294Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-04-21T20:23:40.2307927Z  2025-04-21T20:23:40.2308288Z  2025-04-21T20:23:40.2308929Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-04-21T20:23:40.2309747Z  for _ in range(num_retries): 2025-04-21T20:23:40.2310275Z  try: 2025-04-21T20:23:40.2310759Z  req = Request(url=url, headers=headers) 2025-04-21T20:23:40.2311499Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-04-21T20:23:40.2312336Z  return json.loads(content) 2025-04-21T20:23:40.2312916Z  except Exception as e: 2025-04-21T20:23:40.2313529Z  log.warning(f"Could not download {url}: {e}") 2025-04-21T20:23:40.2314136Z  2025-04-21T20:23:40.2314864Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-04-21T20:23:40.2315642Z  return {} 2025-04-21T20:23:40.2316064Z  2025-04-21T20:23:40.2316421Z  2025-04-21T20:23:40.2316785Z @cache 2025-04-21T20:23:40.2317484Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-04-21T20:23:40.2318299Z  """ 2025-04-21T20:23:40.2318737Z  Dynamically get PR information 2025-04-21T20:23:40.2319274Z  """ 2025-04-21T20:23:40.2319835Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-04-21T20:23:40.2320512Z  headers = { 2025-04-21T20:23:40.2321056Z  "Accept": "application/vnd.github.v3+json", 2025-04-21T20:23:40.2321720Z  "Authorization": f"token {github_token}", 2025-04-21T20:23:40.2322302Z  } 2025-04-21T20:23:40.2322907Z  json_response: dict[str, Any] = download_json( 2025-04-21T20:23:40.2323663Z  url=f"{github_api}/issues/{pr_number}", 2025-04-21T20:23:40.2324252Z  headers=headers, 2025-04-21T20:23:40.2324838Z  ) 2025-04-21T20:23:40.2325227Z  2025-04-21T20:23:40.2325657Z  if not json_response: 2025-04-21T20:23:40.2326321Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-04-21T20:23:40.2326992Z  return {} 2025-04-21T20:23:40.2327438Z  2025-04-21T20:23:40.2327825Z  return json_response 2025-04-21T20:23:40.2328308Z  2025-04-21T20:23:40.2328671Z  2025-04-21T20:23:40.2329313Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-04-21T20:23:40.2330096Z  """ 2025-04-21T20:23:40.2330687Z  Dynamically get the latest list of labels from the pull request 2025-04-21T20:23:40.2331399Z  """ 2025-04-21T20:23:40.2331936Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-04-21T20:23:40.2332603Z  return { 2025-04-21T20:23:40.2333257Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-04-21T20:23:40.2333993Z  } 2025-04-21T20:23:40.2334380Z  2025-04-21T20:23:40.2334839Z  2025-04-21T20:23:40.2335226Z def main() -> None: 2025-04-21T20:23:40.2335702Z  args = parse_args() 2025-04-21T20:23:40.2336207Z  2025-04-21T20:23:40.2336662Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-04-21T20:23:40.2337244Z  2025-04-21T20:23:40.2337648Z  # Check if the PR is opt-out 2025-04-21T20:23:40.2338201Z  if args.pr_number: 2025-04-21T20:23:40.2338960Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-04-21T20:23:40.2339779Z  if OPT_OUT_LABEL in labels: 2025-04-21T20:23:40.2340336Z  log.info( 2025-04-21T20:23:40.2341116Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-04-21T20:23:40.2341933Z  ) 2025-04-21T20:23:40.2342566Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-04-21T20:23:40.2343295Z  sys.exit() 2025-04-21T20:23:40.2343768Z  2025-04-21T20:23:40.2344130Z  try: 2025-04-21T20:23:40.2344721Z  rollout_state = get_rollout_state_from_issue( 2025-04-21T20:23:40.2345512Z  args.github_token, args.github_issue_repo, args.github_issue 2025-04-21T20:23:40.2346345Z  ) 2025-04-21T20:23:40.2346746Z  2025-04-21T20:23:40.2347175Z  username = get_potential_pr_author( 2025-04-21T20:23:40.2347765Z  args.github_token, 2025-04-21T20:23:40.2348310Z  args.github_repo, 2025-04-21T20:23:40.2348857Z  args.github_actor, 2025-04-21T20:23:40.2349405Z  args.github_ref_type, 2025-04-21T20:23:40.2349960Z  args.github_branch, 2025-04-21T20:23:40.2350466Z  ) 2025-04-21T20:23:40.2350864Z  2025-04-21T20:23:40.2351378Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-04-21T20:23:40.2352034Z  2025-04-21T20:23:40.2352482Z  runner_label_prefix = get_runner_prefix( 2025-04-21T20:23:40.2353078Z  rollout_state, 2025-04-21T20:23:40.2353641Z  (args.github_issue_owner, username), 2025-04-21T20:23:40.2354239Z  args.github_branch, 2025-04-21T20:23:40.2354904Z  args.eligible_experiments, 2025-04-21T20:23:40.2355459Z  is_canary, 2025-04-21T20:23:40.2356050Z  ) 2025-04-21T20:23:40.2356450Z  2025-04-21T20:23:40.2356842Z  except Exception as e: 2025-04-21T20:23:40.2357349Z  log.error( 2025-04-21T20:23:40.2358102Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-04-21T20:23:40.2358922Z  ) 2025-04-21T20:23:40.2359318Z  2025-04-21T20:23:40.2359886Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-04-21T20:23:40.2360583Z  2025-04-21T20:23:40.2360937Z  2025-04-21T20:23:40.2361318Z if __name__ == "__main__": 2025-04-21T20:23:40.2361812Z  main() 2025-04-21T20:23:40.2362222Z  2025-04-21T20:23:40.2362589Z EOF 2025-04-21T20:23:40.2362965Z  2025-04-21T20:23:40.2363361Z cat runner_determinator.py 2025-04-21T20:23:40.2707996Z shell: /usr/bin/bash -e {0} 2025-04-21T20:23:40.2708803Z env: 2025-04-21T20:23:40.2709548Z GITHUB_TOKEN: *** 2025-04-21T20:23:40.2709979Z ISSUE_NUMBER: 5132 2025-04-21T20:23:40.2710429Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-04-21T20:23:40.2710938Z ISSUE_OWNER: 2025-04-21T20:23:40.2711351Z CHECK_EXPERIMENTS: 2025-04-21T20:23:40.2711769Z PR_NUMBER: 2025-04-21T20:23:40.2712164Z ##[endgroup] 2025-04-21T20:23:40.2926115Z # flake8: noqa: G004 2025-04-21T20:23:40.2926475Z 2025-04-21T20:23:40.2926917Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-04-21T20:23:40.2927882Z # must be kept in sync. You can do it easily by running the following command: 2025-04-21T20:23:40.2928704Z # python .github/scripts/update_runner_determinator.py 2025-04-21T20:23:40.2929146Z 2025-04-21T20:23:40.2929339Z """ 2025-04-21T20:23:40.2929923Z This runner determinator is used to determine which set of runners to run a 2025-04-21T20:23:40.2930840Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-04-21T20:23:40.2931796Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-04-21T20:23:40.2932624Z of which runners should be used to run which job. 2025-04-21T20:23:40.2933031Z 2025-04-21T20:23:40.2933425Z The configuration has two parts, the settings and a list of opted-in users, 2025-04-21T20:23:40.2934322Z separated by a line containing "---". If the line is not present, the 2025-04-21T20:23:40.2935434Z settings are considered to be empty with only the second part, the user 2025-04-21T20:23:40.2936143Z list, defined. 2025-04-21T20:23:40.2936383Z 2025-04-21T20:23:40.2936752Z The first part is a YAML block that defines the rollout settings. This can be 2025-04-21T20:23:40.2937698Z used to define any settings that are needed to determine which runners to use. 2025-04-21T20:23:40.2938757Z It's fields are defined by the RolloutSettings class below. 2025-04-21T20:23:40.2939208Z 2025-04-21T20:23:40.2939619Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-04-21T20:23:40.2940523Z The user list is also a comma separated list of additional features or 2025-04-21T20:23:40.2941281Z experiments which the user could be opted in to. 2025-04-21T20:23:40.2941692Z 2025-04-21T20:23:40.2941894Z The user list has the following rules: 2025-04-21T20:23:40.2942266Z 2025-04-21T20:23:40.2942580Z - Users are GitHub usernames, which must start with the @ prefix 2025-04-21T20:23:40.2943456Z - Each user is also a comma-separated list of features/experiments to enable 2025-04-21T20:23:40.2944240Z - A "#" prefix opts the user out of all experiments 2025-04-21T20:23:40.2945255Z 2025-04-21T20:23:40.2945460Z Example config: 2025-04-21T20:23:40.2945942Z # A list of experiments that can be opted into. 2025-04-21T20:23:40.2946638Z # This defines the behavior they'll induce when opted into. 2025-04-21T20:23:40.2947279Z # Expected syntax is: 2025-04-21T20:23:40.2947935Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-04-21T20:23:40.2949065Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-04-21T20:23:40.2949709Z 2025-04-21T20:23:40.2949893Z experiments: 2025-04-21T20:23:40.2950294Z lf: 2025-04-21T20:23:40.2950674Z rollout_percent: 25 2025-04-21T20:23:40.2951144Z all_branches: false 2025-04-21T20:23:40.2951594Z default: true 2025-04-21T20:23:40.2952012Z --- 2025-04-21T20:23:40.2952212Z 2025-04-21T20:23:40.2952378Z # Opt-ins: 2025-04-21T20:23:40.2952969Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-04-21T20:23:40.2953831Z # and specifying experiments to enable in a comma-separated list. 2025-04-21T20:23:40.2954789Z # To always opt out of an experiment, prefix it with a "-". 2025-04-21T20:23:40.2955495Z # Experiments should be from the above list. 2025-04-21T20:23:40.2955887Z 2025-04-21T20:23:40.2956071Z @User1,-lf,split_build 2025-04-21T20:23:40.2956516Z @User2,lf 2025-04-21T20:23:40.2956908Z @User3,split_build 2025-04-21T20:23:40.2957325Z """ 2025-04-21T20:23:40.2957525Z 2025-04-21T20:23:40.2957688Z import json 2025-04-21T20:23:40.2958070Z import logging 2025-04-21T20:23:40.2958451Z import os 2025-04-21T20:23:40.2958815Z import random 2025-04-21T20:23:40.2959201Z import re 2025-04-21T20:23:40.2959565Z import sys 2025-04-21T20:23:40.2959970Z from argparse import ArgumentParser 2025-04-21T20:23:40.2960503Z from collections.abc import Iterable 2025-04-21T20:23:40.2961028Z from functools import cache 2025-04-21T20:23:40.2961499Z from logging import LogRecord 2025-04-21T20:23:40.2961998Z from typing import Any, NamedTuple 2025-04-21T20:23:40.2962526Z from urllib.request import Request, urlopen 2025-04-21T20:23:40.2962911Z 2025-04-21T20:23:40.2963080Z import yaml 2025-04-21T20:23:40.2963476Z from github import Auth, Github 2025-04-21T20:23:40.2963971Z from github.Issue import Issue 2025-04-21T20:23:40.2964280Z 2025-04-21T20:23:40.2964286Z 2025-04-21T20:23:40.2964514Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-04-21T20:23:40.2965387Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-04-21T20:23:40.2966263Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-04-21T20:23:40.2966829Z 2025-04-21T20:23:40.2967060Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-04-21T20:23:40.2967644Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-04-21T20:23:40.2968155Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-04-21T20:23:40.2968722Z OPT_OUT_LABEL = "no-runner-experiments" 2025-04-21T20:23:40.2969079Z 2025-04-21T20:23:40.2969275Z SETTING_EXPERIMENTS = "experiments" 2025-04-21T20:23:40.2969616Z 2025-04-21T20:23:40.2969801Z LF_FLEET_EXPERIMENT = "lf" 2025-04-21T20:23:40.2970261Z CANARY_FLEET_SUFFIX = ".c" 2025-04-21T20:23:40.2970693Z 2025-04-21T20:23:40.2970700Z 2025-04-21T20:23:40.2970890Z class Experiment(NamedTuple): 2025-04-21T20:23:40.2971371Z rollout_perc: float = ( 2025-04-21T20:23:40.2972014Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-04-21T20:23:40.2972701Z ) 2025-04-21T20:23:40.2973070Z all_branches: bool = ( 2025-04-21T20:23:40.2973692Z False # If True, the experiment is also enabled on the exception branches 2025-04-21T20:23:40.2974377Z ) 2025-04-21T20:23:40.2974961Z default: bool = ( 2025-04-21T20:23:40.2975566Z True # If True, the experiment is enabled by default for all queries 2025-04-21T20:23:40.2976217Z ) 2025-04-21T20:23:40.2976424Z 2025-04-21T20:23:40.2976608Z # Add more fields as needed 2025-04-21T20:23:40.2976915Z 2025-04-21T20:23:40.2976921Z 2025-04-21T20:23:40.2977110Z class Settings(NamedTuple): 2025-04-21T20:23:40.2977547Z """ 2025-04-21T20:23:40.2978004Z Settings for the experiments that can be opted into. 2025-04-21T20:23:40.2978598Z """ 2025-04-21T20:23:40.2978800Z 2025-04-21T20:23:40.2979015Z experiments: dict[str, Experiment] = {} 2025-04-21T20:23:40.2979390Z 2025-04-21T20:23:40.2979396Z 2025-04-21T20:23:40.2979738Z class ColorFormatter(logging.Formatter): 2025-04-21T20:23:40.2980399Z """Color codes the log messages based on the log level""" 2025-04-21T20:23:40.2980843Z 2025-04-21T20:23:40.2981010Z COLORS = { 2025-04-21T20:23:40.2981414Z "WARNING": "\033[33m", # Yellow 2025-04-21T20:23:40.2981933Z "ERROR": "\033[31m", # Red 2025-04-21T20:23:40.2982429Z "CRITICAL": "\033[31m", # Red 2025-04-21T20:23:40.2982934Z "INFO": "\033[0m", # Reset 2025-04-21T20:23:40.2983425Z "DEBUG": "\033[0m", # Reset 2025-04-21T20:23:40.2983904Z } 2025-04-21T20:23:40.2984100Z 2025-04-21T20:23:40.2984318Z def format(self, record: LogRecord) -> str: 2025-04-21T20:23:40.2985298Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-04-21T20:23:40.2986119Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-04-21T20:23:40.2986711Z return super().format(record) 2025-04-21T20:23:40.2987049Z 2025-04-21T20:23:40.2987056Z 2025-04-21T20:23:40.2987266Z handler = logging.StreamHandler() 2025-04-21T20:23:40.2987982Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-04-21T20:23:40.2988557Z 2025-04-21T20:23:40.2988803Z log = logging.getLogger(os.path.basename(__file__)) 2025-04-21T20:23:40.2989385Z log.addHandler(handler) 2025-04-21T20:23:40.2989830Z log.setLevel(logging.INFO) 2025-04-21T20:23:40.2990116Z 2025-04-21T20:23:40.2990122Z 2025-04-21T20:23:40.2990379Z def set_github_output(key: str, value: str) -> None: 2025-04-21T20:23:40.2990950Z """ 2025-04-21T20:23:40.2991460Z Defines outputs of the github action that invokes this script 2025-04-21T20:23:40.2992092Z """ 2025-04-21T20:23:40.2992464Z if not GITHUB_OUTPUT: 2025-04-21T20:23:40.2993539Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-04-21T20:23:40.2994913Z log.warning( 2025-04-21T20:23:40.2995805Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-04-21T20:23:40.2996747Z ) 2025-04-21T20:23:40.3007063Z print(f"::set-output name={key}::{value}") 2025-04-21T20:23:40.3007683Z return 2025-04-21T20:23:40.3007932Z 2025-04-21T20:23:40.3008147Z with open(GITHUB_OUTPUT, "a") as f: 2025-04-21T20:23:40.3008733Z log.info(f"Setting output: {key}='{value}'") 2025-04-21T20:23:40.3009339Z f.write(f"{key}={value}\n") 2025-04-21T20:23:40.3009732Z 2025-04-21T20:23:40.3009739Z 2025-04-21T20:23:40.3010068Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-04-21T20:23:40.3010717Z return frozenset( 2025-04-21T20:23:40.3011345Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-04-21T20:23:40.3012271Z ) 2025-04-21T20:23:40.3012475Z 2025-04-21T20:23:40.3012483Z 2025-04-21T20:23:40.3012667Z def parse_args() -> Any: 2025-04-21T20:23:40.3013236Z parser = ArgumentParser("Get dynamic rollout settings") 2025-04-21T20:23:40.3014126Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-04-21T20:23:40.3015184Z parser.add_argument( 2025-04-21T20:23:40.3015651Z "--github-issue-repo", 2025-04-21T20:23:40.3016124Z type=str, 2025-04-21T20:23:40.3016522Z required=False, 2025-04-21T20:23:40.3016983Z default="pytorch/test-infra", 2025-04-21T20:23:40.3017538Z help="GitHub repo to get the issue", 2025-04-21T20:23:40.3018062Z ) 2025-04-21T20:23:40.3018420Z parser.add_argument( 2025-04-21T20:23:40.3018867Z "--github-repo", 2025-04-21T20:23:40.3019295Z type=str, 2025-04-21T20:23:40.3019698Z required=True, 2025-04-21T20:23:40.3020175Z help="GitHub repo where CI is running", 2025-04-21T20:23:40.3020716Z ) 2025-04-21T20:23:40.3021086Z parser.add_argument( 2025-04-21T20:23:40.3021694Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-04-21T20:23:40.3022499Z ) 2025-04-21T20:23:40.3022875Z parser.add_argument( 2025-04-21T20:23:40.3023517Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-04-21T20:23:40.3024212Z ) 2025-04-21T20:23:40.3024740Z parser.add_argument( 2025-04-21T20:23:40.3025430Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-04-21T20:23:40.3026163Z ) 2025-04-21T20:23:40.3026549Z parser.add_argument( 2025-04-21T20:23:40.3027233Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-04-21T20:23:40.3027982Z ) 2025-04-21T20:23:40.3028342Z parser.add_argument( 2025-04-21T20:23:40.3028804Z "--github-ref-type", 2025-04-21T20:23:40.3029273Z type=str, 2025-04-21T20:23:40.3029677Z required=True, 2025-04-21T20:23:40.3030166Z help="Current GitHub ref type, branch or tag", 2025-04-21T20:23:40.3030729Z ) 2025-04-21T20:23:40.3031092Z parser.add_argument( 2025-04-21T20:23:40.3031565Z "--eligible-experiments", 2025-04-21T20:23:40.3032088Z type=_str_comma_separated_to_set, 2025-04-21T20:23:40.3032607Z required=False, 2025-04-21T20:23:40.3033033Z default="", 2025-04-21T20:23:40.3033893Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-04-21T20:23:40.3035014Z ) 2025-04-21T20:23:40.3035386Z parser.add_argument( 2025-04-21T20:23:40.3035838Z "--pr-number", 2025-04-21T20:23:40.3036251Z type=str, 2025-04-21T20:23:40.3036668Z required=False, 2025-04-21T20:23:40.3037088Z default="", 2025-04-21T20:23:40.3037568Z help="the optional PR number where this is run", 2025-04-21T20:23:40.3038145Z ) 2025-04-21T20:23:40.3038342Z 2025-04-21T20:23:40.3038529Z return parser.parse_args() 2025-04-21T20:23:40.3038841Z 2025-04-21T20:23:40.3038847Z 2025-04-21T20:23:40.3039269Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-04-21T20:23:40.3040043Z auth = Auth.Token(github_token) 2025-04-21T20:23:40.3040554Z return Github(auth=auth) 2025-04-21T20:23:40.3040857Z 2025-04-21T20:23:40.3040863Z 2025-04-21T20:23:40.3041322Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-04-21T20:23:40.3042146Z repo = gh.get_repo(repo) 2025-04-21T20:23:40.3042653Z return repo.get_issue(number=issue_num) 2025-04-21T20:23:40.3043028Z 2025-04-21T20:23:40.3043036Z 2025-04-21T20:23:40.3043217Z def get_potential_pr_author( 2025-04-21T20:23:40.3043862Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-04-21T20:23:40.3044546Z ) -> str: 2025-04-21T20:23:40.3045555Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-04-21T20:23:40.3046393Z # Fetch the actual username from the original PR. The PR number is 2025-04-21T20:23:40.3047163Z # embedded in the tag name: ciflow// 2025-04-21T20:23:40.3047615Z 2025-04-21T20:23:40.3047809Z gh = get_gh_client(github_token) 2025-04-21T20:23:40.3048155Z 2025-04-21T20:23:40.3048424Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-04-21T20:23:40.3049060Z split_tag = ref_name.split("/") 2025-04-21T20:23:40.3049560Z if ( 2025-04-21T20:23:40.3049953Z len(split_tag) == 3 2025-04-21T20:23:40.3050443Z and split_tag[0] == "ciflow" 2025-04-21T20:23:40.3050972Z and split_tag[2].isnumeric() 2025-04-21T20:23:40.3051467Z ): 2025-04-21T20:23:40.3051848Z pr_number = split_tag[2] 2025-04-21T20:23:40.3052335Z try: 2025-04-21T20:23:40.3052755Z repository = gh.get_repo(repo) 2025-04-21T20:23:40.3053391Z pull = repository.get_pull(number=int(pr_number)) 2025-04-21T20:23:40.3053992Z except Exception as e: 2025-04-21T20:23:40.3054521Z raise Exception( # noqa: TRY002 2025-04-21T20:23:40.3055570Z f"issue with pull request {pr_number} from repo {repository}" 2025-04-21T20:23:40.3056253Z ) from e 2025-04-21T20:23:40.3056793Z return pull.user.login # type: ignore[no-any-return] 2025-04-21T20:23:40.3057496Z # In all other cases, return the original input username 2025-04-21T20:23:40.3058090Z return username 2025-04-21T20:23:40.3058329Z 2025-04-21T20:23:40.3058336Z 2025-04-21T20:23:40.3058561Z def is_exception_branch(branch: str) -> bool: 2025-04-21T20:23:40.3059102Z """ 2025-04-21T20:23:40.3059743Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-04-21T20:23:40.3060535Z """ 2025-04-21T20:23:40.3061089Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-04-21T20:23:40.3109103Z 2025-04-21T20:23:40.3109116Z 2025-04-21T20:23:40.3109442Z def load_yaml(yaml_text: str) -> Any: 2025-04-21T20:23:40.3110063Z try: 2025-04-21T20:23:40.3110538Z data = yaml.safe_load(yaml_text) 2025-04-21T20:23:40.3111079Z return data 2025-04-21T20:23:40.3111495Z except yaml.YAMLError: 2025-04-21T20:23:40.3111990Z log.exception("Error loading YAML") 2025-04-21T20:23:40.3112505Z raise 2025-04-21T20:23:40.3112733Z 2025-04-21T20:23:40.3112740Z 2025-04-21T20:23:40.3113166Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-04-21T20:23:40.3113928Z """ 2025-04-21T20:23:40.3114549Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-04-21T20:23:40.3115283Z 2025-04-21T20:23:40.3115634Z If the issue body contains "---" then the text above that is the settings 2025-04-21T20:23:40.3116403Z and the text below is the list of opted in users. 2025-04-21T20:23:40.3116827Z 2025-04-21T20:23:40.3117202Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-04-21T20:23:40.3117909Z """ 2025-04-21T20:23:40.3118362Z rollout_state_parts = rollout_state.split("---") 2025-04-21T20:23:40.3118970Z if len(rollout_state_parts) >= 2: 2025-04-21T20:23:40.3119568Z return rollout_state_parts[0], rollout_state_parts[1] 2025-04-21T20:23:40.3120167Z else: 2025-04-21T20:23:40.3120547Z return "", rollout_state 2025-04-21T20:23:40.3120862Z 2025-04-21T20:23:40.3120875Z 2025-04-21T20:23:40.3121072Z class UserOptins(dict[str, list[str]]): 2025-04-21T20:23:40.3121584Z """ 2025-04-21T20:23:40.3122101Z Dictionary of users with a list of features they have opted into 2025-04-21T20:23:40.3122757Z """ 2025-04-21T20:23:40.3122959Z 2025-04-21T20:23:40.3122966Z 2025-04-21T20:23:40.3123306Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-04-21T20:23:40.3124195Z """ 2025-04-21T20:23:40.3125034Z Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-04-21T20:23:40.3125760Z 2025-04-21T20:23:40.3126402Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-04-21T20:23:40.3127410Z - Example line: "@User1,lf,split_build" 2025-04-21T20:23:40.3128127Z - A "#" prefix indicates the user is opted out of all experiments 2025-04-21T20:23:40.3128633Z 2025-04-21T20:23:40.3128639Z 2025-04-21T20:23:40.3128805Z """ 2025-04-21T20:23:40.3129173Z optins = UserOptins() 2025-04-21T20:23:40.3129673Z for user in user_optin_text.split("\n"): 2025-04-21T20:23:40.3130237Z user = user.strip("\r\n\t -") 2025-04-21T20:23:40.3130799Z if not user or not user.startswith("@"): 2025-04-21T20:23:40.3131356Z # Not a valid user. Skip 2025-04-21T20:23:40.3131852Z continue 2025-04-21T20:23:40.3132115Z 2025-04-21T20:23:40.3132277Z if user: 2025-04-21T20:23:40.3132719Z usr_name = user.split(",")[0].strip("@") 2025-04-21T20:23:40.3133561Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-04-21T20:23:40.3134083Z 2025-04-21T20:23:40.3134258Z return optins 2025-04-21T20:23:40.3134512Z 2025-04-21T20:23:40.3134520Z 2025-04-21T20:23:40.3134927Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-04-21T20:23:40.3135547Z """ 2025-04-21T20:23:40.3135952Z Check if the experiment name is valid. 2025-04-21T20:23:40.3136481Z A valid name: 2025-04-21T20:23:40.3137135Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-04-21T20:23:40.3138102Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-04-21T20:23:40.3138848Z - Cannot contain spaces 2025-04-21T20:23:40.3139311Z """ 2025-04-21T20:23:40.3139511Z 2025-04-21T20:23:40.3139787Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-04-21T20:23:40.3140503Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-04-21T20:23:40.3140959Z 2025-04-21T20:23:40.3141116Z if valid: 2025-04-21T20:23:40.3141510Z return True 2025-04-21T20:23:40.3141752Z 2025-04-21T20:23:40.3141916Z log.error( 2025-04-21T20:23:40.3143384Z 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-04-21T20:23:40.3145137Z ) 2025-04-21T20:23:40.3145500Z return False 2025-04-21T20:23:40.3145742Z 2025-04-21T20:23:40.3145749Z 2025-04-21T20:23:40.3146050Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-04-21T20:23:40.3146678Z """ 2025-04-21T20:23:40.3147261Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-04-21T20:23:40.3147992Z """ 2025-04-21T20:23:40.3148343Z try: 2025-04-21T20:23:40.3148731Z if settings_text: 2025-04-21T20:23:40.3149461Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-04-21T20:23:40.3150273Z # for easy reading 2025-04-21T20:23:40.3151070Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-04-21T20:23:40.3151970Z # the backtick character in shell commands. 2025-04-21T20:23:40.3152583Z backtick = chr(96) # backtick character 2025-04-21T20:23:40.3153248Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-04-21T20:23:40.3153916Z settings = load_yaml(settings_text) 2025-04-21T20:23:40.3154293Z 2025-04-21T20:23:40.3154786Z # For now we just load experiments. We can expand this if/when we add more settings 2025-04-21T20:23:40.3155573Z experiments = {} 2025-04-21T20:23:40.3156043Z 2025-04-21T20:23:40.3156411Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-04-21T20:23:40.3157192Z if not is_valid_experiment_name(exp_name): 2025-04-21T20:23:40.3158334Z # 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-04-21T20:23:40.3159411Z continue 2025-04-21T20:23:40.3159705Z 2025-04-21T20:23:40.3159884Z valid_settings = {} 2025-04-21T20:23:40.3160414Z for setting in exp_settings: 2025-04-21T20:23:40.3160986Z if setting not in Experiment._fields: 2025-04-21T20:23:40.3161553Z log.warning( 2025-04-21T20:23:40.3162270Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-04-21T20:23:40.3163006Z ) 2025-04-21T20:23:40.3163428Z else: 2025-04-21T20:23:40.3163954Z valid_settings[setting] = exp_settings[setting] 2025-04-21T20:23:40.3164390Z 2025-04-21T20:23:40.3164771Z experiments[exp_name] = Experiment(**valid_settings) 2025-04-21T20:23:40.3165537Z return Settings(experiments) 2025-04-21T20:23:40.3165914Z 2025-04-21T20:23:40.3166095Z except Exception: 2025-04-21T20:23:40.3166575Z log.exception("Failed to parse settings") 2025-04-21T20:23:40.3166987Z 2025-04-21T20:23:40.3167157Z return Settings() 2025-04-21T20:23:40.3167422Z 2025-04-21T20:23:40.3167429Z 2025-04-21T20:23:40.3167676Z def parse_settings(rollout_state: str) -> Settings: 2025-04-21T20:23:40.3168255Z """ 2025-04-21T20:23:40.3168691Z Parse settings, if any, from the rollout state. 2025-04-21T20:23:40.3169112Z 2025-04-21T20:23:40.3169467Z If the issue body contains "---" then the text above that is the settings 2025-04-21T20:23:40.3170252Z and the text below is the list of opted in users. 2025-04-21T20:23:40.3170674Z 2025-04-21T20:23:40.3171107Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-04-21T20:23:40.3171860Z """ 2025-04-21T20:23:40.3172414Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-04-21T20:23:40.3173184Z return parse_settings_from_text(settings_text) 2025-04-21T20:23:40.3173596Z 2025-04-21T20:23:40.3173603Z 2025-04-21T20:23:40.3173858Z def parse_users(rollout_state: str) -> UserOptins: 2025-04-21T20:23:40.3174442Z """ 2025-04-21T20:23:40.3175179Z Parse users from the rollout state. 2025-04-21T20:23:40.3175554Z 2025-04-21T20:23:40.3175710Z """ 2025-04-21T20:23:40.3176249Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-04-21T20:23:40.3177005Z return parse_user_opt_in_from_text(users_text) 2025-04-21T20:23:40.3177430Z 2025-04-21T20:23:40.3177436Z 2025-04-21T20:23:40.3177853Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-04-21T20:23:40.3178646Z """ 2025-04-21T20:23:40.3179055Z Check if a user is opted into an experiment 2025-04-21T20:23:40.3179607Z """ 2025-04-21T20:23:40.3180052Z return experiment_name in user_optins.get(user, []) 2025-04-21T20:23:40.3180499Z 2025-04-21T20:23:40.3180506Z 2025-04-21T20:23:40.3180937Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-04-21T20:23:40.3181713Z """ 2025-04-21T20:23:40.3182165Z Check if a user explicitly opted out of an experiment 2025-04-21T20:23:40.3182764Z """ 2025-04-21T20:23:40.3183264Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-04-21T20:23:40.3183970Z experiment_optout = "-" + experiment_name 2025-04-21T20:23:40.3184866Z if experiment_optout not in user_optins.get(user, []): 2025-04-21T20:23:40.3185536Z return False 2025-04-21T20:23:40.3185791Z 2025-04-21T20:23:40.3186062Z if is_user_opted_in(user, user_optins, experiment_name): 2025-04-21T20:23:40.3186916Z log.warning( 2025-04-21T20:23:40.3187742Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-04-21T20:23:40.3188661Z ) 2025-04-21T20:23:40.3188878Z 2025-04-21T20:23:40.3189047Z return True 2025-04-21T20:23:40.3189283Z 2025-04-21T20:23:40.3189290Z 2025-04-21T20:23:40.3189469Z def get_runner_prefix( 2025-04-21T20:23:40.3189908Z rollout_state: str, 2025-04-21T20:23:40.3190366Z workflow_requestors: Iterable[str], 2025-04-21T20:23:40.3190890Z branch: str, 2025-04-21T20:23:40.3191381Z eligible_experiments: frozenset[str] = frozenset(), 2025-04-21T20:23:40.3191994Z is_canary: bool = False, 2025-04-21T20:23:40.3192449Z ) -> str: 2025-04-21T20:23:40.3192860Z settings = parse_settings(rollout_state) 2025-04-21T20:23:40.3193443Z user_optins = parse_users(rollout_state) 2025-04-21T20:23:40.3193822Z 2025-04-21T20:23:40.3193988Z fleet_prefix = "" 2025-04-21T20:23:40.3194418Z prefixes = [] 2025-04-21T20:23:40.3195375Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-04-21T20:23:40.3196358Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-04-21T20:23:40.3197254Z log.info( 2025-04-21T20:23:40.3197957Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-04-21T20:23:40.3198740Z ) 2025-04-21T20:23:40.3199122Z continue 2025-04-21T20:23:40.3199375Z 2025-04-21T20:23:40.3199573Z if eligible_experiments: 2025-04-21T20:23:40.3200139Z if experiment_name not in eligible_experiments: 2025-04-21T20:23:40.3200782Z exp_list = ", ".join(eligible_experiments) 2025-04-21T20:23:40.3201351Z log.info( 2025-04-21T20:23:40.3202157Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-04-21T20:23:40.3203033Z ) 2025-04-21T20:23:40.3203422Z continue 2025-04-21T20:23:40.3203903Z elif not experiment_settings.default: 2025-04-21T20:23:40.3204438Z log.info( 2025-04-21T20:23:40.3205247Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-04-21T20:23:40.3206007Z ) 2025-04-21T20:23:40.3206390Z continue 2025-04-21T20:23:40.3206641Z 2025-04-21T20:23:40.3206917Z # Is any workflow_requestor opted out to this experiment? 2025-04-21T20:23:40.3207553Z opted_out_users = [ 2025-04-21T20:23:40.3208008Z requestor 2025-04-21T20:23:40.3208462Z for requestor in workflow_requestors 2025-04-21T20:23:40.3209143Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-04-21T20:23:40.3209814Z ] 2025-04-21T20:23:40.3210030Z 2025-04-21T20:23:40.3210213Z if opted_out_users: 2025-04-21T20:23:40.3210691Z log.info( 2025-04-21T20:23:40.3211324Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-04-21T20:23:40.3212047Z ) 2025-04-21T20:23:40.3212423Z continue 2025-04-21T20:23:40.3212672Z 2025-04-21T20:23:40.3212951Z # Is any workflow_requestor opted in to this experiment? 2025-04-21T20:23:40.3213573Z opted_in_users = [ 2025-04-21T20:23:40.3214022Z requestor 2025-04-21T20:23:40.3214473Z for requestor in workflow_requestors 2025-04-21T20:23:40.3215252Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-04-21T20:23:40.3215880Z ] 2025-04-21T20:23:40.3216090Z 2025-04-21T20:23:40.3216258Z enabled = False 2025-04-21T20:23:40.3216701Z if opted_in_users: 2025-04-21T20:23:40.3217138Z log.info( 2025-04-21T20:23:40.3217744Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-04-21T20:23:40.3218440Z ) 2025-04-21T20:23:40.3218986Z enabled = True 2025-04-21T20:23:40.3219274Z 2025-04-21T20:23:40.3219493Z elif experiment_settings.rollout_perc: 2025-04-21T20:23:40.3220343Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-04-21T20:23:40.3221306Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-04-21T20:23:40.3222182Z log.info( 2025-04-21T20:23:40.3223312Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-04-21T20:23:40.3224314Z ) 2025-04-21T20:23:40.3225012Z enabled = True 2025-04-21T20:23:40.3225348Z 2025-04-21T20:23:40.3225517Z if enabled: 2025-04-21T20:23:40.3225947Z label = experiment_name 2025-04-21T20:23:40.3226498Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-04-21T20:23:40.3227366Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-04-21T20:23:40.3228272Z # - If it's enabled, then we always list it's prefix first 2025-04-21T20:23:40.3229185Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-04-21T20:23:40.3229886Z if is_canary: 2025-04-21T20:23:40.3230381Z label += CANARY_FLEET_SUFFIX 2025-04-21T20:23:40.3230939Z fleet_prefix = label 2025-04-21T20:23:40.3231430Z else: 2025-04-21T20:23:40.3231855Z prefixes.append(label) 2025-04-21T20:23:40.3232208Z 2025-04-21T20:23:40.3232393Z if len(prefixes) > 1: 2025-04-21T20:23:40.3233050Z log.error( 2025-04-21T20:23:40.3234255Z 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-04-21T20:23:40.3235851Z ) 2025-04-21T20:23:40.3236268Z prefixes = prefixes[:1] 2025-04-21T20:23:40.3236599Z 2025-04-21T20:23:40.3236789Z # Fleet always comes first 2025-04-21T20:23:40.3237265Z if fleet_prefix: 2025-04-21T20:23:40.3237720Z prefixes.insert(0, fleet_prefix) 2025-04-21T20:23:40.3238085Z 2025-04-21T20:23:40.3238339Z return ".".join(prefixes) + "." if prefixes else "" 2025-04-21T20:23:40.3238770Z 2025-04-21T20:23:40.3238777Z 2025-04-21T20:23:40.3239227Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-04-21T20:23:40.3240020Z """ 2025-04-21T20:23:40.3240606Z Gets the first comment of the issue, which contains the desired rollout state. 2025-04-21T20:23:40.3241185Z 2025-04-21T20:23:40.3241595Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-04-21T20:23:40.3242328Z """ 2025-04-21T20:23:40.3242721Z gh = get_gh_client(github_token) 2025-04-21T20:23:40.3243268Z issue = get_issue(gh, repo, issue_num) 2025-04-21T20:23:40.3243915Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-04-21T20:23:40.3244375Z 2025-04-21T20:23:40.3244382Z 2025-04-21T20:23:40.3244914Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-04-21T20:23:40.3246075Z for _ in range(num_retries): 2025-04-21T20:23:40.3246756Z try: 2025-04-21T20:23:40.3247194Z req = Request(url=url, headers=headers) 2025-04-21T20:23:40.3247868Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-04-21T20:23:40.3248531Z return json.loads(content) 2025-04-21T20:23:40.3249071Z except Exception as e: 2025-04-21T20:23:40.3249613Z log.warning(f"Could not download {url}: {e}") 2025-04-21T20:23:40.3250019Z 2025-04-21T20:23:40.3250401Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-04-21T20:23:40.3251130Z return {} 2025-04-21T20:23:40.3251356Z 2025-04-21T20:23:40.3251362Z 2025-04-21T20:23:40.3251514Z @cache 2025-04-21T20:23:40.3252141Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-04-21T20:23:40.3253089Z """ 2025-04-21T20:23:40.3253484Z Dynamically get PR information 2025-04-21T20:23:40.3253982Z """ 2025-04-21T20:23:40.3254487Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-04-21T20:23:40.3255271Z headers = { 2025-04-21T20:23:40.3255730Z "Accept": "application/vnd.github.v3+json", 2025-04-21T20:23:40.3256343Z "Authorization": f"token {github_token}", 2025-04-21T20:23:40.3256883Z } 2025-04-21T20:23:40.3257318Z json_response: dict[str, Any] = download_json( 2025-04-21T20:23:40.3257931Z url=f"{github_api}/issues/{pr_number}", 2025-04-21T20:23:40.3258491Z headers=headers, 2025-04-21T20:23:40.3258918Z ) 2025-04-21T20:23:40.3259122Z 2025-04-21T20:23:40.3259301Z if not json_response: 2025-04-21T20:23:40.3259872Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-04-21T20:23:40.3260492Z return {} 2025-04-21T20:23:40.3260738Z 2025-04-21T20:23:40.3260922Z return json_response 2025-04-21T20:23:40.3261203Z 2025-04-21T20:23:40.3261210Z 2025-04-21T20:23:40.3261612Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-04-21T20:23:40.3262497Z """ 2025-04-21T20:23:40.3263034Z Dynamically get the latest list of labels from the pull request 2025-04-21T20:23:40.3263699Z """ 2025-04-21T20:23:40.3264176Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-04-21T20:23:40.3265084Z return { 2025-04-21T20:23:40.3265693Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-04-21T20:23:40.3266403Z } 2025-04-21T20:23:40.3266608Z 2025-04-21T20:23:40.3266615Z 2025-04-21T20:23:40.3266789Z def main() -> None: 2025-04-21T20:23:40.3267204Z args = parse_args() 2025-04-21T20:23:40.3267480Z 2025-04-21T20:23:40.3267697Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-04-21T20:23:40.3268093Z 2025-04-21T20:23:40.3268301Z # Check if the PR is opt-out 2025-04-21T20:23:40.3268795Z if args.pr_number: 2025-04-21T20:23:40.3269449Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-04-21T20:23:40.3270235Z if OPT_OUT_LABEL in labels: 2025-04-21T20:23:40.3270750Z log.info( 2025-04-21T20:23:40.3271446Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-04-21T20:23:40.3272232Z ) 2025-04-21T20:23:40.3272779Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-04-21T20:23:40.3273457Z sys.exit() 2025-04-21T20:23:40.3273714Z 2025-04-21T20:23:40.3273880Z try: 2025-04-21T20:23:40.3274311Z rollout_state = get_rollout_state_from_issue( 2025-04-21T20:23:40.3275252Z args.github_token, args.github_issue_repo, args.github_issue 2025-04-21T20:23:40.3275902Z ) 2025-04-21T20:23:40.3276121Z 2025-04-21T20:23:40.3276324Z username = get_potential_pr_author( 2025-04-21T20:23:40.3276879Z args.github_token, 2025-04-21T20:23:40.3277361Z args.github_repo, 2025-04-21T20:23:40.3277833Z args.github_actor, 2025-04-21T20:23:40.3278317Z args.github_ref_type, 2025-04-21T20:23:40.3278822Z args.github_branch, 2025-04-21T20:23:40.3279283Z ) 2025-04-21T20:23:40.3279488Z 2025-04-21T20:23:40.3279778Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-04-21T20:23:40.3280242Z 2025-04-21T20:23:40.3280461Z runner_label_prefix = get_runner_prefix( 2025-04-21T20:23:40.3281035Z rollout_state, 2025-04-21T20:23:40.3281521Z (args.github_issue_owner, username), 2025-04-21T20:23:40.3282074Z args.github_branch, 2025-04-21T20:23:40.3282568Z args.eligible_experiments, 2025-04-21T20:23:40.3283077Z is_canary, 2025-04-21T20:23:40.3283484Z ) 2025-04-21T20:23:40.3283688Z 2025-04-21T20:23:40.3283875Z except Exception as e: 2025-04-21T20:23:40.3284494Z log.error( 2025-04-21T20:23:40.3285295Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-04-21T20:23:40.3286077Z ) 2025-04-21T20:23:40.3286428Z 2025-04-21T20:23:40.3286776Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-04-21T20:23:40.3287350Z 2025-04-21T20:23:40.3287358Z 2025-04-21T20:23:40.3287536Z if __name__ == "__main__": 2025-04-21T20:23:40.3287990Z main() 2025-04-21T20:23:40.3288203Z 2025-04-21T20:23:40.3376177Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-04-21T20:23:40.3377111Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-04-21T20:23:40.3423860Z shell: /usr/bin/bash -e {0} 2025-04-21T20:23:40.3424349Z env: 2025-04-21T20:23:40.3425105Z GITHUB_TOKEN: *** 2025-04-21T20:23:40.3425529Z ISSUE_NUMBER: 5132 2025-04-21T20:23:40.3425972Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-04-21T20:23:40.3426475Z ISSUE_OWNER: 2025-04-21T20:23:40.3426879Z CHECK_EXPERIMENTS: 2025-04-21T20:23:40.3427283Z PR_NUMBER: 2025-04-21T20:23:40.3427654Z ##[endgroup] 2025-04-21T20:23:40.7097770Z Defaulting to user installation because normal site-packages is not writeable 2025-04-21T20:23:41.0751441Z Collecting urllib3==1.26.18 2025-04-21T20:23:41.1186591Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-04-21T20:23:41.1388168Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 4.1 MB/s eta 0:00:00 2025-04-21T20:23:41.1652153Z Collecting PyGithub==2.3.0 2025-04-21T20:23:41.1732154Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-04-21T20:23:41.2185921Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-04-21T20:23:41.2256723Z 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-04-21T20:23:41.2300883Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-04-21T20:23:41.2317138Z 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-04-21T20:23:41.2331567Z Requirement already satisfied: typing-extensions>=4.0.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (4.10.0) 2025-04-21T20:23:41.2624403Z Collecting Deprecated (from PyGithub==2.3.0) 2025-04-21T20:23:41.2698431Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl.metadata (5.7 kB) 2025-04-21T20:23:41.2925233Z 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-04-21T20:23:41.4087944Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-04-21T20:23:41.4158250Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-04-21T20:23:41.5234965Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-04-21T20:23:41.5307347Z 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-04-21T20:23:41.5530881Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-04-21T20:23:41.5599633Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-04-21T20:23:41.5882424Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-04-21T20:23:41.5976385Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 18.1 MB/s eta 0:00:00 2025-04-21T20:23:41.6048034Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-04-21T20:23:41.6156021Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 37.9 MB/s eta 0:00:00 2025-04-21T20:23:41.6227223Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (856 kB) 2025-04-21T20:23:41.6349519Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 81.9 MB/s eta 0:00:00 2025-04-21T20:23:41.6429396Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl (10.0 kB) 2025-04-21T20:23:41.6527995Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-04-21T20:23:41.6594543Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 93.0 MB/s eta 0:00:00 2025-04-21T20:23:41.6668391Z 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-04-21T20:23:41.6709504Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 31.4 MB/s eta 0:00:00 2025-04-21T20:23:41.6777922Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-04-21T20:23:41.6821618Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 40.0 MB/s eta 0:00:00 2025-04-21T20:23:41.9626181Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-04-21T20:23:42.4855186Z 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-04-21T20:23:42.5589467Z ##[group]Run curr_branch="ciflow/inductor/148893" 2025-04-21T20:23:42.5589839Z curr_branch="ciflow/inductor/148893" 2025-04-21T20:23:42.5590091Z curr_ref_type="tag" 2025-04-21T20:23:42.5590331Z echo "Current branch is '$curr_branch'" 2025-04-21T20:23:42.5590568Z  2025-04-21T20:23:42.5590751Z python3 runner_determinator.py \ 2025-04-21T20:23:42.5591024Z  --github-token "$GITHUB_TOKEN" \ 2025-04-21T20:23:42.5591288Z  --github-issue "$ISSUE_NUMBER" \ 2025-04-21T20:23:42.5591542Z  --github-branch "$curr_branch" \ 2025-04-21T20:23:42.5591803Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-04-21T20:23:42.5592085Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-04-21T20:23:42.5592357Z  --github-ref-type "$curr_ref_type" \ 2025-04-21T20:23:42.5592622Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-04-21T20:23:42.5592949Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-04-21T20:23:42.5593247Z  --pr-number "${PR_NUMBER}" 2025-04-21T20:23:42.5641370Z shell: /usr/bin/bash -e {0} 2025-04-21T20:23:42.5641597Z env: 2025-04-21T20:23:42.5642132Z GITHUB_TOKEN: *** 2025-04-21T20:23:42.5642324Z ISSUE_NUMBER: 5132 2025-04-21T20:23:42.5642529Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-04-21T20:23:42.5642754Z ISSUE_OWNER: 2025-04-21T20:23:42.5642926Z CHECK_EXPERIMENTS: 2025-04-21T20:23:42.5643105Z PR_NUMBER: 2025-04-21T20:23:42.5643273Z ##[endgroup] 2025-04-21T20:23:42.5707503Z Current branch is 'ciflow/inductor/148893' 2025-04-21T20:23:44.5267136Z INFO : Based on rollout percentage of 45%, enabling experiment lf. 2025-04-21T20:23:44.5268073Z INFO : Setting output: label-type='lf.' 2025-04-21T20:23:44.5572357Z Evaluate and set job outputs 2025-04-21T20:23:44.5578940Z Set output 'label-type' 2025-04-21T20:23:44.5581015Z Cleaning up orphan processes