2025-03-14T03:51:40.6492078Z Current runner version: '2.322.0' 2025-03-14T03:51:40.6516688Z ##[group]Operating System 2025-03-14T03:51:40.6517733Z Ubuntu 2025-03-14T03:51:40.6518260Z 24.04.2 2025-03-14T03:51:40.6518742Z LTS 2025-03-14T03:51:40.6519260Z ##[endgroup] 2025-03-14T03:51:40.6519794Z ##[group]Runner Image 2025-03-14T03:51:40.6520354Z Image: ubuntu-24.04 2025-03-14T03:51:40.6520953Z Version: 20250309.1.0 2025-03-14T03:51:40.6521984Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250309.1/images/ubuntu/Ubuntu2404-Readme.md 2025-03-14T03:51:40.6523393Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250309.1 2025-03-14T03:51:40.6524269Z ##[endgroup] 2025-03-14T03:51:40.6524813Z ##[group]Runner Image Provisioner 2025-03-14T03:51:40.6525439Z 2.0.422.1 2025-03-14T03:51:40.6525957Z ##[endgroup] 2025-03-14T03:51:40.6528310Z ##[group]GITHUB_TOKEN Permissions 2025-03-14T03:51:40.6530294Z Actions: read 2025-03-14T03:51:40.6531031Z Attestations: read 2025-03-14T03:51:40.6531794Z Checks: read 2025-03-14T03:51:40.6532258Z Contents: read 2025-03-14T03:51:40.6532845Z Deployments: read 2025-03-14T03:51:40.6533369Z Discussions: read 2025-03-14T03:51:40.6533863Z Issues: read 2025-03-14T03:51:40.6534441Z Metadata: read 2025-03-14T03:51:40.6534919Z Packages: read 2025-03-14T03:51:40.6535412Z Pages: read 2025-03-14T03:51:40.6535989Z PullRequests: read 2025-03-14T03:51:40.6536534Z RepositoryProjects: read 2025-03-14T03:51:40.6537351Z SecurityEvents: read 2025-03-14T03:51:40.6537901Z Statuses: read 2025-03-14T03:51:40.6538397Z ##[endgroup] 2025-03-14T03:51:40.6541294Z Secret source: Actions 2025-03-14T03:51:40.6542085Z Prepare workflow directory 2025-03-14T03:51:40.7020619Z Prepare all required actions 2025-03-14T03:51:40.7073810Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (aed0b7a742a2d7b7901790622829cbd2135049a4) 2025-03-14T03:51:40.7079003Z ##[group] Inputs 2025-03-14T03:51:40.7079645Z check_experiments: 2025-03-14T03:51:40.7080324Z triggering_actor: pytorchmergebot 2025-03-14T03:51:40.7081023Z issue_owner: 2025-03-14T03:51:40.7081542Z curr_branch: main 2025-03-14T03:51:40.7082032Z curr_ref_type: branch 2025-03-14T03:51:40.7082622Z issue_number: 5132 2025-03-14T03:51:40.7083159Z ##[endgroup] 2025-03-14T03:51:40.7083726Z Complete job name: get-label-type / runner-determinator 2025-03-14T03:51:40.7789740Z ##[group]Run cat < runner_determinator.py 2025-03-14T03:51:40.7791318Z cat < runner_determinator.py 2025-03-14T03:51:40.7791892Z # flake8: noqa: G004 2025-03-14T03:51:40.7792344Z  2025-03-14T03:51:40.7792981Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-03-14T03:51:40.7793937Z # must be kept in sync. You can do it easily by running the following command: 2025-03-14T03:51:40.7794765Z # python .github/scripts/update_runner_determinator.py 2025-03-14T03:51:40.7795379Z  2025-03-14T03:51:40.7795743Z """ 2025-03-14T03:51:40.7796389Z This runner determinator is used to determine which set of runners to run a 2025-03-14T03:51:40.7797455Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-03-14T03:51:40.7798435Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-03-14T03:51:40.7799297Z of which runners should be used to run which job. 2025-03-14T03:51:40.7799865Z  2025-03-14T03:51:40.7800460Z The configuration has two parts, the settings and a list of opted-in users, 2025-03-14T03:51:40.7801378Z separated by a line containing "---". If the line is not present, the 2025-03-14T03:51:40.7802286Z settings are considered to be empty with only the second part, the user 2025-03-14T03:51:40.7803000Z list, defined. 2025-03-14T03:51:40.7803425Z  2025-03-14T03:51:40.7803994Z The first part is a YAML block that defines the rollout settings. This can be 2025-03-14T03:51:40.7805278Z used to define any settings that are needed to determine which runners to use. 2025-03-14T03:51:40.7806151Z It's fields are defined by the RolloutSettings class below. 2025-03-14T03:51:40.7806773Z  2025-03-14T03:51:40.7807588Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-03-14T03:51:40.7808627Z The user list is also a comma separated list of additional features or 2025-03-14T03:51:40.7810007Z experiments which the user could be opted in to. 2025-03-14T03:51:40.7811027Z  2025-03-14T03:51:40.7811813Z The user list has the following rules: 2025-03-14T03:51:40.7812784Z  2025-03-14T03:51:40.7813778Z - Users are GitHub usernames, which must start with the @ prefix 2025-03-14T03:51:40.7815493Z - Each user is also a comma-separated list of features/experiments to enable 2025-03-14T03:51:40.7817008Z - A "#" prefix opts the user out of all experiments 2025-03-14T03:51:40.7818328Z  2025-03-14T03:51:40.7818993Z Example config: 2025-03-14T03:51:40.7819900Z  # A list of experiments that can be opted into. 2025-03-14T03:51:40.7821155Z  # This defines the behavior they'll induce when opted into. 2025-03-14T03:51:40.7822305Z  # Expected syntax is: 2025-03-14T03:51:40.7823587Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-03-14T03:51:40.7825395Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-03-14T03:51:40.7826861Z  2025-03-14T03:51:40.7827840Z  experiments: 2025-03-14T03:51:40.7828650Z  lf: 2025-03-14T03:51:40.7829380Z  rollout_percent: 25 2025-03-14T03:51:40.7830281Z  all_branches: false 2025-03-14T03:51:40.7831179Z  default: true 2025-03-14T03:51:40.7832009Z  --- 2025-03-14T03:51:40.7832687Z  2025-03-14T03:51:40.7833344Z  # Opt-ins: 2025-03-14T03:51:40.7834515Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-03-14T03:51:40.7836559Z  # and specifying experiments to enable in a comma-separated list. 2025-03-14T03:51:40.7838334Z  # To always opt out of an experiment, prefix it with a "-". 2025-03-14T03:51:40.7839543Z  # Experiments should be from the above list. 2025-03-14T03:51:40.7840512Z  2025-03-14T03:51:40.7841262Z  @User1,-lf,split_build 2025-03-14T03:51:40.7842153Z  @User2,lf 2025-03-14T03:51:40.7842950Z  @User3,split_build 2025-03-14T03:51:40.7843768Z """ 2025-03-14T03:51:40.7844426Z  2025-03-14T03:51:40.7845068Z import json 2025-03-14T03:51:40.7845798Z import logging 2025-03-14T03:51:40.7846595Z import os 2025-03-14T03:51:40.7847594Z import random 2025-03-14T03:51:40.7848381Z import re 2025-03-14T03:51:40.7849103Z import sys 2025-03-14T03:51:40.7849916Z from argparse import ArgumentParser 2025-03-14T03:51:40.7850961Z from collections.abc import Iterable 2025-03-14T03:51:40.7851988Z from functools import cache 2025-03-14T03:51:40.7852977Z from logging import LogRecord 2025-03-14T03:51:40.7853998Z from typing import Any, NamedTuple 2025-03-14T03:51:40.7855086Z from urllib.request import Request, urlopen 2025-03-14T03:51:40.7856165Z  2025-03-14T03:51:40.7856833Z import yaml 2025-03-14T03:51:40.7857810Z from github import Auth, Github 2025-03-14T03:51:40.7858791Z from github.Issue import Issue 2025-03-14T03:51:40.7859684Z  2025-03-14T03:51:40.7860336Z  2025-03-14T03:51:40.7861175Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-03-14T03:51:40.7862976Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-03-14T03:51:40.7864679Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-03-14T03:51:40.7866055Z  2025-03-14T03:51:40.7866905Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-03-14T03:51:40.7868177Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-03-14T03:51:40.7869189Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-03-14T03:51:40.7870266Z OPT_OUT_LABEL = "no-runner-experiments" 2025-03-14T03:51:40.7871268Z  2025-03-14T03:51:40.7872048Z SETTING_EXPERIMENTS = "experiments" 2025-03-14T03:51:40.7873011Z  2025-03-14T03:51:40.7873692Z LF_FLEET_EXPERIMENT = "lf" 2025-03-14T03:51:40.7874602Z CANARY_FLEET_SUFFIX = ".c" 2025-03-14T03:51:40.7875509Z  2025-03-14T03:51:40.7876176Z  2025-03-14T03:51:40.7876917Z class Experiment(NamedTuple): 2025-03-14T03:51:40.7878131Z  rollout_perc: float = ( 2025-03-14T03:51:40.7879454Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-03-14T03:51:40.7880863Z  ) 2025-03-14T03:51:40.7881637Z  all_branches: bool = ( 2025-03-14T03:51:40.7882923Z  False # If True, the experiment is also enabled on the exception branches 2025-03-14T03:51:40.7884230Z  ) 2025-03-14T03:51:40.7884984Z  default: bool = ( 2025-03-14T03:51:40.7886308Z  True # If True, the experiment is enabled by default for all queries 2025-03-14T03:51:40.7888157Z  ) 2025-03-14T03:51:40.7888829Z  2025-03-14T03:51:40.7889528Z  # Add more fields as needed 2025-03-14T03:51:40.7890429Z  2025-03-14T03:51:40.7891053Z  2025-03-14T03:51:40.7891723Z class Settings(NamedTuple): 2025-03-14T03:51:40.7892574Z  """ 2025-03-14T03:51:40.7893451Z  Settings for the experiments that can be opted into. 2025-03-14T03:51:40.7894498Z  """ 2025-03-14T03:51:40.7895194Z  2025-03-14T03:51:40.7895992Z  experiments: dict[str, Experiment] = {} 2025-03-14T03:51:40.7896917Z  2025-03-14T03:51:40.7897993Z  2025-03-14T03:51:40.7898837Z class ColorFormatter(logging.Formatter): 2025-03-14T03:51:40.7900079Z  """Color codes the log messages based on the log level""" 2025-03-14T03:51:40.7900933Z  2025-03-14T03:51:40.7901309Z  COLORS = { 2025-03-14T03:51:40.7901780Z  "WARNING": "\033[33m", # Yellow 2025-03-14T03:51:40.7902330Z  "ERROR": "\033[31m", # Red 2025-03-14T03:51:40.7902880Z  "CRITICAL": "\033[31m", # Red 2025-03-14T03:51:40.7903428Z  "INFO": "\033[0m", # Reset 2025-03-14T03:51:40.7903964Z  "DEBUG": "\033[0m", # Reset 2025-03-14T03:51:40.7904489Z  } 2025-03-14T03:51:40.7904858Z  2025-03-14T03:51:40.7905309Z  def format(self, record: LogRecord) -> str: 2025-03-14T03:51:40.7906105Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-03-14T03:51:40.7906903Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-03-14T03:51:40.7907771Z  return super().format(record) 2025-03-14T03:51:40.7908305Z  2025-03-14T03:51:40.7908656Z  2025-03-14T03:51:40.7909057Z handler = logging.StreamHandler() 2025-03-14T03:51:40.7909829Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-03-14T03:51:40.7910563Z  2025-03-14T03:51:40.7911049Z log = logging.getLogger(os.path.basename(__file__)) 2025-03-14T03:51:40.7911658Z log.addHandler(handler) 2025-03-14T03:51:40.7912159Z log.setLevel(logging.INFO) 2025-03-14T03:51:40.7912983Z  2025-03-14T03:51:40.7913343Z  2025-03-14T03:51:40.7913830Z def set_github_output(key: str, value: str) -> None: 2025-03-14T03:51:40.7914583Z  """ 2025-03-14T03:51:40.7915162Z  Defines outputs of the github action that invokes this script 2025-03-14T03:51:40.7915830Z  """ 2025-03-14T03:51:40.7916234Z  if not GITHUB_OUTPUT: 2025-03-14T03:51:40.7917571Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-03-14T03:51:40.7918684Z  log.warning( 2025-03-14T03:51:40.7919568Z  "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:40.7920494Z  ) 2025-03-14T03:51:40.7920973Z  print(f"::set-output name={key}::{value}") 2025-03-14T03:51:40.7921527Z  return 2025-03-14T03:51:40.7921947Z  2025-03-14T03:51:40.7922353Z  with open(GITHUB_OUTPUT, "a") as f: 2025-03-14T03:51:40.7922960Z  log.info(f"Setting output: {key}='{value}'") 2025-03-14T03:51:40.7923555Z  f.write(f"{key}={value}\n") 2025-03-14T03:51:40.7924060Z  2025-03-14T03:51:40.7924417Z  2025-03-14T03:51:40.7924944Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-03-14T03:51:40.7925610Z  return frozenset( 2025-03-14T03:51:40.7926281Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-03-14T03:51:40.7926961Z  ) 2025-03-14T03:51:40.7927543Z  2025-03-14T03:51:40.7927897Z  2025-03-14T03:51:40.7928271Z def parse_args() -> Any: 2025-03-14T03:51:40.7928886Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-03-14T03:51:40.7929778Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-03-14T03:51:40.7930583Z  parser.add_argument( 2025-03-14T03:51:40.7931099Z  "--github-issue-repo", 2025-03-14T03:51:40.7931617Z  type=str, 2025-03-14T03:51:40.7932075Z  required=False, 2025-03-14T03:51:40.7932752Z  default="pytorch/test-infra", 2025-03-14T03:51:40.7933350Z  help="GitHub repo to get the issue", 2025-03-14T03:51:40.7933894Z  ) 2025-03-14T03:51:40.7934303Z  parser.add_argument( 2025-03-14T03:51:40.7934830Z  "--github-repo", 2025-03-14T03:51:40.7935316Z  type=str, 2025-03-14T03:51:40.7935774Z  required=True, 2025-03-14T03:51:40.7936297Z  help="GitHub repo where CI is running", 2025-03-14T03:51:40.7936842Z  ) 2025-03-14T03:51:40.7937442Z  parser.add_argument( 2025-03-14T03:51:40.7938138Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-03-14T03:51:40.7938826Z  ) 2025-03-14T03:51:40.7939231Z  parser.add_argument( 2025-03-14T03:51:40.7939928Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-03-14T03:51:40.7940626Z  ) 2025-03-14T03:51:40.7941031Z  parser.add_argument( 2025-03-14T03:51:40.7941721Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-03-14T03:51:40.7942419Z  ) 2025-03-14T03:51:40.7942812Z  parser.add_argument( 2025-03-14T03:51:40.7943528Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-03-14T03:51:40.7944259Z  ) 2025-03-14T03:51:40.7944662Z  parser.add_argument( 2025-03-14T03:51:40.7945161Z  "--github-ref-type", 2025-03-14T03:51:40.7945655Z  type=str, 2025-03-14T03:51:40.7946254Z  required=True, 2025-03-14T03:51:40.7946814Z  help="Current GitHub ref type, branch or tag", 2025-03-14T03:51:40.7947580Z  ) 2025-03-14T03:51:40.7947990Z  parser.add_argument( 2025-03-14T03:51:40.7948504Z  "--eligible-experiments", 2025-03-14T03:51:40.7949080Z  type=_str_comma_separated_to_set, 2025-03-14T03:51:40.7949633Z  required=False, 2025-03-14T03:51:40.7950116Z  default="", 2025-03-14T03:51:40.7951015Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-03-14T03:51:40.7951932Z  ) 2025-03-14T03:51:40.7952330Z  parser.add_argument( 2025-03-14T03:51:40.7952825Z  "--pr-number", 2025-03-14T03:51:40.7953303Z  type=str, 2025-03-14T03:51:40.7953769Z  required=False, 2025-03-14T03:51:40.7954253Z  default="", 2025-03-14T03:51:40.7954797Z  help="the optional PR number where this is run", 2025-03-14T03:51:40.7955385Z  ) 2025-03-14T03:51:40.7955764Z  2025-03-14T03:51:40.7956162Z  return parser.parse_args() 2025-03-14T03:51:40.7956667Z  2025-03-14T03:51:40.7957021Z  2025-03-14T03:51:40.7957814Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-03-14T03:51:40.7958610Z  auth = Auth.Token(github_token) 2025-03-14T03:51:40.7959155Z  return Github(auth=auth) 2025-03-14T03:51:40.7959636Z  2025-03-14T03:51:40.7959985Z  2025-03-14T03:51:40.7960661Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-03-14T03:51:40.7961475Z  repo = gh.get_repo(repo) 2025-03-14T03:51:40.7962036Z  return repo.get_issue(number=issue_num) 2025-03-14T03:51:40.7962583Z  2025-03-14T03:51:40.7962935Z  2025-03-14T03:51:40.7963316Z def get_potential_pr_author( 2025-03-14T03:51:40.7964016Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-03-14T03:51:40.7964706Z ) -> str: 2025-03-14T03:51:40.7965396Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-03-14T03:51:40.7966234Z  # Fetch the actual username from the original PR. The PR number is 2025-03-14T03:51:40.7967008Z  # embedded in the tag name: ciflow// 2025-03-14T03:51:40.7967807Z  2025-03-14T03:51:40.7968215Z  gh = get_gh_client(github_token) 2025-03-14T03:51:40.7968762Z  2025-03-14T03:51:40.7969258Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-03-14T03:51:40.7969914Z  split_tag = ref_name.split("/") 2025-03-14T03:51:40.7970436Z  if ( 2025-03-14T03:51:40.7970876Z  len(split_tag) == 3 2025-03-14T03:51:40.7971417Z  and split_tag[0] == "ciflow" 2025-03-14T03:51:40.7971979Z  and split_tag[2].isnumeric() 2025-03-14T03:51:40.7972500Z  ): 2025-03-14T03:51:40.7972935Z  pr_number = split_tag[2] 2025-03-14T03:51:40.7973458Z  try: 2025-03-14T03:51:40.7973945Z  repository = gh.get_repo(repo) 2025-03-14T03:51:40.7974593Z  pull = repository.get_pull(number=int(pr_number)) 2025-03-14T03:51:40.7975234Z  except Exception as e: 2025-03-14T03:51:40.7975795Z  raise Exception( # noqa: TRY002 2025-03-14T03:51:40.7976487Z  f"issue with pull request {pr_number} from repo {repository}" 2025-03-14T03:51:40.7977330Z  ) from e 2025-03-14T03:51:40.7977959Z  return pull.user.login # type: ignore[no-any-return] 2025-03-14T03:51:40.7978838Z  # In all other cases, return the original input username 2025-03-14T03:51:40.7979448Z  return username 2025-03-14T03:51:40.7979886Z  2025-03-14T03:51:40.7980229Z  2025-03-14T03:51:40.7980713Z def is_exception_branch(branch: str) -> bool: 2025-03-14T03:51:40.7981265Z  """ 2025-03-14T03:51:40.7981962Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-03-14T03:51:40.7982746Z  """ 2025-03-14T03:51:40.7983329Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-03-14T03:51:40.7984000Z  2025-03-14T03:51:40.7984351Z  2025-03-14T03:51:40.7984747Z def load_yaml(yaml_text: str) -> Any: 2025-03-14T03:51:40.7985268Z  try: 2025-03-14T03:51:40.7985688Z  data = yaml.safe_load(yaml_text) 2025-03-14T03:51:40.7986221Z  return data 2025-03-14T03:51:40.7986691Z  except yaml.YAMLError: 2025-03-14T03:51:40.7987443Z  log.exception("Error loading YAML") 2025-03-14T03:51:40.7988003Z  raise 2025-03-14T03:51:40.7988460Z  2025-03-14T03:51:40.7988807Z  2025-03-14T03:51:40.7989440Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-03-14T03:51:40.7990183Z  """ 2025-03-14T03:51:40.7990834Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-03-14T03:51:40.7991579Z  2025-03-14T03:51:40.7992307Z  If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:40.7993544Z  and the text below is the list of opted in users. 2025-03-14T03:51:40.7994210Z  2025-03-14T03:51:40.7994804Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-03-14T03:51:40.7995527Z  """ 2025-03-14T03:51:40.7996019Z  rollout_state_parts = rollout_state.split("---") 2025-03-14T03:51:40.7996633Z  if len(rollout_state_parts) >= 2: 2025-03-14T03:51:40.7997611Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-03-14T03:51:40.7998249Z  else: 2025-03-14T03:51:40.7998670Z  return "", rollout_state 2025-03-14T03:51:40.7999162Z  2025-03-14T03:51:40.7999504Z  2025-03-14T03:51:40.7999925Z class UserOptins(dict[str, list[str]]): 2025-03-14T03:51:40.8000454Z  """ 2025-03-14T03:51:40.8001021Z  Dictionary of users with a list of features they have opted into 2025-03-14T03:51:40.8001676Z  """ 2025-03-14T03:51:40.8002039Z  2025-03-14T03:51:40.8002389Z  2025-03-14T03:51:40.8002943Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-03-14T03:51:40.8003620Z  """ 2025-03-14T03:51:40.8004358Z  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:40.8005183Z  2025-03-14T03:51:40.8005989Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-03-14T03:51:40.8006993Z  - Example line: "@User1,lf,split_build" 2025-03-14T03:51:40.8007807Z  - A "#" prefix indicates the user is opted out of all experiments 2025-03-14T03:51:40.8008456Z  2025-03-14T03:51:40.8008804Z  2025-03-14T03:51:40.8009150Z  """ 2025-03-14T03:51:40.8009556Z  optins = UserOptins() 2025-03-14T03:51:40.8010104Z  for user in user_optin_text.split("\n"): 2025-03-14T03:51:40.8010691Z  user = user.strip("\r\n\t -") 2025-03-14T03:51:40.8011275Z  if not user or not user.startswith("@"): 2025-03-14T03:51:40.8011991Z  # Not a valid user. Skip 2025-03-14T03:51:40.8012504Z  continue 2025-03-14T03:51:40.8012942Z  2025-03-14T03:51:40.8013298Z  if user: 2025-03-14T03:51:40.8013813Z  usr_name = user.split(",")[0].strip("@") 2025-03-14T03:51:40.8014539Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-03-14T03:51:40.8015174Z  2025-03-14T03:51:40.8015540Z  return optins 2025-03-14T03:51:40.8015968Z  2025-03-14T03:51:40.8016314Z  2025-03-14T03:51:40.8016829Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-03-14T03:51:40.8017555Z  """ 2025-03-14T03:51:40.8017999Z  Check if the experiment name is valid. 2025-03-14T03:51:40.8018536Z  A valid name: 2025-03-14T03:51:40.8019223Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-03-14T03:51:40.8020166Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-03-14T03:51:40.8020885Z  - Cannot contain spaces 2025-03-14T03:51:40.8021374Z  """ 2025-03-14T03:51:40.8021747Z  2025-03-14T03:51:40.8022222Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-03-14T03:51:40.8022954Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-03-14T03:51:40.8023559Z  2025-03-14T03:51:40.8023914Z  if valid: 2025-03-14T03:51:40.8024344Z  return True 2025-03-14T03:51:40.8024778Z  2025-03-14T03:51:40.8025138Z  log.error( 2025-03-14T03:51:40.8026573Z  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:40.8028159Z  ) 2025-03-14T03:51:40.8028544Z  return False 2025-03-14T03:51:40.8028964Z  2025-03-14T03:51:40.8029304Z  2025-03-14T03:51:40.8029946Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-03-14T03:51:40.8030589Z  """ 2025-03-14T03:51:40.8031215Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-03-14T03:51:40.8031935Z  """ 2025-03-14T03:51:40.8032314Z  try: 2025-03-14T03:51:40.8032710Z  if settings_text: 2025-03-14T03:51:40.8033472Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-03-14T03:51:40.8034266Z  # for easy reading 2025-03-14T03:51:40.8035091Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-03-14T03:51:40.8035991Z  # the backtick character in shell commands. 2025-03-14T03:51:40.8036627Z  backtick = chr(96) # backtick character 2025-03-14T03:51:40.8037419Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-03-14T03:51:40.8038121Z  settings = load_yaml(settings_text) 2025-03-14T03:51:40.8038644Z  2025-03-14T03:51:40.8039245Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-03-14T03:51:40.8039992Z  experiments = {} 2025-03-14T03:51:40.8040470Z  2025-03-14T03:51:40.8041052Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-03-14T03:51:40.8041830Z  if not is_valid_experiment_name(exp_name): 2025-03-14T03:51:40.8042928Z  # 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:40.8044079Z  continue 2025-03-14T03:51:40.8044542Z  2025-03-14T03:51:40.8044929Z  valid_settings = {} 2025-03-14T03:51:40.8045488Z  for setting in exp_settings: 2025-03-14T03:51:40.8046082Z  if setting not in Experiment._fields: 2025-03-14T03:51:40.8046659Z  log.warning( 2025-03-14T03:51:40.8047487Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-03-14T03:51:40.8048401Z  ) 2025-03-14T03:51:40.8049077Z  else: 2025-03-14T03:51:40.8049961Z  valid_settings[setting] = exp_settings[setting] 2025-03-14T03:51:40.8050555Z  2025-03-14T03:51:40.8051050Z  experiments[exp_name] = Experiment(**valid_settings) 2025-03-14T03:51:40.8051712Z  return Settings(experiments) 2025-03-14T03:51:40.8052235Z  2025-03-14T03:51:40.8052607Z  except Exception: 2025-03-14T03:51:40.8053150Z  log.exception("Failed to parse settings") 2025-03-14T03:51:40.8053705Z  2025-03-14T03:51:40.8054070Z  return Settings() 2025-03-14T03:51:40.8054525Z  2025-03-14T03:51:40.8054873Z  2025-03-14T03:51:40.8055348Z def parse_settings(rollout_state: str) -> Settings: 2025-03-14T03:51:40.8055939Z  """ 2025-03-14T03:51:40.8056417Z  Parse settings, if any, from the rollout state. 2025-03-14T03:51:40.8056986Z  2025-03-14T03:51:40.8057670Z  If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:40.8058461Z  and the text below is the list of opted in users. 2025-03-14T03:51:40.8059039Z  2025-03-14T03:51:40.8059653Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-03-14T03:51:40.8060383Z  """ 2025-03-14T03:51:40.8060979Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:40.8061913Z  return parse_settings_from_text(settings_text) 2025-03-14T03:51:40.8062487Z  2025-03-14T03:51:40.8062832Z  2025-03-14T03:51:40.8063300Z def parse_users(rollout_state: str) -> UserOptins: 2025-03-14T03:51:40.8063885Z  """ 2025-03-14T03:51:40.8064318Z  Parse users from the rollout state. 2025-03-14T03:51:40.8064844Z  2025-03-14T03:51:40.8065190Z  """ 2025-03-14T03:51:40.8065761Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:40.8066530Z  return parse_user_opt_in_from_text(users_text) 2025-03-14T03:51:40.8067090Z  2025-03-14T03:51:40.8067545Z  2025-03-14T03:51:40.8068186Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:40.8068937Z  """ 2025-03-14T03:51:40.8069397Z  Check if a user is opted into an experiment 2025-03-14T03:51:40.8069949Z  """ 2025-03-14T03:51:40.8070448Z  return experiment_name in user_optins.get(user, []) 2025-03-14T03:51:40.8071030Z  2025-03-14T03:51:40.8071378Z  2025-03-14T03:51:40.8072002Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:40.8072757Z  """ 2025-03-14T03:51:40.8073252Z  Check if a user explicitly opted out of an experiment 2025-03-14T03:51:40.8073841Z  """ 2025-03-14T03:51:40.8074380Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-03-14T03:51:40.8075096Z  experiment_optout = "-" + experiment_name 2025-03-14T03:51:40.8075885Z  if experiment_optout not in user_optins.get(user, []): 2025-03-14T03:51:40.8076490Z  return False 2025-03-14T03:51:40.8076922Z  2025-03-14T03:51:40.8077503Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-03-14T03:51:40.8078120Z  log.warning( 2025-03-14T03:51:40.8078945Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-03-14T03:51:40.8079798Z  ) 2025-03-14T03:51:40.8080183Z  2025-03-14T03:51:40.8080575Z  return True 2025-03-14T03:51:40.8080986Z  2025-03-14T03:51:40.8081325Z  2025-03-14T03:51:40.8081698Z def get_runner_prefix( 2025-03-14T03:51:40.8082178Z  rollout_state: str, 2025-03-14T03:51:40.8082694Z  workflow_requestors: Iterable[str], 2025-03-14T03:51:40.8083233Z  branch: str, 2025-03-14T03:51:40.8083792Z  eligible_experiments: frozenset[str] = frozenset(), 2025-03-14T03:51:40.8084403Z  is_canary: bool = False, 2025-03-14T03:51:40.8084882Z ) -> str: 2025-03-14T03:51:40.8085336Z  settings = parse_settings(rollout_state) 2025-03-14T03:51:40.8085945Z  user_optins = parse_users(rollout_state) 2025-03-14T03:51:40.8086478Z  2025-03-14T03:51:40.8086840Z  fleet_prefix = "" 2025-03-14T03:51:40.8087418Z  prefixes = [] 2025-03-14T03:51:40.8088103Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-03-14T03:51:40.8089060Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-03-14T03:51:40.8089777Z  log.info( 2025-03-14T03:51:40.8090497Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-03-14T03:51:40.8091262Z  ) 2025-03-14T03:51:40.8091678Z  continue 2025-03-14T03:51:40.8092109Z  2025-03-14T03:51:40.8092678Z  if eligible_experiments: 2025-03-14T03:51:40.8093768Z  if experiment_name not in eligible_experiments: 2025-03-14T03:51:40.8094578Z  exp_list = ", ".join(eligible_experiments) 2025-03-14T03:51:40.8095158Z  log.info( 2025-03-14T03:51:40.8095983Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-03-14T03:51:40.8096815Z  ) 2025-03-14T03:51:40.8097372Z  continue 2025-03-14T03:51:40.8097906Z  elif not experiment_settings.default: 2025-03-14T03:51:40.8098453Z  log.info( 2025-03-14T03:51:40.8099160Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-03-14T03:51:40.8099900Z  ) 2025-03-14T03:51:40.8100318Z  continue 2025-03-14T03:51:40.8100751Z  2025-03-14T03:51:40.8101237Z  # Is any workflow_requestor opted out to this experiment? 2025-03-14T03:51:40.8101866Z  opted_out_users = [ 2025-03-14T03:51:40.8102360Z  requestor 2025-03-14T03:51:40.8102869Z  for requestor in workflow_requestors 2025-03-14T03:51:40.8103558Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-03-14T03:51:40.8104196Z  ] 2025-03-14T03:51:40.8104575Z  2025-03-14T03:51:40.8104955Z  if opted_out_users: 2025-03-14T03:51:40.8105441Z  log.info( 2025-03-14T03:51:40.8106112Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-03-14T03:51:40.8106935Z  ) 2025-03-14T03:51:40.8107451Z  continue 2025-03-14T03:51:40.8107882Z  2025-03-14T03:51:40.8108366Z  # Is any workflow_requestor opted in to this experiment? 2025-03-14T03:51:40.8108983Z  opted_in_users = [ 2025-03-14T03:51:40.8109473Z  requestor 2025-03-14T03:51:40.8109982Z  for requestor in workflow_requestors 2025-03-14T03:51:40.8110866Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-03-14T03:51:40.8111755Z  ] 2025-03-14T03:51:40.8112140Z  2025-03-14T03:51:40.8112515Z  enabled = False 2025-03-14T03:51:40.8112989Z  if opted_in_users: 2025-03-14T03:51:40.8113466Z  log.info( 2025-03-14T03:51:40.8114122Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-03-14T03:51:40.8114804Z  ) 2025-03-14T03:51:40.8115234Z  enabled = True 2025-03-14T03:51:40.8115695Z  2025-03-14T03:51:40.8116111Z  elif experiment_settings.rollout_perc: 2025-03-14T03:51:40.8116957Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-03-14T03:51:40.8118031Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-03-14T03:51:40.8118701Z  log.info( 2025-03-14T03:51:40.8119593Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-03-14T03:51:40.8120491Z  ) 2025-03-14T03:51:40.8120949Z  enabled = True 2025-03-14T03:51:40.8121433Z  2025-03-14T03:51:40.8121794Z  if enabled: 2025-03-14T03:51:40.8122271Z  label = experiment_name 2025-03-14T03:51:40.8122859Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-03-14T03:51:40.8123698Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-03-14T03:51:40.8124727Z  # - If it's enabled, then we always list it's prefix first 2025-03-14T03:51:40.8125504Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-03-14T03:51:40.8126170Z  if is_canary: 2025-03-14T03:51:40.8126701Z  label += CANARY_FLEET_SUFFIX 2025-03-14T03:51:40.8127365Z  fleet_prefix = label 2025-03-14T03:51:40.8127881Z  else: 2025-03-14T03:51:40.8128357Z  prefixes.append(label) 2025-03-14T03:51:40.8128870Z  2025-03-14T03:51:40.8129243Z  if len(prefixes) > 1: 2025-03-14T03:51:40.8129718Z  log.error( 2025-03-14T03:51:40.8130768Z  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:40.8131841Z  ) 2025-03-14T03:51:40.8132256Z  prefixes = prefixes[:1] 2025-03-14T03:51:40.8132751Z  2025-03-14T03:51:40.8133130Z  # Fleet always comes first 2025-03-14T03:51:40.8133634Z  if fleet_prefix: 2025-03-14T03:51:40.8134135Z  prefixes.insert(0, fleet_prefix) 2025-03-14T03:51:40.8134650Z  2025-03-14T03:51:40.8135160Z  return ".".join(prefixes) + "." if prefixes else "" 2025-03-14T03:51:40.8135739Z  2025-03-14T03:51:40.8136089Z  2025-03-14T03:51:40.8136738Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-03-14T03:51:40.8137631Z  """ 2025-03-14T03:51:40.8138243Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-03-14T03:51:40.8139065Z  2025-03-14T03:51:40.8139648Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-03-14T03:51:40.8140354Z  """ 2025-03-14T03:51:40.8140775Z  gh = get_gh_client(github_token) 2025-03-14T03:51:40.8141335Z  issue = get_issue(gh, repo, issue_num) 2025-03-14T03:51:40.8141996Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-03-14T03:51:40.8142602Z  2025-03-14T03:51:40.8142955Z  2025-03-14T03:51:40.8143565Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-03-14T03:51:40.8144338Z  for _ in range(num_retries): 2025-03-14T03:51:40.8144831Z  try: 2025-03-14T03:51:40.8145296Z  req = Request(url=url, headers=headers) 2025-03-14T03:51:40.8145977Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-03-14T03:51:40.8146643Z  return json.loads(content) 2025-03-14T03:51:40.8147285Z  except Exception as e: 2025-03-14T03:51:40.8147875Z  log.warning(f"Could not download {url}: {e}") 2025-03-14T03:51:40.8148440Z  2025-03-14T03:51:40.8149028Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-03-14T03:51:40.8149737Z  return {} 2025-03-14T03:51:40.8150140Z  2025-03-14T03:51:40.8150486Z  2025-03-14T03:51:40.8150825Z @cache 2025-03-14T03:51:40.8151480Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-03-14T03:51:40.8152222Z  """ 2025-03-14T03:51:40.8152639Z  Dynamically get PR information 2025-03-14T03:51:40.8153141Z  """ 2025-03-14T03:51:40.8153664Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-03-14T03:51:40.8154304Z  headers = { 2025-03-14T03:51:40.8154982Z  "Accept": "application/vnd.github.v3+json", 2025-03-14T03:51:40.8155921Z  "Authorization": f"token {github_token}", 2025-03-14T03:51:40.8156672Z  } 2025-03-14T03:51:40.8157392Z  json_response: dict[str, Any] = download_json( 2025-03-14T03:51:40.8158061Z  url=f"{github_api}/issues/{pr_number}", 2025-03-14T03:51:40.8158621Z  headers=headers, 2025-03-14T03:51:40.8159085Z  ) 2025-03-14T03:51:40.8159452Z  2025-03-14T03:51:40.8159821Z  if not json_response: 2025-03-14T03:51:40.8160438Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-03-14T03:51:40.8161070Z  return {} 2025-03-14T03:51:40.8161494Z  2025-03-14T03:51:40.8161865Z  return json_response 2025-03-14T03:51:40.8162365Z  2025-03-14T03:51:40.8162796Z  2025-03-14T03:51:40.8163406Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-03-14T03:51:40.8164126Z  """ 2025-03-14T03:51:40.8164684Z  Dynamically get the latest list of labels from the pull request 2025-03-14T03:51:40.8165386Z  """ 2025-03-14T03:51:40.8165898Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-03-14T03:51:40.8166515Z  return { 2025-03-14T03:51:40.8167130Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-03-14T03:51:40.8167965Z  } 2025-03-14T03:51:40.8168337Z  2025-03-14T03:51:40.8168677Z  2025-03-14T03:51:40.8169048Z def main() -> None: 2025-03-14T03:51:40.8169508Z  args = parse_args() 2025-03-14T03:51:40.8169968Z  2025-03-14T03:51:40.8170398Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-03-14T03:51:40.8171071Z  2025-03-14T03:51:40.8171456Z  # Check if the PR is opt-out 2025-03-14T03:51:40.8171974Z  if args.pr_number: 2025-03-14T03:51:40.8172670Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-03-14T03:51:40.8173436Z  if OPT_OUT_LABEL in labels: 2025-03-14T03:51:40.8173954Z  log.info( 2025-03-14T03:51:40.8174680Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-03-14T03:51:40.8175437Z  ) 2025-03-14T03:51:40.8176028Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:40.8176851Z  sys.exit() 2025-03-14T03:51:40.8177750Z  2025-03-14T03:51:40.8178265Z  try: 2025-03-14T03:51:40.8178876Z  rollout_state = get_rollout_state_from_issue( 2025-03-14T03:51:40.8179974Z  args.github_token, args.github_issue_repo, args.github_issue 2025-03-14T03:51:40.8180656Z  ) 2025-03-14T03:51:40.8181045Z  2025-03-14T03:51:40.8181700Z  username = get_potential_pr_author( 2025-03-14T03:51:40.8182625Z  args.github_token, 2025-03-14T03:51:40.8183226Z  args.github_repo, 2025-03-14T03:51:40.8183731Z  args.github_actor, 2025-03-14T03:51:40.8184256Z  args.github_ref_type, 2025-03-14T03:51:40.8184784Z  args.github_branch, 2025-03-14T03:51:40.8185336Z  ) 2025-03-14T03:51:40.8185722Z  2025-03-14T03:51:40.8186202Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-03-14T03:51:40.8186800Z  2025-03-14T03:51:40.8187356Z  runner_label_prefix = get_runner_prefix( 2025-03-14T03:51:40.8187920Z  rollout_state, 2025-03-14T03:51:40.8188452Z  (args.github_issue_owner, username), 2025-03-14T03:51:40.8189009Z  args.github_branch, 2025-03-14T03:51:40.8189543Z  args.eligible_experiments, 2025-03-14T03:51:40.8190065Z  is_canary, 2025-03-14T03:51:40.8190670Z  ) 2025-03-14T03:51:40.8191059Z  2025-03-14T03:51:40.8191434Z  except Exception as e: 2025-03-14T03:51:40.8191916Z  log.error( 2025-03-14T03:51:40.8192634Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-03-14T03:51:40.8253835Z  ) 2025-03-14T03:51:40.8254327Z  2025-03-14T03:51:40.8254970Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:40.8255751Z  2025-03-14T03:51:40.8256195Z  2025-03-14T03:51:40.8256581Z if __name__ == "__main__": 2025-03-14T03:51:40.8257073Z  main() 2025-03-14T03:51:40.8257580Z  2025-03-14T03:51:40.8257929Z EOF 2025-03-14T03:51:40.8258291Z  2025-03-14T03:51:40.8258676Z cat runner_determinator.py 2025-03-14T03:51:40.8544275Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:40.8545046Z env: 2025-03-14T03:51:40.8545699Z GITHUB_TOKEN: *** 2025-03-14T03:51:40.8546108Z ISSUE_NUMBER: 5132 2025-03-14T03:51:40.8546538Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:40.8547035Z ISSUE_OWNER: 2025-03-14T03:51:40.8547596Z CHECK_EXPERIMENTS: 2025-03-14T03:51:40.8548003Z PR_NUMBER: 2025-03-14T03:51:40.8548368Z ##[endgroup] 2025-03-14T03:51:40.8781598Z # flake8: noqa: G004 2025-03-14T03:51:40.8781926Z 2025-03-14T03:51:40.8782350Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-03-14T03:51:40.8783272Z # must be kept in sync. You can do it easily by running the following command: 2025-03-14T03:51:40.8784025Z # python .github/scripts/update_runner_determinator.py 2025-03-14T03:51:40.8784658Z 2025-03-14T03:51:40.8784815Z """ 2025-03-14T03:51:40.8785377Z This runner determinator is used to determine which set of runners to run a 2025-03-14T03:51:40.8786193Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-03-14T03:51:40.8787075Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-03-14T03:51:40.8788545Z of which runners should be used to run which job. 2025-03-14T03:51:40.8788934Z 2025-03-14T03:51:40.8789307Z The configuration has two parts, the settings and a list of opted-in users, 2025-03-14T03:51:40.8790172Z separated by a line containing "---". If the line is not present, the 2025-03-14T03:51:40.8791001Z settings are considered to be empty with only the second part, the user 2025-03-14T03:51:40.8791653Z list, defined. 2025-03-14T03:51:40.8791872Z 2025-03-14T03:51:40.8792210Z The first part is a YAML block that defines the rollout settings. This can be 2025-03-14T03:51:40.8793092Z used to define any settings that are needed to determine which runners to use. 2025-03-14T03:51:40.8793875Z It's fields are defined by the RolloutSettings class below. 2025-03-14T03:51:40.8794297Z 2025-03-14T03:51:40.8794652Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-03-14T03:51:40.8795493Z The user list is also a comma separated list of additional features or 2025-03-14T03:51:40.8796189Z experiments which the user could be opted in to. 2025-03-14T03:51:40.8796574Z 2025-03-14T03:51:40.8796759Z The user list has the following rules: 2025-03-14T03:51:40.8797098Z 2025-03-14T03:51:40.8797568Z - Users are GitHub usernames, which must start with the @ prefix 2025-03-14T03:51:40.8798398Z - Each user is also a comma-separated list of features/experiments to enable 2025-03-14T03:51:40.8799130Z - A "#" prefix opts the user out of all experiments 2025-03-14T03:51:40.8799509Z 2025-03-14T03:51:40.8799674Z Example config: 2025-03-14T03:51:40.8800099Z # A list of experiments that can be opted into. 2025-03-14T03:51:40.8800746Z # This defines the behavior they'll induce when opted into. 2025-03-14T03:51:40.8801336Z # Expected syntax is: 2025-03-14T03:51:40.8801946Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-03-14T03:51:40.8803036Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-03-14T03:51:40.8803629Z 2025-03-14T03:51:40.8803788Z experiments: 2025-03-14T03:51:40.8804150Z lf: 2025-03-14T03:51:40.8804503Z rollout_percent: 25 2025-03-14T03:51:40.8804944Z all_branches: false 2025-03-14T03:51:40.8805360Z default: true 2025-03-14T03:51:40.8805739Z --- 2025-03-14T03:51:40.8805934Z 2025-03-14T03:51:40.8806085Z # Opt-ins: 2025-03-14T03:51:40.8806625Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-03-14T03:51:40.8807710Z # and specifying experiments to enable in a comma-separated list. 2025-03-14T03:51:40.8808453Z # To always opt out of an experiment, prefix it with a "-". 2025-03-14T03:51:40.8809065Z # Experiments should be from the above list. 2025-03-14T03:51:40.8809434Z 2025-03-14T03:51:40.8809605Z @User1,-lf,split_build 2025-03-14T03:51:40.8810034Z @User2,lf 2025-03-14T03:51:40.8810404Z @User3,split_build 2025-03-14T03:51:40.8810793Z """ 2025-03-14T03:51:40.8810976Z 2025-03-14T03:51:40.8811126Z import json 2025-03-14T03:51:40.8811477Z import logging 2025-03-14T03:51:40.8811831Z import os 2025-03-14T03:51:40.8812218Z import random 2025-03-14T03:51:40.8812575Z import re 2025-03-14T03:51:40.8812903Z import sys 2025-03-14T03:51:40.8813276Z from argparse import ArgumentParser 2025-03-14T03:51:40.8813781Z from collections.abc import Iterable 2025-03-14T03:51:40.8814271Z from functools import cache 2025-03-14T03:51:40.8814717Z from logging import LogRecord 2025-03-14T03:51:40.8815176Z from typing import Any, NamedTuple 2025-03-14T03:51:40.8815685Z from urllib.request import Request, urlopen 2025-03-14T03:51:40.8816184Z 2025-03-14T03:51:40.8816338Z import yaml 2025-03-14T03:51:40.8816709Z from github import Auth, Github 2025-03-14T03:51:40.8817383Z from github.Issue import Issue 2025-03-14T03:51:40.8817703Z 2025-03-14T03:51:40.8817709Z 2025-03-14T03:51:40.8817925Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-03-14T03:51:40.8818587Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-03-14T03:51:40.8819411Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-03-14T03:51:40.8819951Z 2025-03-14T03:51:40.8820166Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-03-14T03:51:40.8820710Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-03-14T03:51:40.8821196Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-03-14T03:51:40.8821713Z OPT_OUT_LABEL = "no-runner-experiments" 2025-03-14T03:51:40.8822053Z 2025-03-14T03:51:40.8822237Z SETTING_EXPERIMENTS = "experiments" 2025-03-14T03:51:40.8822557Z 2025-03-14T03:51:40.8822739Z LF_FLEET_EXPERIMENT = "lf" 2025-03-14T03:51:40.8823177Z CANARY_FLEET_SUFFIX = ".c" 2025-03-14T03:51:40.8823442Z 2025-03-14T03:51:40.8823448Z 2025-03-14T03:51:40.8823631Z class Experiment(NamedTuple): 2025-03-14T03:51:40.8824092Z rollout_perc: float = ( 2025-03-14T03:51:40.8824696Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-03-14T03:51:40.8825341Z ) 2025-03-14T03:51:40.8825692Z all_branches: bool = ( 2025-03-14T03:51:40.8826288Z False # If True, the experiment is also enabled on the exception branches 2025-03-14T03:51:40.8826922Z ) 2025-03-14T03:51:40.8827409Z default: bool = ( 2025-03-14T03:51:40.8827971Z True # If True, the experiment is enabled by default for all queries 2025-03-14T03:51:40.8828580Z ) 2025-03-14T03:51:40.8828766Z 2025-03-14T03:51:40.8828941Z # Add more fields as needed 2025-03-14T03:51:40.8829236Z 2025-03-14T03:51:40.8829242Z 2025-03-14T03:51:40.8829420Z class Settings(NamedTuple): 2025-03-14T03:51:40.8829841Z """ 2025-03-14T03:51:40.8830270Z Settings for the experiments that can be opted into. 2025-03-14T03:51:40.8830813Z """ 2025-03-14T03:51:40.8830996Z 2025-03-14T03:51:40.8831196Z experiments: dict[str, Experiment] = {} 2025-03-14T03:51:40.8831552Z 2025-03-14T03:51:40.8831559Z 2025-03-14T03:51:40.8831893Z class ColorFormatter(logging.Formatter): 2025-03-14T03:51:40.8832500Z """Color codes the log messages based on the log level""" 2025-03-14T03:51:40.8832913Z 2025-03-14T03:51:40.8833063Z COLORS = { 2025-03-14T03:51:40.8833436Z "WARNING": "\033[33m", # Yellow 2025-03-14T03:51:40.8833921Z "ERROR": "\033[31m", # Red 2025-03-14T03:51:40.8834389Z "CRITICAL": "\033[31m", # Red 2025-03-14T03:51:40.8834862Z "INFO": "\033[0m", # Reset 2025-03-14T03:51:40.8835316Z "DEBUG": "\033[0m", # Reset 2025-03-14T03:51:40.8835761Z } 2025-03-14T03:51:40.8835951Z 2025-03-14T03:51:40.8836154Z def format(self, record: LogRecord) -> str: 2025-03-14T03:51:40.8836875Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-03-14T03:51:40.8837781Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-03-14T03:51:40.8838327Z return super().format(record) 2025-03-14T03:51:40.8838657Z 2025-03-14T03:51:40.8838663Z 2025-03-14T03:51:40.8838849Z handler = logging.StreamHandler() 2025-03-14T03:51:40.8839516Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-03-14T03:51:40.8840045Z 2025-03-14T03:51:40.8840276Z log = logging.getLogger(os.path.basename(__file__)) 2025-03-14T03:51:40.8840835Z log.addHandler(handler) 2025-03-14T03:51:40.8841255Z log.setLevel(logging.INFO) 2025-03-14T03:51:40.8841526Z 2025-03-14T03:51:40.8841532Z 2025-03-14T03:51:40.8841764Z def set_github_output(key: str, value: str) -> None: 2025-03-14T03:51:40.8842297Z """ 2025-03-14T03:51:40.8842777Z Defines outputs of the github action that invokes this script 2025-03-14T03:51:40.8843370Z """ 2025-03-14T03:51:40.8843856Z if not GITHUB_OUTPUT: 2025-03-14T03:51:40.8844867Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-03-14T03:51:40.8845929Z log.warning( 2025-03-14T03:51:40.8846733Z "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:40.8847779Z ) 2025-03-14T03:51:40.8857920Z print(f"::set-output name={key}::{value}") 2025-03-14T03:51:40.8858536Z return 2025-03-14T03:51:40.8858761Z 2025-03-14T03:51:40.8858957Z with open(GITHUB_OUTPUT, "a") as f: 2025-03-14T03:51:40.8859522Z log.info(f"Setting output: {key}='{value}'") 2025-03-14T03:51:40.8860077Z f.write(f"{key}={value}\n") 2025-03-14T03:51:40.8860387Z 2025-03-14T03:51:40.8860393Z 2025-03-14T03:51:40.8860689Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-03-14T03:51:40.8861301Z return frozenset( 2025-03-14T03:51:40.8861907Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-03-14T03:51:40.8862562Z ) 2025-03-14T03:51:40.8862749Z 2025-03-14T03:51:40.8862755Z 2025-03-14T03:51:40.8862938Z def parse_args() -> Any: 2025-03-14T03:51:40.8863478Z parser = ArgumentParser("Get dynamic rollout settings") 2025-03-14T03:51:40.8864305Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-03-14T03:51:40.8865039Z parser.add_argument( 2025-03-14T03:51:40.8865472Z "--github-issue-repo", 2025-03-14T03:51:40.8865908Z type=str, 2025-03-14T03:51:40.8866287Z required=False, 2025-03-14T03:51:40.8866711Z default="pytorch/test-infra", 2025-03-14T03:51:40.8867400Z help="GitHub repo to get the issue", 2025-03-14T03:51:40.8867906Z ) 2025-03-14T03:51:40.8868268Z parser.add_argument( 2025-03-14T03:51:40.8868686Z "--github-repo", 2025-03-14T03:51:40.8869095Z type=str, 2025-03-14T03:51:40.8869473Z required=True, 2025-03-14T03:51:40.8869909Z help="GitHub repo where CI is running", 2025-03-14T03:51:40.8870397Z ) 2025-03-14T03:51:40.8870741Z parser.add_argument( 2025-03-14T03:51:40.8871325Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-03-14T03:51:40.8872156Z ) 2025-03-14T03:51:40.8872521Z parser.add_argument( 2025-03-14T03:51:40.8873112Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-03-14T03:51:40.8873750Z ) 2025-03-14T03:51:40.8874097Z parser.add_argument( 2025-03-14T03:51:40.8874703Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-03-14T03:51:40.8875366Z ) 2025-03-14T03:51:40.8875714Z parser.add_argument( 2025-03-14T03:51:40.8876325Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-03-14T03:51:40.8876999Z ) 2025-03-14T03:51:40.8877516Z parser.add_argument( 2025-03-14T03:51:40.8877956Z "--github-ref-type", 2025-03-14T03:51:40.8878382Z type=str, 2025-03-14T03:51:40.8878754Z required=True, 2025-03-14T03:51:40.8879214Z help="Current GitHub ref type, branch or tag", 2025-03-14T03:51:40.8879734Z ) 2025-03-14T03:51:40.8880085Z parser.add_argument( 2025-03-14T03:51:40.8880534Z "--eligible-experiments", 2025-03-14T03:51:40.8881020Z type=_str_comma_separated_to_set, 2025-03-14T03:51:40.8881520Z required=False, 2025-03-14T03:51:40.8881921Z default="", 2025-03-14T03:51:40.8882726Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-03-14T03:51:40.8883603Z ) 2025-03-14T03:51:40.8883949Z parser.add_argument( 2025-03-14T03:51:40.8884366Z "--pr-number", 2025-03-14T03:51:40.8884758Z type=str, 2025-03-14T03:51:40.8885139Z required=False, 2025-03-14T03:51:40.8885536Z default="", 2025-03-14T03:51:40.8886107Z help="the optional PR number where this is run", 2025-03-14T03:51:40.8886637Z ) 2025-03-14T03:51:40.8886830Z 2025-03-14T03:51:40.8887008Z return parser.parse_args() 2025-03-14T03:51:40.8887447Z 2025-03-14T03:51:40.8887454Z 2025-03-14T03:51:40.8887856Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-03-14T03:51:40.8888570Z auth = Auth.Token(github_token) 2025-03-14T03:51:40.8889050Z return Github(auth=auth) 2025-03-14T03:51:40.8889348Z 2025-03-14T03:51:40.8889354Z 2025-03-14T03:51:40.8889783Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-03-14T03:51:40.8890541Z repo = gh.get_repo(repo) 2025-03-14T03:51:40.8891010Z return repo.get_issue(number=issue_num) 2025-03-14T03:51:40.8891356Z 2025-03-14T03:51:40.8891362Z 2025-03-14T03:51:40.8891545Z def get_potential_pr_author( 2025-03-14T03:51:40.8892154Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-03-14T03:51:40.8892797Z ) -> str: 2025-03-14T03:51:40.8893290Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-03-14T03:51:40.8894047Z # Fetch the actual username from the original PR. The PR number is 2025-03-14T03:51:40.8894757Z # embedded in the tag name: ciflow// 2025-03-14T03:51:40.8895146Z 2025-03-14T03:51:40.8895331Z gh = get_gh_client(github_token) 2025-03-14T03:51:40.8895649Z 2025-03-14T03:51:40.8895905Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-03-14T03:51:40.8896492Z split_tag = ref_name.split("/") 2025-03-14T03:51:40.8896970Z if ( 2025-03-14T03:51:40.8897528Z len(split_tag) == 3 2025-03-14T03:51:40.8897996Z and split_tag[0] == "ciflow" 2025-03-14T03:51:40.8898518Z and split_tag[2].isnumeric() 2025-03-14T03:51:40.8898987Z ): 2025-03-14T03:51:40.8899353Z pr_number = split_tag[2] 2025-03-14T03:51:40.8899815Z try: 2025-03-14T03:51:40.8900226Z repository = gh.get_repo(repo) 2025-03-14T03:51:40.8900819Z pull = repository.get_pull(number=int(pr_number)) 2025-03-14T03:51:40.8901383Z except Exception as e: 2025-03-14T03:51:40.8901875Z raise Exception( # noqa: TRY002 2025-03-14T03:51:40.8902644Z f"issue with pull request {pr_number} from repo {repository}" 2025-03-14T03:51:40.8903262Z ) from e 2025-03-14T03:51:40.8903781Z return pull.user.login # type: ignore[no-any-return] 2025-03-14T03:51:40.8904438Z # In all other cases, return the original input username 2025-03-14T03:51:40.8904998Z return username 2025-03-14T03:51:40.8905234Z 2025-03-14T03:51:40.8905240Z 2025-03-14T03:51:40.8905450Z def is_exception_branch(branch: str) -> bool: 2025-03-14T03:51:40.8905965Z """ 2025-03-14T03:51:40.8906576Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-03-14T03:51:40.8907477Z """ 2025-03-14T03:51:40.8908010Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-03-14T03:51:40.8908513Z 2025-03-14T03:51:40.8908518Z 2025-03-14T03:51:40.8908704Z def load_yaml(yaml_text: str) -> Any: 2025-03-14T03:51:40.8909182Z try: 2025-03-14T03:51:40.8909561Z data = yaml.safe_load(yaml_text) 2025-03-14T03:51:40.8910047Z return data 2025-03-14T03:51:40.8910429Z except yaml.YAMLError: 2025-03-14T03:51:40.8910897Z log.exception("Error loading YAML") 2025-03-14T03:51:40.8911383Z raise 2025-03-14T03:51:40.8911584Z 2025-03-14T03:51:40.8911591Z 2025-03-14T03:51:40.8911985Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-03-14T03:51:40.8912688Z """ 2025-03-14T03:51:40.8913271Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-03-14T03:51:40.8913853Z 2025-03-14T03:51:40.8914179Z If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:40.8915019Z and the text below is the list of opted in users. 2025-03-14T03:51:40.8915404Z 2025-03-14T03:51:40.8915754Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-03-14T03:51:40.8916420Z """ 2025-03-14T03:51:40.8916847Z rollout_state_parts = rollout_state.split("---") 2025-03-14T03:51:40.8917607Z if len(rollout_state_parts) >= 2: 2025-03-14T03:51:40.8918193Z return rollout_state_parts[0], rollout_state_parts[1] 2025-03-14T03:51:40.8918754Z else: 2025-03-14T03:51:40.8919115Z return "", rollout_state 2025-03-14T03:51:40.8919407Z 2025-03-14T03:51:40.8919415Z 2025-03-14T03:51:40.8919607Z class UserOptins(dict[str, list[str]]): 2025-03-14T03:51:40.8920101Z """ 2025-03-14T03:51:40.8920596Z Dictionary of users with a list of features they have opted into 2025-03-14T03:51:40.8921205Z """ 2025-03-14T03:51:40.8921395Z 2025-03-14T03:51:40.8921401Z 2025-03-14T03:51:40.8921728Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-03-14T03:51:40.8922349Z """ 2025-03-14T03:51:40.8923020Z 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:40.8923665Z 2025-03-14T03:51:40.8924263Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-03-14T03:51:40.8925201Z - Example line: "@User1,lf,split_build" 2025-03-14T03:51:40.8925864Z - A "#" prefix indicates the user is opted out of all experiments 2025-03-14T03:51:40.8926330Z 2025-03-14T03:51:40.8926337Z 2025-03-14T03:51:40.8926482Z """ 2025-03-14T03:51:40.8926838Z optins = UserOptins() 2025-03-14T03:51:40.8927474Z for user in user_optin_text.split("\n"): 2025-03-14T03:51:40.8928016Z user = user.strip("\r\n\t -") 2025-03-14T03:51:40.8928544Z if not user or not user.startswith("@"): 2025-03-14T03:51:40.8929071Z # Not a valid user. Skip 2025-03-14T03:51:40.8929541Z continue 2025-03-14T03:51:40.8929773Z 2025-03-14T03:51:40.8929928Z if user: 2025-03-14T03:51:40.8930338Z usr_name = user.split(",")[0].strip("@") 2025-03-14T03:51:40.8931002Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-03-14T03:51:40.8931599Z 2025-03-14T03:51:40.8931761Z return optins 2025-03-14T03:51:40.8931987Z 2025-03-14T03:51:40.8931994Z 2025-03-14T03:51:40.8932264Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-03-14T03:51:40.8932859Z """ 2025-03-14T03:51:40.8933232Z Check if the experiment name is valid. 2025-03-14T03:51:40.8933721Z A valid name: 2025-03-14T03:51:40.8934323Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-03-14T03:51:40.8935222Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-03-14T03:51:40.8935910Z - Cannot contain spaces 2025-03-14T03:51:40.8936348Z """ 2025-03-14T03:51:40.8936532Z 2025-03-14T03:51:40.8936781Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-03-14T03:51:40.8937646Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-03-14T03:51:40.8938077Z 2025-03-14T03:51:40.8938229Z if valid: 2025-03-14T03:51:40.8938597Z return True 2025-03-14T03:51:40.8938829Z 2025-03-14T03:51:40.8938989Z log.error( 2025-03-14T03:51:40.8940357Z 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:40.8941813Z ) 2025-03-14T03:51:40.8942145Z return False 2025-03-14T03:51:40.8942373Z 2025-03-14T03:51:40.8942379Z 2025-03-14T03:51:40.8942669Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-03-14T03:51:40.8943256Z """ 2025-03-14T03:51:40.8943803Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-03-14T03:51:40.8944617Z """ 2025-03-14T03:51:40.8944948Z try: 2025-03-14T03:51:40.8945294Z if settings_text: 2025-03-14T03:51:40.8945976Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-03-14T03:51:40.8946734Z # for easy reading 2025-03-14T03:51:40.8947668Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-03-14T03:51:40.8948516Z # the backtick character in shell commands. 2025-03-14T03:51:40.8949089Z backtick = chr(96) # backtick character 2025-03-14T03:51:40.8949721Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-03-14T03:51:40.8950341Z settings = load_yaml(settings_text) 2025-03-14T03:51:40.8950693Z 2025-03-14T03:51:40.8951076Z # For now we just load experiments. We can expand this if/when we add more settings 2025-03-14T03:51:40.8951787Z experiments = {} 2025-03-14T03:51:40.8952061Z 2025-03-14T03:51:40.8952399Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-03-14T03:51:40.8953134Z if not is_valid_experiment_name(exp_name): 2025-03-14T03:51:40.8954196Z # 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:40.8955187Z continue 2025-03-14T03:51:40.8955459Z 2025-03-14T03:51:40.8955640Z valid_settings = {} 2025-03-14T03:51:40.8956133Z for setting in exp_settings: 2025-03-14T03:51:40.8956665Z if setting not in Experiment._fields: 2025-03-14T03:51:40.8957367Z log.warning( 2025-03-14T03:51:40.8958065Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-03-14T03:51:40.8958740Z ) 2025-03-14T03:51:40.8959159Z else: 2025-03-14T03:51:40.8959651Z valid_settings[setting] = exp_settings[setting] 2025-03-14T03:51:40.8960053Z 2025-03-14T03:51:40.8960307Z experiments[exp_name] = Experiment(**valid_settings) 2025-03-14T03:51:40.8961063Z return Settings(experiments) 2025-03-14T03:51:40.8961409Z 2025-03-14T03:51:40.8961573Z except Exception: 2025-03-14T03:51:40.8962034Z log.exception("Failed to parse settings") 2025-03-14T03:51:40.8962398Z 2025-03-14T03:51:40.8962564Z return Settings() 2025-03-14T03:51:40.8962804Z 2025-03-14T03:51:40.8962811Z 2025-03-14T03:51:40.8963049Z def parse_settings(rollout_state: str) -> Settings: 2025-03-14T03:51:40.8963576Z """ 2025-03-14T03:51:40.8963994Z Parse settings, if any, from the rollout state. 2025-03-14T03:51:40.8964384Z 2025-03-14T03:51:40.8964714Z If the issue body contains "---" then the text above that is the settings 2025-03-14T03:51:40.8965434Z and the text below is the list of opted in users. 2025-03-14T03:51:40.8965821Z 2025-03-14T03:51:40.8966234Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-03-14T03:51:40.8966932Z """ 2025-03-14T03:51:40.8967596Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:40.8968316Z return parse_settings_from_text(settings_text) 2025-03-14T03:51:40.8968699Z 2025-03-14T03:51:40.8968706Z 2025-03-14T03:51:40.8968931Z def parse_users(rollout_state: str) -> UserOptins: 2025-03-14T03:51:40.8969459Z """ 2025-03-14T03:51:40.8969829Z Parse users from the rollout state. 2025-03-14T03:51:40.8970163Z 2025-03-14T03:51:40.8970313Z """ 2025-03-14T03:51:40.8970818Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-03-14T03:51:40.8971508Z return parse_user_opt_in_from_text(users_text) 2025-03-14T03:51:40.8971891Z 2025-03-14T03:51:40.8971898Z 2025-03-14T03:51:40.8972478Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:40.8973178Z """ 2025-03-14T03:51:40.8973565Z Check if a user is opted into an experiment 2025-03-14T03:51:40.8974067Z """ 2025-03-14T03:51:40.8974490Z return experiment_name in user_optins.get(user, []) 2025-03-14T03:51:40.8974892Z 2025-03-14T03:51:40.8974899Z 2025-03-14T03:51:40.8975289Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-03-14T03:51:40.8976000Z """ 2025-03-14T03:51:40.8976429Z Check if a user explicitly opted out of an experiment 2025-03-14T03:51:40.8976973Z """ 2025-03-14T03:51:40.8977561Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-03-14T03:51:40.8978219Z experiment_optout = "-" + experiment_name 2025-03-14T03:51:40.8978825Z if experiment_optout not in user_optins.get(user, []): 2025-03-14T03:51:40.8979387Z return False 2025-03-14T03:51:40.8979625Z 2025-03-14T03:51:40.8979893Z if is_user_opted_in(user, user_optins, experiment_name): 2025-03-14T03:51:40.8980463Z log.warning( 2025-03-14T03:51:40.8981212Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-03-14T03:51:40.8982036Z ) 2025-03-14T03:51:40.8982234Z 2025-03-14T03:51:40.8982396Z return True 2025-03-14T03:51:40.8982615Z 2025-03-14T03:51:40.8982621Z 2025-03-14T03:51:40.8982791Z def get_runner_prefix( 2025-03-14T03:51:40.8983197Z rollout_state: str, 2025-03-14T03:51:40.8983635Z workflow_requestors: Iterable[str], 2025-03-14T03:51:40.8984124Z branch: str, 2025-03-14T03:51:40.8984585Z eligible_experiments: frozenset[str] = frozenset(), 2025-03-14T03:51:40.8985153Z is_canary: bool = False, 2025-03-14T03:51:40.8985581Z ) -> str: 2025-03-14T03:51:40.8985974Z settings = parse_settings(rollout_state) 2025-03-14T03:51:40.8986525Z user_optins = parse_users(rollout_state) 2025-03-14T03:51:40.8986882Z 2025-03-14T03:51:40.8987048Z fleet_prefix = "" 2025-03-14T03:51:40.8987546Z prefixes = [] 2025-03-14T03:51:40.8988134Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-03-14T03:51:40.8989013Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-03-14T03:51:40.8989837Z log.info( 2025-03-14T03:51:40.8990495Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-03-14T03:51:40.8991212Z ) 2025-03-14T03:51:40.8991572Z continue 2025-03-14T03:51:40.8991810Z 2025-03-14T03:51:40.8991991Z if eligible_experiments: 2025-03-14T03:51:40.8992514Z if experiment_name not in eligible_experiments: 2025-03-14T03:51:40.8993113Z exp_list = ", ".join(eligible_experiments) 2025-03-14T03:51:40.8993641Z log.info( 2025-03-14T03:51:40.8994374Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-03-14T03:51:40.8995161Z ) 2025-03-14T03:51:40.8995536Z continue 2025-03-14T03:51:40.8995984Z elif not experiment_settings.default: 2025-03-14T03:51:40.8996478Z log.info( 2025-03-14T03:51:40.8997097Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-03-14T03:51:40.8997898Z ) 2025-03-14T03:51:40.8998256Z continue 2025-03-14T03:51:40.8998493Z 2025-03-14T03:51:40.8998755Z # Is any workflow_requestor opted out to this experiment? 2025-03-14T03:51:40.8999343Z opted_out_users = [ 2025-03-14T03:51:40.8999771Z requestor 2025-03-14T03:51:40.9000196Z for requestor in workflow_requestors 2025-03-14T03:51:40.9000836Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-03-14T03:51:40.9001427Z ] 2025-03-14T03:51:40.9001628Z 2025-03-14T03:51:40.9001793Z if opted_out_users: 2025-03-14T03:51:40.9002346Z log.info( 2025-03-14T03:51:40.9002925Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-03-14T03:51:40.9003578Z ) 2025-03-14T03:51:40.9003930Z continue 2025-03-14T03:51:40.9004168Z 2025-03-14T03:51:40.9004426Z # Is any workflow_requestor opted in to this experiment? 2025-03-14T03:51:40.9005011Z opted_in_users = [ 2025-03-14T03:51:40.9005431Z requestor 2025-03-14T03:51:40.9005864Z for requestor in workflow_requestors 2025-03-14T03:51:40.9006493Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-03-14T03:51:40.9007078Z ] 2025-03-14T03:51:40.9007372Z 2025-03-14T03:51:40.9007541Z enabled = False 2025-03-14T03:51:40.9007955Z if opted_in_users: 2025-03-14T03:51:40.9008368Z log.info( 2025-03-14T03:51:40.9008933Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-03-14T03:51:40.9009578Z ) 2025-03-14T03:51:40.9009941Z enabled = True 2025-03-14T03:51:40.9010203Z 2025-03-14T03:51:40.9010416Z elif experiment_settings.rollout_perc: 2025-03-14T03:51:40.9011197Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-03-14T03:51:40.9012071Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-03-14T03:51:40.9012684Z log.info( 2025-03-14T03:51:40.9013496Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-03-14T03:51:40.9014350Z ) 2025-03-14T03:51:40.9014731Z enabled = True 2025-03-14T03:51:40.9015008Z 2025-03-14T03:51:40.9015159Z if enabled: 2025-03-14T03:51:40.9015558Z label = experiment_name 2025-03-14T03:51:40.9016078Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-03-14T03:51:40.9016869Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-03-14T03:51:40.9017792Z # - If it's enabled, then we always list it's prefix first 2025-03-14T03:51:40.9018619Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-03-14T03:51:40.9019242Z if is_canary: 2025-03-14T03:51:40.9019714Z label += CANARY_FLEET_SUFFIX 2025-03-14T03:51:40.9020231Z fleet_prefix = label 2025-03-14T03:51:40.9020697Z else: 2025-03-14T03:51:40.9021102Z prefixes.append(label) 2025-03-14T03:51:40.9021431Z 2025-03-14T03:51:40.9021598Z if len(prefixes) > 1: 2025-03-14T03:51:40.9022012Z log.error( 2025-03-14T03:51:40.9022976Z 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:40.9024021Z ) 2025-03-14T03:51:40.9024386Z prefixes = prefixes[:1] 2025-03-14T03:51:40.9024672Z 2025-03-14T03:51:40.9024849Z # Fleet always comes first 2025-03-14T03:51:40.9025290Z if fleet_prefix: 2025-03-14T03:51:40.9025715Z prefixes.insert(0, fleet_prefix) 2025-03-14T03:51:40.9026052Z 2025-03-14T03:51:40.9026296Z return ".".join(prefixes) + "." if prefixes else "" 2025-03-14T03:51:40.9026678Z 2025-03-14T03:51:40.9026684Z 2025-03-14T03:51:40.9027099Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-03-14T03:51:40.9027926Z """ 2025-03-14T03:51:40.9028472Z Gets the first comment of the issue, which contains the desired rollout state. 2025-03-14T03:51:40.9028997Z 2025-03-14T03:51:40.9029355Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-03-14T03:51:40.9030013Z """ 2025-03-14T03:51:40.9030372Z gh = get_gh_client(github_token) 2025-03-14T03:51:40.9030877Z issue = get_issue(gh, repo, issue_num) 2025-03-14T03:51:40.9031594Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-03-14T03:51:40.9032012Z 2025-03-14T03:51:40.9032018Z 2025-03-14T03:51:40.9032396Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-03-14T03:51:40.9033101Z for _ in range(num_retries): 2025-03-14T03:51:40.9033554Z try: 2025-03-14T03:51:40.9033962Z req = Request(url=url, headers=headers) 2025-03-14T03:51:40.9034582Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-03-14T03:51:40.9035175Z return json.loads(content) 2025-03-14T03:51:40.9035674Z except Exception as e: 2025-03-14T03:51:40.9036184Z log.warning(f"Could not download {url}: {e}") 2025-03-14T03:51:40.9036559Z 2025-03-14T03:51:40.9036919Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-03-14T03:51:40.9037690Z return {} 2025-03-14T03:51:40.9037902Z 2025-03-14T03:51:40.9037907Z 2025-03-14T03:51:40.9038066Z @cache 2025-03-14T03:51:40.9038646Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-03-14T03:51:40.9039636Z """ 2025-03-14T03:51:40.9040167Z Dynamically get PR information 2025-03-14T03:51:40.9040910Z """ 2025-03-14T03:51:40.9041685Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-03-14T03:51:40.9042621Z headers = { 2025-03-14T03:51:40.9043380Z "Accept": "application/vnd.github.v3+json", 2025-03-14T03:51:40.9044288Z "Authorization": f"token {github_token}", 2025-03-14T03:51:40.9045115Z } 2025-03-14T03:51:40.9045777Z json_response: dict[str, Any] = download_json( 2025-03-14T03:51:40.9046675Z url=f"{github_api}/issues/{pr_number}", 2025-03-14T03:51:40.9047770Z headers=headers, 2025-03-14T03:51:40.9048525Z ) 2025-03-14T03:51:40.9048891Z 2025-03-14T03:51:40.9049206Z if not json_response: 2025-03-14T03:51:40.9050160Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-03-14T03:51:40.9051269Z return {} 2025-03-14T03:51:40.9051702Z 2025-03-14T03:51:40.9052015Z return json_response 2025-03-14T03:51:40.9052527Z 2025-03-14T03:51:40.9052538Z 2025-03-14T03:51:40.9053290Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-03-14T03:51:40.9054841Z """ 2025-03-14T03:51:40.9055816Z Dynamically get the latest list of labels from the pull request 2025-03-14T03:51:40.9056962Z """ 2025-03-14T03:51:40.9058035Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-03-14T03:51:40.9059070Z return { 2025-03-14T03:51:40.9059972Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-03-14T03:51:40.9061000Z } 2025-03-14T03:51:40.9061297Z 2025-03-14T03:51:40.9061307Z 2025-03-14T03:51:40.9061584Z def main() -> None: 2025-03-14T03:51:40.9062237Z args = parse_args() 2025-03-14T03:51:40.9062693Z 2025-03-14T03:51:40.9063058Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-03-14T03:51:40.9063721Z 2025-03-14T03:51:40.9064025Z # Check if the PR is opt-out 2025-03-14T03:51:40.9064792Z if args.pr_number: 2025-03-14T03:51:40.9065823Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-03-14T03:51:40.9067370Z if OPT_OUT_LABEL in labels: 2025-03-14T03:51:40.9068270Z log.info( 2025-03-14T03:51:40.9069351Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-03-14T03:51:40.9070089Z ) 2025-03-14T03:51:40.9070611Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:40.9071239Z sys.exit() 2025-03-14T03:51:40.9071489Z 2025-03-14T03:51:40.9071643Z try: 2025-03-14T03:51:40.9072048Z rollout_state = get_rollout_state_from_issue( 2025-03-14T03:51:40.9072902Z args.github_token, args.github_issue_repo, args.github_issue 2025-03-14T03:51:40.9073519Z ) 2025-03-14T03:51:40.9073914Z 2025-03-14T03:51:40.9074113Z username = get_potential_pr_author( 2025-03-14T03:51:40.9074619Z args.github_token, 2025-03-14T03:51:40.9075081Z args.github_repo, 2025-03-14T03:51:40.9075526Z args.github_actor, 2025-03-14T03:51:40.9075978Z args.github_ref_type, 2025-03-14T03:51:40.9076461Z args.github_branch, 2025-03-14T03:51:40.9076884Z ) 2025-03-14T03:51:40.9077088Z 2025-03-14T03:51:40.9077575Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-03-14T03:51:40.9078013Z 2025-03-14T03:51:40.9078221Z runner_label_prefix = get_runner_prefix( 2025-03-14T03:51:40.9078748Z rollout_state, 2025-03-14T03:51:40.9079201Z (args.github_issue_owner, username), 2025-03-14T03:51:40.9079712Z args.github_branch, 2025-03-14T03:51:40.9080175Z args.eligible_experiments, 2025-03-14T03:51:40.9080661Z is_canary, 2025-03-14T03:51:40.9081051Z ) 2025-03-14T03:51:40.9081245Z 2025-03-14T03:51:40.9081422Z except Exception as e: 2025-03-14T03:51:40.9081845Z log.error( 2025-03-14T03:51:40.9082469Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-03-14T03:51:40.9083185Z ) 2025-03-14T03:51:40.9083374Z 2025-03-14T03:51:40.9083690Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-03-14T03:51:40.9084157Z 2025-03-14T03:51:40.9084162Z 2025-03-14T03:51:40.9084334Z if __name__ == "__main__": 2025-03-14T03:51:40.9084742Z main() 2025-03-14T03:51:40.9084940Z 2025-03-14T03:51:40.9169067Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-03-14T03:51:40.9169918Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-03-14T03:51:40.9218164Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:40.9218633Z env: 2025-03-14T03:51:40.9219213Z GITHUB_TOKEN: *** 2025-03-14T03:51:40.9219605Z ISSUE_NUMBER: 5132 2025-03-14T03:51:40.9220037Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:40.9220508Z ISSUE_OWNER: 2025-03-14T03:51:40.9220889Z CHECK_EXPERIMENTS: 2025-03-14T03:51:40.9221281Z PR_NUMBER: 2025-03-14T03:51:40.9221637Z ##[endgroup] 2025-03-14T03:51:42.0604475Z Defaulting to user installation because normal site-packages is not writeable 2025-03-14T03:51:43.0684151Z Collecting urllib3==1.26.18 2025-03-14T03:51:43.1129197Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-03-14T03:51:43.1333175Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 3.9 MB/s eta 0:00:00 2025-03-14T03:51:43.1588455Z Collecting PyGithub==2.3.0 2025-03-14T03:51:43.1656356Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-03-14T03:51:43.2112794Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-03-14T03:51:43.2182004Z 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:43.2223632Z 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:43.2239612Z 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:43.2253937Z 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:43.2531767Z Collecting Deprecated (from PyGithub==2.3.0) 2025-03-14T03:51:43.2602705Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl.metadata (5.7 kB) 2025-03-14T03:51:43.2833585Z 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:43.4049934Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-03-14T03:51:43.4120424Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-03-14T03:51:43.5170259Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-03-14T03:51:43.5241790Z 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:43.5457659Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-03-14T03:51:43.5530080Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-03-14T03:51:43.5793145Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-03-14T03:51:43.5947319Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 9.8 MB/s eta 0:00:00 2025-03-14T03:51:43.6024219Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-03-14T03:51:43.6270978Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 14.9 MB/s eta 0:00:00 2025-03-14T03:51:43.6343557Z 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:43.6796586Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 19.5 MB/s eta 0:00:00 2025-03-14T03:51:43.6869599Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl (10.0 kB) 2025-03-14T03:51:43.6960019Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-03-14T03:51:43.7170839Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 24.0 MB/s eta 0:00:00 2025-03-14T03:51:43.7245879Z 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:43.7287800Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 29.6 MB/s eta 0:00:00 2025-03-14T03:51:43.7374594Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-03-14T03:51:43.7428809Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 31.6 MB/s eta 0:00:00 2025-03-14T03:51:44.0385613Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-03-14T03:51:44.5648674Z 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:44.6376186Z ##[group]Run curr_branch="main" 2025-03-14T03:51:44.6376582Z curr_branch="main" 2025-03-14T03:51:44.6376825Z curr_ref_type="branch" 2025-03-14T03:51:44.6377087Z echo "Current branch is '$curr_branch'" 2025-03-14T03:51:44.6377572Z  2025-03-14T03:51:44.6377779Z python3 runner_determinator.py \ 2025-03-14T03:51:44.6378189Z  --github-token "$GITHUB_TOKEN" \ 2025-03-14T03:51:44.6378489Z  --github-issue "$ISSUE_NUMBER" \ 2025-03-14T03:51:44.6378761Z  --github-branch "$curr_branch" \ 2025-03-14T03:51:44.6379046Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-03-14T03:51:44.6379335Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-03-14T03:51:44.6379624Z  --github-ref-type "$curr_ref_type" \ 2025-03-14T03:51:44.6379913Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-03-14T03:51:44.6380236Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-03-14T03:51:44.6380588Z  --pr-number "${PR_NUMBER}" 2025-03-14T03:51:44.6427854Z shell: /usr/bin/bash -e {0} 2025-03-14T03:51:44.6428098Z env: 2025-03-14T03:51:44.6428632Z GITHUB_TOKEN: *** 2025-03-14T03:51:44.6428839Z ISSUE_NUMBER: 5132 2025-03-14T03:51:44.6429053Z TRIGGERING_ACTOR: pytorchmergebot 2025-03-14T03:51:44.6429291Z ISSUE_OWNER: 2025-03-14T03:51:44.6429482Z CHECK_EXPERIMENTS: 2025-03-14T03:51:44.6429682Z PR_NUMBER: 2025-03-14T03:51:44.6429864Z ##[endgroup] 2025-03-14T03:51:44.6493213Z Current branch is 'main' 2025-03-14T03:51:46.2709106Z INFO : Branch main is an exception branch. Not enabling experiment ephemeral. 2025-03-14T03:51:46.2709900Z INFO : Setting output: label-type='' 2025-03-14T03:51:46.3013654Z Evaluate and set job outputs 2025-03-14T03:51:46.3021076Z Cleaning up orphan processes