2025-01-24T00:44:51.8489895Z Current runner version: '2.321.0' 2025-01-24T00:44:51.8515760Z ##[group]Operating System 2025-01-24T00:44:51.8516873Z Ubuntu 2025-01-24T00:44:51.8517368Z 24.04.1 2025-01-24T00:44:51.8517904Z LTS 2025-01-24T00:44:51.8518397Z ##[endgroup] 2025-01-24T00:44:51.8518962Z ##[group]Runner Image 2025-01-24T00:44:51.8519550Z Image: ubuntu-24.04 2025-01-24T00:44:51.8520067Z Version: 20250120.5.0 2025-01-24T00:44:51.8521104Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250120.5/images/ubuntu/Ubuntu2404-Readme.md 2025-01-24T00:44:51.8522444Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250120.5 2025-01-24T00:44:51.8523371Z ##[endgroup] 2025-01-24T00:44:51.8523867Z ##[group]Runner Image Provisioner 2025-01-24T00:44:51.8524440Z 2.0.417.1 2025-01-24T00:44:51.8524949Z ##[endgroup] 2025-01-24T00:44:51.8525999Z ##[group]GITHUB_TOKEN Permissions 2025-01-24T00:44:51.8528117Z Contents: read 2025-01-24T00:44:51.8528776Z Metadata: read 2025-01-24T00:44:51.8529455Z Packages: read 2025-01-24T00:44:51.8530124Z ##[endgroup] 2025-01-24T00:44:51.8533424Z Secret source: Actions 2025-01-24T00:44:51.8534176Z Prepare workflow directory 2025-01-24T00:44:51.9024310Z Prepare all required actions 2025-01-24T00:44:51.9078839Z Uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@refs/heads/main (b2c89bc115123aea8e075e882ee121537ec92f89) 2025-01-24T00:44:51.9084038Z ##[group] Inputs 2025-01-24T00:44:51.9084618Z check_experiments: 2025-01-24T00:44:51.9085319Z triggering_actor: pytorch-bot[bot] 2025-01-24T00:44:51.9085955Z issue_owner: 2025-01-24T00:44:51.9086616Z curr_branch: ciflow/trunk/145539 2025-01-24T00:44:51.9087333Z curr_ref_type: tag 2025-01-24T00:44:51.9087875Z issue_number: 5132 2025-01-24T00:44:51.9088363Z ##[endgroup] 2025-01-24T00:44:51.9089031Z Complete job name: get-label-type / runner-determinator 2025-01-24T00:44:51.9816799Z ##[group]Run cat < runner_determinator.py 2025-01-24T00:44:51.9818428Z cat < runner_determinator.py 2025-01-24T00:44:51.9819015Z # flake8: noqa: G004 2025-01-24T00:44:51.9819471Z  2025-01-24T00:44:51.9820109Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-01-24T00:44:51.9821073Z # must be kept in sync. You can do it easily by running the following command: 2025-01-24T00:44:51.9821884Z # python .github/scripts/update_runner_determinator.py 2025-01-24T00:44:51.9822492Z  2025-01-24T00:44:51.9822835Z """ 2025-01-24T00:44:51.9823450Z This runner determinator is used to determine which set of runners to run a 2025-01-24T00:44:51.9824354Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-01-24T00:44:51.9825313Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-01-24T00:44:51.9826151Z of which runners should be used to run which job. 2025-01-24T00:44:51.9826903Z  2025-01-24T00:44:51.9827515Z The configuration has two parts, the settings and a list of opted-in users, 2025-01-24T00:44:51.9828435Z separated by a line containing "---". If the line is not present, the 2025-01-24T00:44:51.9829338Z settings are considered to be empty with only the second part, the user 2025-01-24T00:44:51.9830044Z list, defined. 2025-01-24T00:44:51.9830463Z  2025-01-24T00:44:51.9831042Z The first part is a YAML block that defines the rollout settings. This can be 2025-01-24T00:44:51.9831992Z used to define any settings that are needed to determine which runners to use. 2025-01-24T00:44:51.9832858Z It's fields are defined by the RolloutSettings class below. 2025-01-24T00:44:51.9833470Z  2025-01-24T00:44:51.9834066Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-01-24T00:44:51.9834964Z The user list is also a comma separated list of additional features or 2025-01-24T00:44:51.9836122Z experiments which the user could be opted in to. 2025-01-24T00:44:51.9836913Z  2025-01-24T00:44:51.9837326Z The user list has the following rules: 2025-01-24T00:44:51.9837843Z  2025-01-24T00:44:51.9838377Z - Users are GitHub usernames, which must start with the @ prefix 2025-01-24T00:44:51.9839236Z - Each user is also a comma-separated list of features/experiments to enable 2025-01-24T00:44:51.9840024Z - A "#" prefix opts the user out of all experiments 2025-01-24T00:44:51.9840588Z  2025-01-24T00:44:51.9840955Z Example config: 2025-01-24T00:44:51.9841459Z  # A list of experiments that can be opted into. 2025-01-24T00:44:51.9842152Z  # This defines the behavior they'll induce when opted into. 2025-01-24T00:44:51.9842799Z  # Expected syntax is: 2025-01-24T00:44:51.9843492Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-01-24T00:44:51.9844468Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-01-24T00:44:51.9845227Z  2025-01-24T00:44:51.9845583Z  experiments: 2025-01-24T00:44:51.9845999Z  lf: 2025-01-24T00:44:51.9846507Z  rollout_percent: 25 2025-01-24T00:44:51.9847008Z  all_branches: false 2025-01-24T00:44:51.9847500Z  default: true 2025-01-24T00:44:51.9847964Z  --- 2025-01-24T00:44:51.9848350Z  2025-01-24T00:44:51.9848714Z  # Opt-ins: 2025-01-24T00:44:51.9849368Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-01-24T00:44:51.9850427Z  # and specifying experiments to enable in a comma-separated list. 2025-01-24T00:44:51.9851244Z  # To always opt out of an experiment, prefix it with a "-". 2025-01-24T00:44:51.9851946Z  # Experiments should be from the above list. 2025-01-24T00:44:51.9852501Z  2025-01-24T00:44:51.9852881Z  @User1,-lf,split_build 2025-01-24T00:44:51.9853379Z  @User2,lf 2025-01-24T00:44:51.9853804Z  @User3,split_build 2025-01-24T00:44:51.9854259Z """ 2025-01-24T00:44:51.9854623Z  2025-01-24T00:44:51.9854983Z import json 2025-01-24T00:44:51.9855390Z import logging 2025-01-24T00:44:51.9855810Z import os 2025-01-24T00:44:51.9856209Z import random 2025-01-24T00:44:51.9856739Z import re 2025-01-24T00:44:51.9857190Z import sys 2025-01-24T00:44:51.9857702Z from argparse import ArgumentParser 2025-01-24T00:44:51.9858264Z from functools import lru_cache 2025-01-24T00:44:51.9858797Z from logging import LogRecord 2025-01-24T00:44:51.9859537Z from typing import Any, Dict, FrozenSet, Iterable, List, NamedTuple, Set, Tuple 2025-01-24T00:44:51.9860350Z from urllib.request import Request, urlopen 2025-01-24T00:44:51.9860893Z  2025-01-24T00:44:51.9861254Z import yaml 2025-01-24T00:44:51.9861685Z from github import Auth, Github 2025-01-24T00:44:51.9862210Z from github.Issue import Issue 2025-01-24T00:44:51.9862703Z  2025-01-24T00:44:51.9863054Z  2025-01-24T00:44:51.9863480Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-01-24T00:44:51.9864213Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-01-24T00:44:51.9865107Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-01-24T00:44:51.9865804Z  2025-01-24T00:44:51.9866250Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-01-24T00:44:51.9866978Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-01-24T00:44:51.9867536Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-01-24T00:44:51.9868315Z OPT_OUT_LABEL = "no-runner-experiments" 2025-01-24T00:44:51.9868848Z  2025-01-24T00:44:51.9869244Z SETTING_EXPERIMENTS = "experiments" 2025-01-24T00:44:51.9869750Z  2025-01-24T00:44:51.9870117Z LF_FLEET_EXPERIMENT = "lf" 2025-01-24T00:44:51.9870612Z CANARY_FLEET_SUFFIX = ".c" 2025-01-24T00:44:51.9871082Z  2025-01-24T00:44:51.9871428Z  2025-01-24T00:44:51.9871800Z class Experiment(NamedTuple): 2025-01-24T00:44:51.9872318Z  rollout_perc: float = ( 2025-01-24T00:44:51.9873013Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-01-24T00:44:51.9873690Z  ) 2025-01-24T00:44:51.9874088Z  all_branches: bool = ( 2025-01-24T00:44:51.9874774Z  False # If True, the experiment is also enabled on the exception branches 2025-01-24T00:44:51.9875452Z  ) 2025-01-24T00:44:51.9875844Z  default: bool = ( 2025-01-24T00:44:51.9876644Z  True # If True, the experiment is enabled by default for all queries 2025-01-24T00:44:51.9877300Z  ) 2025-01-24T00:44:51.9877668Z  2025-01-24T00:44:51.9878050Z  # Add more fields as needed 2025-01-24T00:44:51.9878535Z  2025-01-24T00:44:51.9878881Z  2025-01-24T00:44:51.9879260Z class Settings(NamedTuple): 2025-01-24T00:44:51.9879754Z  """ 2025-01-24T00:44:51.9880263Z  Settings for the experiments that can be opted into. 2025-01-24T00:44:51.9880856Z  """ 2025-01-24T00:44:51.9881225Z  2025-01-24T00:44:51.9881639Z  experiments: Dict[str, Experiment] = {} 2025-01-24T00:44:51.9882167Z  2025-01-24T00:44:51.9882507Z  2025-01-24T00:44:51.9883116Z class ColorFormatter(logging.Formatter): 2025-01-24T00:44:51.9883802Z  """Color codes the log messages based on the log level""" 2025-01-24T00:44:51.9884401Z  2025-01-24T00:44:51.9884760Z  COLORS = { 2025-01-24T00:44:51.9885217Z  "WARNING": "\033[33m", # Yellow 2025-01-24T00:44:51.9885742Z  "ERROR": "\033[31m", # Red 2025-01-24T00:44:51.9886262Z  "CRITICAL": "\033[31m", # Red 2025-01-24T00:44:51.9886920Z  "INFO": "\033[0m", # Reset 2025-01-24T00:44:51.9887439Z  "DEBUG": "\033[0m", # Reset 2025-01-24T00:44:51.9887934Z  } 2025-01-24T00:44:51.9888302Z  2025-01-24T00:44:51.9888730Z  def format(self, record: LogRecord) -> str: 2025-01-24T00:44:51.9889758Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-01-24T00:44:51.9890567Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-01-24T00:44:51.9891156Z  return super().format(record) 2025-01-24T00:44:51.9891668Z  2025-01-24T00:44:51.9892018Z  2025-01-24T00:44:51.9892406Z handler = logging.StreamHandler() 2025-01-24T00:44:51.9893170Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-01-24T00:44:51.9893893Z  2025-01-24T00:44:51.9894352Z log = logging.getLogger(os.path.basename(__file__)) 2025-01-24T00:44:51.9894958Z log.addHandler(handler) 2025-01-24T00:44:51.9895451Z log.setLevel(logging.INFO) 2025-01-24T00:44:51.9895927Z  2025-01-24T00:44:51.9896279Z  2025-01-24T00:44:51.9896851Z def set_github_output(key: str, value: str) -> None: 2025-01-24T00:44:51.9897432Z  """ 2025-01-24T00:44:51.9897973Z  Defines outputs of the github action that invokes this script 2025-01-24T00:44:51.9898609Z  """ 2025-01-24T00:44:51.9899006Z  if not GITHUB_OUTPUT: 2025-01-24T00:44:51.9900110Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-01-24T00:44:51.9901461Z  log.warning( 2025-01-24T00:44:51.9902610Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-01-24T00:44:51.9903526Z  ) 2025-01-24T00:44:51.9903995Z  print(f"::set-output name={key}::{value}") 2025-01-24T00:44:51.9904555Z  return 2025-01-24T00:44:51.9904962Z  2025-01-24T00:44:51.9905395Z  with open(GITHUB_OUTPUT, "a") as f: 2025-01-24T00:44:51.9906003Z  log.info(f"Setting output: {key}='{value}'") 2025-01-24T00:44:51.9906722Z  f.write(f"{key}={value}\n") 2025-01-24T00:44:51.9907221Z  2025-01-24T00:44:51.9907574Z  2025-01-24T00:44:51.9908087Z def _str_comma_separated_to_set(value: str) -> FrozenSet[str]: 2025-01-24T00:44:51.9908744Z  return frozenset( 2025-01-24T00:44:51.9909408Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-01-24T00:44:51.9910079Z  ) 2025-01-24T00:44:51.9910452Z  2025-01-24T00:44:51.9910797Z  2025-01-24T00:44:51.9911165Z def parse_args() -> Any: 2025-01-24T00:44:51.9911783Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-01-24T00:44:51.9912668Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-01-24T00:44:51.9913429Z  parser.add_argument( 2025-01-24T00:44:51.9913925Z  "--github-issue-repo", 2025-01-24T00:44:51.9914425Z  type=str, 2025-01-24T00:44:51.9914868Z  required=False, 2025-01-24T00:44:51.9915491Z  default="pytorch/test-infra", 2025-01-24T00:44:51.9916075Z  help="GitHub repo to get the issue", 2025-01-24T00:44:51.9916738Z  ) 2025-01-24T00:44:51.9917133Z  parser.add_argument( 2025-01-24T00:44:51.9917613Z  "--github-repo", 2025-01-24T00:44:51.9918079Z  type=str, 2025-01-24T00:44:51.9918517Z  required=True, 2025-01-24T00:44:51.9919030Z  help="GitHub repo where CI is running", 2025-01-24T00:44:51.9919555Z  ) 2025-01-24T00:44:51.9919947Z  parser.add_argument( 2025-01-24T00:44:51.9920599Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-01-24T00:44:51.9921257Z  ) 2025-01-24T00:44:51.9921644Z  parser.add_argument( 2025-01-24T00:44:51.9922319Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-01-24T00:44:51.9922990Z  ) 2025-01-24T00:44:51.9923385Z  parser.add_argument( 2025-01-24T00:44:51.9924061Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-01-24T00:44:51.9924749Z  ) 2025-01-24T00:44:51.9925141Z  parser.add_argument( 2025-01-24T00:44:51.9925839Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-01-24T00:44:51.9926649Z  ) 2025-01-24T00:44:51.9927031Z  parser.add_argument( 2025-01-24T00:44:51.9927546Z  "--github-ref-type", 2025-01-24T00:44:51.9928031Z  type=str, 2025-01-24T00:44:51.9928469Z  required=True, 2025-01-24T00:44:51.9929020Z  help="Current GitHub ref type, branch or tag", 2025-01-24T00:44:51.9929574Z  ) 2025-01-24T00:44:51.9929962Z  parser.add_argument( 2025-01-24T00:44:51.9930455Z  "--eligible-experiments", 2025-01-24T00:44:51.9931004Z  type=_str_comma_separated_to_set, 2025-01-24T00:44:51.9931537Z  required=False, 2025-01-24T00:44:51.9931993Z  default="", 2025-01-24T00:44:51.9933029Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-01-24T00:44:51.9933960Z  ) 2025-01-24T00:44:51.9934344Z  parser.add_argument( 2025-01-24T00:44:51.9934823Z  "--pr-number", 2025-01-24T00:44:51.9935284Z  type=str, 2025-01-24T00:44:51.9935724Z  required=False, 2025-01-24T00:44:51.9936190Z  default="", 2025-01-24T00:44:51.9936835Z  help="the optional PR number where this is run", 2025-01-24T00:44:51.9937399Z  ) 2025-01-24T00:44:51.9937767Z  2025-01-24T00:44:51.9938149Z  return parser.parse_args() 2025-01-24T00:44:51.9938636Z  2025-01-24T00:44:51.9938985Z  2025-01-24T00:44:51.9939426Z def get_gh_client(github_token: str) -> Github: 2025-01-24T00:44:51.9940016Z  auth = Auth.Token(github_token) 2025-01-24T00:44:51.9940541Z  return Github(auth=auth) 2025-01-24T00:44:51.9941012Z  2025-01-24T00:44:51.9941347Z  2025-01-24T00:44:51.9941855Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: 2025-01-24T00:44:51.9942510Z  repo = gh.get_repo(repo) 2025-01-24T00:44:51.9943045Z  return repo.get_issue(number=issue_num) 2025-01-24T00:44:51.9943579Z  2025-01-24T00:44:51.9943921Z  2025-01-24T00:44:51.9944289Z def get_potential_pr_author( 2025-01-24T00:44:51.9944972Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-01-24T00:44:51.9945647Z ) -> str: 2025-01-24T00:44:51.9946200Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-01-24T00:44:51.9947273Z  # Fetch the actual username from the original PR. The PR number is 2025-01-24T00:44:51.9948052Z  # embedded in the tag name: ciflow// 2025-01-24T00:44:51.9948632Z  2025-01-24T00:44:51.9949018Z  gh = get_gh_client(github_token) 2025-01-24T00:44:51.9949523Z  2025-01-24T00:44:51.9949997Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-01-24T00:44:51.9950632Z  split_tag = ref_name.split("/") 2025-01-24T00:44:51.9951155Z  if ( 2025-01-24T00:44:51.9951571Z  len(split_tag) == 3 2025-01-24T00:44:51.9952082Z  and split_tag[0] == "ciflow" 2025-01-24T00:44:51.9952627Z  and split_tag[2].isnumeric() 2025-01-24T00:44:51.9953137Z  ): 2025-01-24T00:44:51.9953559Z  pr_number = split_tag[2] 2025-01-24T00:44:51.9954061Z  try: 2025-01-24T00:44:51.9954541Z  repository = gh.get_repo(repo) 2025-01-24T00:44:51.9955170Z  pull = repository.get_pull(number=int(pr_number)) 2025-01-24T00:44:51.9955793Z  except Exception as e: 2025-01-24T00:44:51.9956345Z  raise Exception( # noqa: TRY002 2025-01-24T00:44:51.9957172Z  f"issue with pull request {pr_number} from repo {repository}" 2025-01-24T00:44:51.9957826Z  ) from e 2025-01-24T00:44:51.9958332Z  return pull.user.login 2025-01-24T00:44:51.9958934Z  # In all other cases, return the original input username 2025-01-24T00:44:51.9959532Z  return username 2025-01-24T00:44:51.9959964Z  2025-01-24T00:44:51.9960296Z  2025-01-24T00:44:51.9960723Z def is_exception_branch(branch: str) -> bool: 2025-01-24T00:44:51.9961264Z  """ 2025-01-24T00:44:51.9961937Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-01-24T00:44:51.9962712Z  """ 2025-01-24T00:44:51.9963417Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-01-24T00:44:51.9964083Z  2025-01-24T00:44:51.9964422Z  2025-01-24T00:44:51.9964816Z def load_yaml(yaml_text: str) -> Any: 2025-01-24T00:44:51.9965323Z  try: 2025-01-24T00:44:51.9965744Z  data = yaml.safe_load(yaml_text) 2025-01-24T00:44:51.9966264Z  return data 2025-01-24T00:44:51.9966831Z  except yaml.YAMLError: 2025-01-24T00:44:51.9967361Z  log.exception("Error loading YAML") 2025-01-24T00:44:51.9967882Z  raise 2025-01-24T00:44:51.9968280Z  2025-01-24T00:44:51.9968618Z  2025-01-24T00:44:51.9969234Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> Tuple[str, str]: 2025-01-24T00:44:51.9969975Z  """ 2025-01-24T00:44:51.9970621Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-01-24T00:44:51.9971365Z  2025-01-24T00:44:51.9971907Z  If the issue body contains "---" then the text above that is the settings 2025-01-24T00:44:51.9972669Z  and the text below is the list of opted in users. 2025-01-24T00:44:51.9973224Z  2025-01-24T00:44:51.9973793Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-01-24T00:44:51.9974481Z  """ 2025-01-24T00:44:51.9974954Z  rollout_state_parts = rollout_state.split("---") 2025-01-24T00:44:51.9975561Z  if len(rollout_state_parts) >= 2: 2025-01-24T00:44:51.9976185Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-01-24T00:44:51.9976883Z  else: 2025-01-24T00:44:51.9977435Z  return "", rollout_state 2025-01-24T00:44:51.9977913Z  2025-01-24T00:44:51.9978248Z  2025-01-24T00:44:51.9978646Z class UserOptins(Dict[str, List[str]]): 2025-01-24T00:44:51.9979164Z  """ 2025-01-24T00:44:51.9979712Z  Dictionary of users with a list of features they have opted into 2025-01-24T00:44:51.9980350Z  """ 2025-01-24T00:44:51.9980706Z  2025-01-24T00:44:51.9981040Z  2025-01-24T00:44:51.9981573Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-01-24T00:44:51.9982219Z  """ 2025-01-24T00:44:51.9982950Z  Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-01-24T00:44:51.9983769Z  2025-01-24T00:44:51.9984555Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-01-24T00:44:51.9985538Z  - Example line: "@User1,lf,split_build" 2025-01-24T00:44:51.9986238Z  - A "#" prefix indicates the user is opted out of all experiments 2025-01-24T00:44:51.9987007Z  2025-01-24T00:44:51.9987339Z  2025-01-24T00:44:51.9987675Z  """ 2025-01-24T00:44:51.9988057Z  optins = UserOptins() 2025-01-24T00:44:51.9988585Z  for user in user_optin_text.split("\n"): 2025-01-24T00:44:51.9989152Z  user = user.strip("\r\n\t -") 2025-01-24T00:44:51.9989719Z  if not user or not user.startswith("@"): 2025-01-24T00:44:51.9990274Z  # Not a valid user. Skip 2025-01-24T00:44:51.9990777Z  continue 2025-01-24T00:44:51.9991199Z  2025-01-24T00:44:51.9991537Z  if user: 2025-01-24T00:44:51.9992014Z  usr_name = user.split(",")[0].strip("@") 2025-01-24T00:44:51.9992713Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-01-24T00:44:51.9993341Z  2025-01-24T00:44:51.9993691Z  return optins 2025-01-24T00:44:51.9994247Z  2025-01-24T00:44:51.9994577Z  2025-01-24T00:44:51.9995076Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-01-24T00:44:51.9995690Z  """ 2025-01-24T00:44:51.9996114Z  Check if the experiment name is valid. 2025-01-24T00:44:51.9996781Z  A valid name: 2025-01-24T00:44:51.9997463Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-01-24T00:44:51.9998388Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-01-24T00:44:51.9999086Z  - Cannot contain spaces 2025-01-24T00:44:51.9999571Z  """ 2025-01-24T00:44:51.9999929Z  2025-01-24T00:44:52.0000391Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-01-24T00:44:52.0001113Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-01-24T00:44:52.0001702Z  2025-01-24T00:44:52.0002053Z  if valid: 2025-01-24T00:44:52.0002464Z  return True 2025-01-24T00:44:52.0002881Z  2025-01-24T00:44:52.0003230Z  log.error( 2025-01-24T00:44:52.0004654Z  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-01-24T00:44:52.0006177Z  ) 2025-01-24T00:44:52.0006645Z  return False 2025-01-24T00:44:52.0007055Z  2025-01-24T00:44:52.0007378Z  2025-01-24T00:44:52.0007888Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-01-24T00:44:52.0008517Z  """ 2025-01-24T00:44:52.0009255Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-01-24T00:44:52.0009973Z  """ 2025-01-24T00:44:52.0010339Z  try: 2025-01-24T00:44:52.0010720Z  if settings_text: 2025-01-24T00:44:52.0011477Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-01-24T00:44:52.0012258Z  # for easy reading 2025-01-24T00:44:52.0013072Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-01-24T00:44:52.0013951Z  # the backtick character in shell commands. 2025-01-24T00:44:52.0014565Z  backtick = chr(96) # backtick character 2025-01-24T00:44:52.0015260Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-01-24T00:44:52.0015922Z  settings = load_yaml(settings_text) 2025-01-24T00:44:52.0016558Z  2025-01-24T00:44:52.0017176Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-01-24T00:44:52.0017910Z  experiments = {} 2025-01-24T00:44:52.0018375Z  2025-01-24T00:44:52.0018937Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-01-24T00:44:52.0019695Z  if not is_valid_experiment_name(exp_name): 2025-01-24T00:44:52.0020778Z  # 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-01-24T00:44:52.0021792Z  continue 2025-01-24T00:44:52.0022248Z  2025-01-24T00:44:52.0022614Z  valid_settings = {} 2025-01-24T00:44:52.0023152Z  for setting in exp_settings: 2025-01-24T00:44:52.0023734Z  if setting not in Experiment._fields: 2025-01-24T00:44:52.0024296Z  log.warning( 2025-01-24T00:44:52.0025011Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-01-24T00:44:52.0025855Z  ) 2025-01-24T00:44:52.0026304Z  else: 2025-01-24T00:44:52.0026988Z  valid_settings[setting] = exp_settings[setting] 2025-01-24T00:44:52.0027554Z  2025-01-24T00:44:52.0028025Z  experiments[exp_name] = Experiment(**valid_settings) 2025-01-24T00:44:52.0028662Z  return Settings(experiments) 2025-01-24T00:44:52.0029169Z  2025-01-24T00:44:52.0029524Z  except Exception: 2025-01-24T00:44:52.0030042Z  log.exception("Failed to parse settings") 2025-01-24T00:44:52.0030579Z  2025-01-24T00:44:52.0030935Z  return Settings() 2025-01-24T00:44:52.0031365Z  2025-01-24T00:44:52.0031706Z  2025-01-24T00:44:52.0032154Z def parse_settings(rollout_state: str) -> Settings: 2025-01-24T00:44:52.0032742Z  """ 2025-01-24T00:44:52.0033207Z  Parse settings, if any, from the rollout state. 2025-01-24T00:44:52.0033747Z  2025-01-24T00:44:52.0034283Z  If the issue body contains "---" then the text above that is the settings 2025-01-24T00:44:52.0035048Z  and the text below is the list of opted in users. 2025-01-24T00:44:52.0035597Z  2025-01-24T00:44:52.0036191Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-01-24T00:44:52.0037038Z  """ 2025-01-24T00:44:52.0037614Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:44:52.0038371Z  return parse_settings_from_text(settings_text) 2025-01-24T00:44:52.0038923Z  2025-01-24T00:44:52.0039385Z  2025-01-24T00:44:52.0039840Z def parse_users(rollout_state: str) -> UserOptins: 2025-01-24T00:44:52.0040416Z  """ 2025-01-24T00:44:52.0040841Z  Parse users from the rollout state. 2025-01-24T00:44:52.0041341Z  2025-01-24T00:44:52.0041683Z  """ 2025-01-24T00:44:52.0042244Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:44:52.0042990Z  return parse_user_opt_in_from_text(users_text) 2025-01-24T00:44:52.0043538Z  2025-01-24T00:44:52.0043874Z  2025-01-24T00:44:52.0044493Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:44:52.0045239Z  """ 2025-01-24T00:44:52.0045687Z  Check if a user is opted into an experiment 2025-01-24T00:44:52.0046220Z  """ 2025-01-24T00:44:52.0046824Z  return experiment_name in user_optins.get(user, []) 2025-01-24T00:44:52.0047405Z  2025-01-24T00:44:52.0047735Z  2025-01-24T00:44:52.0048353Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:44:52.0049101Z  """ 2025-01-24T00:44:52.0049584Z  Check if a user explicitly opted out of an experiment 2025-01-24T00:44:52.0050163Z  """ 2025-01-24T00:44:52.0050693Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-01-24T00:44:52.0051386Z  experiment_optout = "-" + experiment_name 2025-01-24T00:44:52.0052044Z  if experiment_optout not in user_optins.get(user, []): 2025-01-24T00:44:52.0052636Z  return False 2025-01-24T00:44:52.0053051Z  2025-01-24T00:44:52.0053516Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-01-24T00:44:52.0054116Z  log.warning( 2025-01-24T00:44:52.0054942Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-01-24T00:44:52.0055778Z  ) 2025-01-24T00:44:52.0056302Z  2025-01-24T00:44:52.0056753Z  return True 2025-01-24T00:44:52.0057156Z  2025-01-24T00:44:52.0057480Z  2025-01-24T00:44:52.0057830Z def get_runner_prefix( 2025-01-24T00:44:52.0058296Z  rollout_state: str, 2025-01-24T00:44:52.0058797Z  workflow_requestors: Iterable[str], 2025-01-24T00:44:52.0059317Z  branch: str, 2025-01-24T00:44:52.0059863Z  eligible_experiments: FrozenSet[str] = frozenset(), 2025-01-24T00:44:52.0060465Z  is_canary: bool = False, 2025-01-24T00:44:52.0060933Z ) -> str: 2025-01-24T00:44:52.0061373Z  settings = parse_settings(rollout_state) 2025-01-24T00:44:52.0061960Z  user_optins = parse_users(rollout_state) 2025-01-24T00:44:52.0062483Z  2025-01-24T00:44:52.0062832Z  fleet_prefix = "" 2025-01-24T00:44:52.0063280Z  prefixes = [] 2025-01-24T00:44:52.0063944Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-01-24T00:44:52.0064881Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-01-24T00:44:52.0065582Z  log.info( 2025-01-24T00:44:52.0066288Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-01-24T00:44:52.0067199Z  ) 2025-01-24T00:44:52.0067658Z  continue 2025-01-24T00:44:52.0068132Z  2025-01-24T00:44:52.0068552Z  if eligible_experiments: 2025-01-24T00:44:52.0069198Z  if experiment_name not in eligible_experiments: 2025-01-24T00:44:52.0069891Z  exp_list = ", ".join(eligible_experiments) 2025-01-24T00:44:52.0070609Z  log.info( 2025-01-24T00:44:52.0071478Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-01-24T00:44:52.0072351Z  ) 2025-01-24T00:44:52.0072824Z  continue 2025-01-24T00:44:52.0073390Z  elif not experiment_settings.default: 2025-01-24T00:44:52.0073974Z  log.info( 2025-01-24T00:44:52.0074746Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-01-24T00:44:52.0075511Z  ) 2025-01-24T00:44:52.0075963Z  continue 2025-01-24T00:44:52.0076515Z  2025-01-24T00:44:52.0076986Z  # Is any workflow_requestor opted out to this experiment? 2025-01-24T00:44:52.0077595Z  opted_out_users = [ 2025-01-24T00:44:52.0078070Z  requestor 2025-01-24T00:44:52.0078559Z  for requestor in workflow_requestors 2025-01-24T00:44:52.0079245Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-01-24T00:44:52.0079869Z  ] 2025-01-24T00:44:52.0080235Z  2025-01-24T00:44:52.0080592Z  if opted_out_users: 2025-01-24T00:44:52.0081061Z  log.info( 2025-01-24T00:44:52.0081703Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-01-24T00:44:52.0082390Z  ) 2025-01-24T00:44:52.0082796Z  continue 2025-01-24T00:44:52.0083204Z  2025-01-24T00:44:52.0083659Z  # Is any workflow_requestor opted in to this experiment? 2025-01-24T00:44:52.0084264Z  opted_in_users = [ 2025-01-24T00:44:52.0084728Z  requestor 2025-01-24T00:44:52.0085222Z  for requestor in workflow_requestors 2025-01-24T00:44:52.0085895Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-01-24T00:44:52.0086606Z  ] 2025-01-24T00:44:52.0087119Z  2025-01-24T00:44:52.0087473Z  enabled = False 2025-01-24T00:44:52.0087933Z  if opted_in_users: 2025-01-24T00:44:52.0088400Z  log.info( 2025-01-24T00:44:52.0089036Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-01-24T00:44:52.0089712Z  ) 2025-01-24T00:44:52.0090124Z  enabled = True 2025-01-24T00:44:52.0090572Z  2025-01-24T00:44:52.0090970Z  elif experiment_settings.rollout_perc: 2025-01-24T00:44:52.0091814Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-01-24T00:44:52.0092746Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-01-24T00:44:52.0093383Z  log.info( 2025-01-24T00:44:52.0094251Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-01-24T00:44:52.0095144Z  ) 2025-01-24T00:44:52.0095580Z  enabled = True 2025-01-24T00:44:52.0096055Z  2025-01-24T00:44:52.0096506Z  if enabled: 2025-01-24T00:44:52.0096980Z  label = experiment_name 2025-01-24T00:44:52.0097542Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-01-24T00:44:52.0098366Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-01-24T00:44:52.0099227Z  # - If it's enabled, then we always list it's prefix first 2025-01-24T00:44:52.0100115Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-01-24T00:44:52.0100787Z  if is_canary: 2025-01-24T00:44:52.0101315Z  label += CANARY_FLEET_SUFFIX 2025-01-24T00:44:52.0101869Z  fleet_prefix = label 2025-01-24T00:44:52.0102354Z  else: 2025-01-24T00:44:52.0102810Z  prefixes.append(label) 2025-01-24T00:44:52.0103314Z  2025-01-24T00:44:52.0103666Z  if len(prefixes) > 1: 2025-01-24T00:44:52.0104135Z  log.error( 2025-01-24T00:44:52.0105177Z  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-01-24T00:44:52.0106269Z  ) 2025-01-24T00:44:52.0106801Z  prefixes = prefixes[:1] 2025-01-24T00:44:52.0107288Z  2025-01-24T00:44:52.0107650Z  # Fleet always comes first 2025-01-24T00:44:52.0108145Z  if fleet_prefix: 2025-01-24T00:44:52.0108626Z  prefixes.insert(0, fleet_prefix) 2025-01-24T00:44:52.0109126Z  2025-01-24T00:44:52.0109583Z  return ".".join(prefixes) + "." if prefixes else "" 2025-01-24T00:44:52.0110140Z  2025-01-24T00:44:52.0110472Z  2025-01-24T00:44:52.0111099Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-01-24T00:44:52.0111849Z  """ 2025-01-24T00:44:52.0112436Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-01-24T00:44:52.0113131Z  2025-01-24T00:44:52.0113702Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-01-24T00:44:52.0114384Z  """ 2025-01-24T00:44:52.0114791Z  gh = get_gh_client(github_token) 2025-01-24T00:44:52.0115344Z  issue = get_issue(gh, repo, issue_num) 2025-01-24T00:44:52.0115994Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-01-24T00:44:52.0116737Z  2025-01-24T00:44:52.0117073Z  2025-01-24T00:44:52.0117795Z def download_json(url: str, headers: Dict[str, str], num_retries: int = 3) -> Any: 2025-01-24T00:44:52.0118539Z  for _ in range(num_retries): 2025-01-24T00:44:52.0119021Z  try: 2025-01-24T00:44:52.0119463Z  req = Request(url=url, headers=headers) 2025-01-24T00:44:52.0120126Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-01-24T00:44:52.0120775Z  return json.loads(content) 2025-01-24T00:44:52.0121299Z  except Exception as e: 2025-01-24T00:44:52.0121858Z  log.warning(f"Could not download {url}: {e}") 2025-01-24T00:44:52.0122403Z  2025-01-24T00:44:52.0122972Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-01-24T00:44:52.0123675Z  return {} 2025-01-24T00:44:52.0124074Z  2025-01-24T00:44:52.0124405Z  2025-01-24T00:44:52.0124767Z @lru_cache(maxsize=None) 2025-01-24T00:44:52.0125489Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> Dict[str, Any]: 2025-01-24T00:44:52.0126222Z  """ 2025-01-24T00:44:52.0126743Z  Dynamically get PR information 2025-01-24T00:44:52.0127240Z  """ 2025-01-24T00:44:52.0127764Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-01-24T00:44:52.0128381Z  headers = { 2025-01-24T00:44:52.0128870Z  "Accept": "application/vnd.github.v3+json", 2025-01-24T00:44:52.0129478Z  "Authorization": f"token {github_token}", 2025-01-24T00:44:52.0129998Z  } 2025-01-24T00:44:52.0130444Z  json_response: Dict[str, Any] = download_json( 2025-01-24T00:44:52.0131172Z  url=f"{github_api}/issues/{pr_number}", 2025-01-24T00:44:52.0131726Z  headers=headers, 2025-01-24T00:44:52.0132180Z  ) 2025-01-24T00:44:52.0132538Z  2025-01-24T00:44:52.0132891Z  if not json_response: 2025-01-24T00:44:52.0133494Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-01-24T00:44:52.0134110Z  return {} 2025-01-24T00:44:52.0134512Z  2025-01-24T00:44:52.0134871Z  return json_response 2025-01-24T00:44:52.0135328Z  2025-01-24T00:44:52.0135653Z  2025-01-24T00:44:52.0136239Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> Set[str]: 2025-01-24T00:44:52.0137061Z  """ 2025-01-24T00:44:52.0137601Z  Dynamically get the latest list of labels from the pull request 2025-01-24T00:44:52.0138239Z  """ 2025-01-24T00:44:52.0138747Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-01-24T00:44:52.0139344Z  return { 2025-01-24T00:44:52.0139947Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-01-24T00:44:52.0140627Z  } 2025-01-24T00:44:52.0140981Z  2025-01-24T00:44:52.0141315Z  2025-01-24T00:44:52.0141666Z def main() -> None: 2025-01-24T00:44:52.0142151Z  args = parse_args() 2025-01-24T00:44:52.0142600Z  2025-01-24T00:44:52.0143015Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-01-24T00:44:52.0143540Z  2025-01-24T00:44:52.0143906Z  # Check if the PR is opt-out 2025-01-24T00:44:52.0144408Z  if args.pr_number: 2025-01-24T00:44:52.0145084Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-01-24T00:44:52.0145816Z  if OPT_OUT_LABEL in labels: 2025-01-24T00:44:52.0146316Z  log.info( 2025-01-24T00:44:52.0147142Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-01-24T00:44:52.0148025Z  ) 2025-01-24T00:44:52.0148619Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:44:52.0149281Z  sys.exit() 2025-01-24T00:44:52.0149717Z  2025-01-24T00:44:52.0150053Z  try: 2025-01-24T00:44:52.0150508Z  rollout_state = get_rollout_state_from_issue( 2025-01-24T00:44:52.0151228Z  args.github_token, args.github_issue_repo, args.github_issue 2025-01-24T00:44:52.0151849Z  ) 2025-01-24T00:44:52.0152215Z  2025-01-24T00:44:52.0152605Z  username = get_potential_pr_author( 2025-01-24T00:44:52.0153140Z  args.github_token, 2025-01-24T00:44:52.0153633Z  args.github_repo, 2025-01-24T00:44:52.0154121Z  args.github_actor, 2025-01-24T00:44:52.0154624Z  args.github_ref_type, 2025-01-24T00:44:52.0155130Z  args.github_branch, 2025-01-24T00:44:52.0155597Z  ) 2025-01-24T00:44:52.0155964Z  2025-01-24T00:44:52.0156568Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-01-24T00:44:52.0157157Z  2025-01-24T00:44:52.0157565Z  runner_label_prefix = get_runner_prefix( 2025-01-24T00:44:52.0158107Z  rollout_state, 2025-01-24T00:44:52.0158619Z  (args.github_issue_owner, username), 2025-01-24T00:44:52.0159162Z  args.github_branch, 2025-01-24T00:44:52.0159686Z  args.eligible_experiments, 2025-01-24T00:44:52.0160195Z  is_canary, 2025-01-24T00:44:52.0160629Z  ) 2025-01-24T00:44:52.0160998Z  2025-01-24T00:44:52.0161351Z  except Exception as e: 2025-01-24T00:44:52.0161942Z  log.error( 2025-01-24T00:44:52.0162649Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-01-24T00:44:52.0163376Z  ) 2025-01-24T00:44:52.0163743Z  2025-01-24T00:44:52.0164264Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:44:52.0164898Z  2025-01-24T00:44:52.0165239Z  2025-01-24T00:44:52.0165590Z if __name__ == "__main__": 2025-01-24T00:44:52.0166038Z  main() 2025-01-24T00:44:52.0166520Z  2025-01-24T00:44:52.0166887Z EOF 2025-01-24T00:44:52.0167245Z  2025-01-24T00:44:52.0167635Z cat runner_determinator.py 2025-01-24T00:44:52.0476577Z shell: /usr/bin/bash -e {0} 2025-01-24T00:44:52.0477544Z env: 2025-01-24T00:44:52.0478220Z GITHUB_TOKEN: *** 2025-01-24T00:44:52.0478643Z ISSUE_NUMBER: 5132 2025-01-24T00:44:52.0479106Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:44:52.0479636Z ISSUE_OWNER: 2025-01-24T00:44:52.0480036Z CHECK_EXPERIMENTS: 2025-01-24T00:44:52.0480461Z PR_NUMBER: 2025-01-24T00:44:52.0480851Z ##[endgroup] 2025-01-24T00:44:52.0689353Z # flake8: noqa: G004 2025-01-24T00:44:52.0689702Z 2025-01-24T00:44:52.0690131Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-01-24T00:44:52.0691040Z # must be kept in sync. You can do it easily by running the following command: 2025-01-24T00:44:52.0691795Z # python .github/scripts/update_runner_determinator.py 2025-01-24T00:44:52.0692210Z 2025-01-24T00:44:52.0692363Z """ 2025-01-24T00:44:52.0692897Z This runner determinator is used to determine which set of runners to run a 2025-01-24T00:44:52.0693797Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-01-24T00:44:52.0694653Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-01-24T00:44:52.0695896Z of which runners should be used to run which job. 2025-01-24T00:44:52.0696714Z 2025-01-24T00:44:52.0697237Z The configuration has two parts, the settings and a list of opted-in users, 2025-01-24T00:44:52.0698953Z separated by a line containing "---". If the line is not present, the 2025-01-24T00:44:52.0700314Z settings are considered to be empty with only the second part, the user 2025-01-24T00:44:52.0701439Z list, defined. 2025-01-24T00:44:52.0701793Z 2025-01-24T00:44:52.0764697Z The first part is a YAML block that defines the rollout settings. This can be 2025-01-24T00:44:52.0766756Z used to define any settings that are needed to determine which runners to use. 2025-01-24T00:44:52.0767671Z It's fields are defined by the RolloutSettings class below. 2025-01-24T00:44:52.0768101Z 2025-01-24T00:44:52.0768472Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-01-24T00:44:52.0769311Z The user list is also a comma separated list of additional features or 2025-01-24T00:44:52.0770042Z experiments which the user could be opted in to. 2025-01-24T00:44:52.0770440Z 2025-01-24T00:44:52.0770628Z The user list has the following rules: 2025-01-24T00:44:52.0770975Z 2025-01-24T00:44:52.0771283Z - Users are GitHub usernames, which must start with the @ prefix 2025-01-24T00:44:52.0772103Z - Each user is also a comma-separated list of features/experiments to enable 2025-01-24T00:44:52.0772842Z - A "#" prefix opts the user out of all experiments 2025-01-24T00:44:52.0773223Z 2025-01-24T00:44:52.0773387Z Example config: 2025-01-24T00:44:52.0773829Z # A list of experiments that can be opted into. 2025-01-24T00:44:52.0774467Z # This defines the behavior they'll induce when opted into. 2025-01-24T00:44:52.0775047Z # Expected syntax is: 2025-01-24T00:44:52.0775673Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-01-24T00:44:52.0777551Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-01-24T00:44:52.0778898Z 2025-01-24T00:44:52.0779191Z experiments: 2025-01-24T00:44:52.0779809Z lf: 2025-01-24T00:44:52.0780389Z rollout_percent: 25 2025-01-24T00:44:52.0781123Z all_branches: false 2025-01-24T00:44:52.0781824Z default: true 2025-01-24T00:44:52.0782459Z --- 2025-01-24T00:44:52.0782781Z 2025-01-24T00:44:52.0783044Z # Opt-ins: 2025-01-24T00:44:52.0783951Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-01-24T00:44:52.0785323Z # and specifying experiments to enable in a comma-separated list. 2025-01-24T00:44:52.0786710Z # To always opt out of an experiment, prefix it with a "-". 2025-01-24T00:44:52.0787769Z # Experiments should be from the above list. 2025-01-24T00:44:52.0788381Z 2025-01-24T00:44:52.0788652Z @User1,-lf,split_build 2025-01-24T00:44:52.0789316Z @User2,lf 2025-01-24T00:44:52.0789882Z @User3,split_build 2025-01-24T00:44:52.0790500Z """ 2025-01-24T00:44:52.0790808Z 2025-01-24T00:44:52.0791085Z import json 2025-01-24T00:44:52.0791672Z import logging 2025-01-24T00:44:52.0792271Z import os 2025-01-24T00:44:52.0792632Z import random 2025-01-24T00:44:52.0792987Z import re 2025-01-24T00:44:52.0793325Z import sys 2025-01-24T00:44:52.0793700Z from argparse import ArgumentParser 2025-01-24T00:44:52.0794186Z from functools import lru_cache 2025-01-24T00:44:52.0794693Z from logging import LogRecord 2025-01-24T00:44:52.0795342Z from typing import Any, Dict, FrozenSet, Iterable, List, NamedTuple, Set, Tuple 2025-01-24T00:44:52.0796069Z from urllib.request import Request, urlopen 2025-01-24T00:44:52.0796689Z 2025-01-24T00:44:52.0796877Z import yaml 2025-01-24T00:44:52.0797250Z from github import Auth, Github 2025-01-24T00:44:52.0797711Z from github.Issue import Issue 2025-01-24T00:44:52.0798012Z 2025-01-24T00:44:52.0798019Z 2025-01-24T00:44:52.0798220Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-01-24T00:44:52.0798896Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-01-24T00:44:52.0799711Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-01-24T00:44:52.0800238Z 2025-01-24T00:44:52.0800674Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-01-24T00:44:52.0801226Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-01-24T00:44:52.0801702Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-01-24T00:44:52.0802211Z OPT_OUT_LABEL = "no-runner-experiments" 2025-01-24T00:44:52.0802545Z 2025-01-24T00:44:52.0802725Z SETTING_EXPERIMENTS = "experiments" 2025-01-24T00:44:52.0803036Z 2025-01-24T00:44:52.0803212Z LF_FLEET_EXPERIMENT = "lf" 2025-01-24T00:44:52.0803684Z CANARY_FLEET_SUFFIX = ".c" 2025-01-24T00:44:52.0803948Z 2025-01-24T00:44:52.0803954Z 2025-01-24T00:44:52.0804127Z class Experiment(NamedTuple): 2025-01-24T00:44:52.0804576Z rollout_perc: float = ( 2025-01-24T00:44:52.0805172Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-01-24T00:44:52.0805825Z ) 2025-01-24T00:44:52.0806165Z all_branches: bool = ( 2025-01-24T00:44:52.0806926Z False # If True, the experiment is also enabled on the exception branches 2025-01-24T00:44:52.0807571Z ) 2025-01-24T00:44:52.0807917Z default: bool = ( 2025-01-24T00:44:52.0808479Z True # If True, the experiment is enabled by default for all queries 2025-01-24T00:44:52.0809084Z ) 2025-01-24T00:44:52.0809291Z 2025-01-24T00:44:52.0809461Z # Add more fields as needed 2025-01-24T00:44:52.0809750Z 2025-01-24T00:44:52.0809756Z 2025-01-24T00:44:52.0809937Z class Settings(NamedTuple): 2025-01-24T00:44:52.0810349Z """ 2025-01-24T00:44:52.0810771Z Settings for the experiments that can be opted into. 2025-01-24T00:44:52.0811310Z """ 2025-01-24T00:44:52.0811490Z 2025-01-24T00:44:52.0811681Z experiments: Dict[str, Experiment] = {} 2025-01-24T00:44:52.0812027Z 2025-01-24T00:44:52.0812034Z 2025-01-24T00:44:52.0812224Z class ColorFormatter(logging.Formatter): 2025-01-24T00:44:52.0812830Z """Color codes the log messages based on the log level""" 2025-01-24T00:44:52.0813623Z 2025-01-24T00:44:52.0813928Z COLORS = { 2025-01-24T00:44:52.0814544Z "WARNING": "\033[33m", # Yellow 2025-01-24T00:44:52.0815363Z "ERROR": "\033[31m", # Red 2025-01-24T00:44:52.0816141Z "CRITICAL": "\033[31m", # Red 2025-01-24T00:44:52.0817170Z "INFO": "\033[0m", # Reset 2025-01-24T00:44:52.0817923Z "DEBUG": "\033[0m", # Reset 2025-01-24T00:44:52.0818627Z } 2025-01-24T00:44:52.0818923Z 2025-01-24T00:44:52.0819273Z def format(self, record: LogRecord) -> str: 2025-01-24T00:44:52.0820426Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-01-24T00:44:52.0821453Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-01-24T00:44:52.0822006Z return super().format(record) 2025-01-24T00:44:52.0822327Z 2025-01-24T00:44:52.0822333Z 2025-01-24T00:44:52.0822513Z handler = logging.StreamHandler() 2025-01-24T00:44:52.0823196Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-01-24T00:44:52.0823891Z 2025-01-24T00:44:52.0824190Z log = logging.getLogger(os.path.basename(__file__)) 2025-01-24T00:44:52.0824749Z log.addHandler(handler) 2025-01-24T00:44:52.0825167Z log.setLevel(logging.INFO) 2025-01-24T00:44:52.0825436Z 2025-01-24T00:44:52.0825443Z 2025-01-24T00:44:52.0825674Z def set_github_output(key: str, value: str) -> None: 2025-01-24T00:44:52.0826200Z """ 2025-01-24T00:44:52.0826887Z Defines outputs of the github action that invokes this script 2025-01-24T00:44:52.0827490Z """ 2025-01-24T00:44:52.0828233Z if not GITHUB_OUTPUT: 2025-01-24T00:44:52.0829797Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-01-24T00:44:52.0831581Z log.warning( 2025-01-24T00:44:52.0832881Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-01-24T00:44:52.0834271Z ) 2025-01-24T00:44:52.0849579Z print(f"::set-output name={key}::{value}") 2025-01-24T00:44:52.0850453Z return 2025-01-24T00:44:52.0851115Z 2025-01-24T00:44:52.0851444Z with open(GITHUB_OUTPUT, "a") as f: 2025-01-24T00:44:52.0852367Z log.info(f"Setting output: {key}='{value}'") 2025-01-24T00:44:52.0853231Z f.write(f"{key}={value}\n") 2025-01-24T00:44:52.0853742Z 2025-01-24T00:44:52.0853755Z 2025-01-24T00:44:52.0854212Z def _str_comma_separated_to_set(value: str) -> FrozenSet[str]: 2025-01-24T00:44:52.0855316Z return frozenset( 2025-01-24T00:44:52.0856374Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-01-24T00:44:52.0857737Z ) 2025-01-24T00:44:52.0858025Z 2025-01-24T00:44:52.0858032Z 2025-01-24T00:44:52.0858211Z def parse_args() -> Any: 2025-01-24T00:44:52.0858757Z parser = ArgumentParser("Get dynamic rollout settings") 2025-01-24T00:44:52.0859578Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-01-24T00:44:52.0860305Z parser.add_argument( 2025-01-24T00:44:52.0860730Z "--github-issue-repo", 2025-01-24T00:44:52.0861153Z type=str, 2025-01-24T00:44:52.0861527Z required=False, 2025-01-24T00:44:52.0862123Z default="pytorch/test-infra", 2025-01-24T00:44:52.0862631Z help="GitHub repo to get the issue", 2025-01-24T00:44:52.0863107Z ) 2025-01-24T00:44:52.0863453Z parser.add_argument( 2025-01-24T00:44:52.0863854Z "--github-repo", 2025-01-24T00:44:52.0864248Z type=str, 2025-01-24T00:44:52.0864608Z required=True, 2025-01-24T00:44:52.0865030Z help="GitHub repo where CI is running", 2025-01-24T00:44:52.0865517Z ) 2025-01-24T00:44:52.0865851Z parser.add_argument( 2025-01-24T00:44:52.0866676Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-01-24T00:44:52.0867359Z ) 2025-01-24T00:44:52.0867702Z parser.add_argument( 2025-01-24T00:44:52.0868467Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-01-24T00:44:52.0869136Z ) 2025-01-24T00:44:52.0869708Z parser.add_argument( 2025-01-24T00:44:52.0870343Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-01-24T00:44:52.0871012Z ) 2025-01-24T00:44:52.0871422Z parser.add_argument( 2025-01-24T00:44:52.0872323Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-01-24T00:44:52.0873288Z ) 2025-01-24T00:44:52.0873671Z parser.add_argument( 2025-01-24T00:44:52.0874096Z "--github-ref-type", 2025-01-24T00:44:52.0874570Z type=str, 2025-01-24T00:44:52.0874964Z required=True, 2025-01-24T00:44:52.0875409Z help="Current GitHub ref type, branch or tag", 2025-01-24T00:44:52.0875930Z ) 2025-01-24T00:44:52.0876263Z parser.add_argument( 2025-01-24T00:44:52.0876935Z "--eligible-experiments", 2025-01-24T00:44:52.0877427Z type=_str_comma_separated_to_set, 2025-01-24T00:44:52.0877903Z required=False, 2025-01-24T00:44:52.0878292Z default="", 2025-01-24T00:44:52.0879083Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-01-24T00:44:52.0879944Z ) 2025-01-24T00:44:52.0880279Z parser.add_argument( 2025-01-24T00:44:52.0880685Z "--pr-number", 2025-01-24T00:44:52.0881072Z type=str, 2025-01-24T00:44:52.0881437Z required=False, 2025-01-24T00:44:52.0881822Z default="", 2025-01-24T00:44:52.0882258Z help="the optional PR number where this is run", 2025-01-24T00:44:52.0882785Z ) 2025-01-24T00:44:52.0882966Z 2025-01-24T00:44:52.0883138Z return parser.parse_args() 2025-01-24T00:44:52.0883437Z 2025-01-24T00:44:52.0883443Z 2025-01-24T00:44:52.0883653Z def get_gh_client(github_token: str) -> Github: 2025-01-24T00:44:52.0884198Z auth = Auth.Token(github_token) 2025-01-24T00:44:52.0884678Z return Github(auth=auth) 2025-01-24T00:44:52.0884957Z 2025-01-24T00:44:52.0884964Z 2025-01-24T00:44:52.0885244Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: 2025-01-24T00:44:52.0886018Z repo = gh.get_repo(repo) 2025-01-24T00:44:52.0886695Z return repo.get_issue(number=issue_num) 2025-01-24T00:44:52.0887052Z 2025-01-24T00:44:52.0887058Z 2025-01-24T00:44:52.0887239Z def get_potential_pr_author( 2025-01-24T00:44:52.0887844Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-01-24T00:44:52.0888482Z ) -> str: 2025-01-24T00:44:52.0888952Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-01-24T00:44:52.0889707Z # Fetch the actual username from the original PR. The PR number is 2025-01-24T00:44:52.0890403Z # embedded in the tag name: ciflow// 2025-01-24T00:44:52.0890787Z 2025-01-24T00:44:52.0890965Z gh = get_gh_client(github_token) 2025-01-24T00:44:52.0891277Z 2025-01-24T00:44:52.0891533Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-01-24T00:44:52.0892111Z split_tag = ref_name.split("/") 2025-01-24T00:44:52.0892577Z if ( 2025-01-24T00:44:52.0892935Z len(split_tag) == 3 2025-01-24T00:44:52.0893381Z and split_tag[0] == "ciflow" 2025-01-24T00:44:52.0893872Z and split_tag[2].isnumeric() 2025-01-24T00:44:52.0894328Z ): 2025-01-24T00:44:52.0894859Z pr_number = split_tag[2] 2025-01-24T00:44:52.0896022Z try: 2025-01-24T00:44:52.0896924Z repository = gh.get_repo(repo) 2025-01-24T00:44:52.0897923Z pull = repository.get_pull(number=int(pr_number)) 2025-01-24T00:44:52.0898839Z except Exception as e: 2025-01-24T00:44:52.0899607Z raise Exception( # noqa: TRY002 2025-01-24T00:44:52.0900723Z f"issue with pull request {pr_number} from repo {repository}" 2025-01-24T00:44:52.0901851Z ) from e 2025-01-24T00:44:52.0902819Z return pull.user.login 2025-01-24T00:44:52.0903856Z # In all other cases, return the original input username 2025-01-24T00:44:52.0904815Z return username 2025-01-24T00:44:52.0905221Z 2025-01-24T00:44:52.0905232Z 2025-01-24T00:44:52.0905573Z def is_exception_branch(branch: str) -> bool: 2025-01-24T00:44:52.0906199Z """ 2025-01-24T00:44:52.0907091Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-01-24T00:44:52.0907830Z """ 2025-01-24T00:44:52.0908352Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-01-24T00:44:52.0908857Z 2025-01-24T00:44:52.0908863Z 2025-01-24T00:44:52.0909044Z def load_yaml(yaml_text: str) -> Any: 2025-01-24T00:44:52.0909504Z try: 2025-01-24T00:44:52.0909851Z data = yaml.safe_load(yaml_text) 2025-01-24T00:44:52.0910321Z return data 2025-01-24T00:44:52.0910691Z except yaml.YAMLError: 2025-01-24T00:44:52.0911138Z log.exception("Error loading YAML") 2025-01-24T00:44:52.0911624Z raise 2025-01-24T00:44:52.0911822Z 2025-01-24T00:44:52.0911830Z 2025-01-24T00:44:52.0912222Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> Tuple[str, str]: 2025-01-24T00:44:52.0912912Z """ 2025-01-24T00:44:52.0913486Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-01-24T00:44:52.0914046Z 2025-01-24T00:44:52.0914369Z If the issue body contains "---" then the text above that is the settings 2025-01-24T00:44:52.0915063Z and the text below is the list of opted in users. 2025-01-24T00:44:52.0915450Z 2025-01-24T00:44:52.0915798Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-01-24T00:44:52.0916669Z """ 2025-01-24T00:44:52.0917087Z rollout_state_parts = rollout_state.split("---") 2025-01-24T00:44:52.0917638Z if len(rollout_state_parts) >= 2: 2025-01-24T00:44:52.0918202Z return rollout_state_parts[0], rollout_state_parts[1] 2025-01-24T00:44:52.0918753Z else: 2025-01-24T00:44:52.0919099Z return "", rollout_state 2025-01-24T00:44:52.0919381Z 2025-01-24T00:44:52.0919387Z 2025-01-24T00:44:52.0919770Z class UserOptins(Dict[str, List[str]]): 2025-01-24T00:44:52.0920248Z """ 2025-01-24T00:44:52.0920734Z Dictionary of users with a list of features they have opted into 2025-01-24T00:44:52.0921346Z """ 2025-01-24T00:44:52.0921524Z 2025-01-24T00:44:52.0921530Z 2025-01-24T00:44:52.0921844Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-01-24T00:44:52.0922459Z """ 2025-01-24T00:44:52.0923121Z Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-01-24T00:44:52.0923767Z 2025-01-24T00:44:52.0924353Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-01-24T00:44:52.0925274Z - Example line: "@User1,lf,split_build" 2025-01-24T00:44:52.0925918Z - A "#" prefix indicates the user is opted out of all experiments 2025-01-24T00:44:52.0926365Z 2025-01-24T00:44:52.0926371Z 2025-01-24T00:44:52.0926711Z """ 2025-01-24T00:44:52.0927072Z optins = UserOptins() 2025-01-24T00:44:52.0927680Z for user in user_optin_text.split("\n"): 2025-01-24T00:44:52.0928209Z user = user.strip("\r\n\t -") 2025-01-24T00:44:52.0928800Z if not user or not user.startswith("@"): 2025-01-24T00:44:52.0929401Z # Not a valid user. Skip 2025-01-24T00:44:52.0929854Z continue 2025-01-24T00:44:52.0930080Z 2025-01-24T00:44:52.0930228Z if user: 2025-01-24T00:44:52.0930723Z usr_name = user.split(",")[0].strip("@") 2025-01-24T00:44:52.0931371Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-01-24T00:44:52.0931824Z 2025-01-24T00:44:52.0931975Z return optins 2025-01-24T00:44:52.0932200Z 2025-01-24T00:44:52.0932206Z 2025-01-24T00:44:52.0932472Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-01-24T00:44:52.0933171Z """ 2025-01-24T00:44:52.0933538Z Check if the experiment name is valid. 2025-01-24T00:44:52.0934029Z A valid name: 2025-01-24T00:44:52.0934620Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-01-24T00:44:52.0935487Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-01-24T00:44:52.0936155Z - Cannot contain spaces 2025-01-24T00:44:52.0936781Z """ 2025-01-24T00:44:52.0936967Z 2025-01-24T00:44:52.0937209Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-01-24T00:44:52.0937860Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-01-24T00:44:52.0938266Z 2025-01-24T00:44:52.0938417Z if valid: 2025-01-24T00:44:52.0938762Z return True 2025-01-24T00:44:52.0938985Z 2025-01-24T00:44:52.0939136Z log.error( 2025-01-24T00:44:52.0940486Z 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-01-24T00:44:52.0941937Z ) 2025-01-24T00:44:52.0942254Z return False 2025-01-24T00:44:52.0942472Z 2025-01-24T00:44:52.0942477Z 2025-01-24T00:44:52.0942759Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-01-24T00:44:52.0943341Z """ 2025-01-24T00:44:52.0943877Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-01-24T00:44:52.0944540Z """ 2025-01-24T00:44:52.0944868Z try: 2025-01-24T00:44:52.0945198Z if settings_text: 2025-01-24T00:44:52.0945860Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-01-24T00:44:52.0946774Z # for easy reading 2025-01-24T00:44:52.0947511Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-01-24T00:44:52.0948343Z # the backtick character in shell commands. 2025-01-24T00:44:52.0948899Z backtick = chr(96) # backtick character 2025-01-24T00:44:52.0949674Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-01-24T00:44:52.0950281Z settings = load_yaml(settings_text) 2025-01-24T00:44:52.0950621Z 2025-01-24T00:44:52.0951000Z # For now we just load experiments. We can expand this if/when we add more settings 2025-01-24T00:44:52.0951688Z experiments = {} 2025-01-24T00:44:52.0951957Z 2025-01-24T00:44:52.0952286Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-01-24T00:44:52.0952980Z if not is_valid_experiment_name(exp_name): 2025-01-24T00:44:52.0954011Z # 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-01-24T00:44:52.0954989Z continue 2025-01-24T00:44:52.0955260Z 2025-01-24T00:44:52.0955427Z valid_settings = {} 2025-01-24T00:44:52.0955907Z for setting in exp_settings: 2025-01-24T00:44:52.0956687Z if setting not in Experiment._fields: 2025-01-24T00:44:52.0957220Z log.warning( 2025-01-24T00:44:52.0957870Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-01-24T00:44:52.0958527Z ) 2025-01-24T00:44:52.0958959Z else: 2025-01-24T00:44:52.0959512Z valid_settings[setting] = exp_settings[setting] 2025-01-24T00:44:52.0959910Z 2025-01-24T00:44:52.0960160Z experiments[exp_name] = Experiment(**valid_settings) 2025-01-24T00:44:52.0960749Z return Settings(experiments) 2025-01-24T00:44:52.0961074Z 2025-01-24T00:44:52.0961239Z except Exception: 2025-01-24T00:44:52.0961678Z log.exception("Failed to parse settings") 2025-01-24T00:44:52.0962042Z 2025-01-24T00:44:52.0962339Z return Settings() 2025-01-24T00:44:52.0962587Z 2025-01-24T00:44:52.0962593Z 2025-01-24T00:44:52.0962832Z def parse_settings(rollout_state: str) -> Settings: 2025-01-24T00:44:52.0963359Z """ 2025-01-24T00:44:52.0963748Z Parse settings, if any, from the rollout state. 2025-01-24T00:44:52.0964117Z 2025-01-24T00:44:52.0964445Z If the issue body contains "---" then the text above that is the settings 2025-01-24T00:44:52.0965138Z and the text below is the list of opted in users. 2025-01-24T00:44:52.0965518Z 2025-01-24T00:44:52.0965890Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-01-24T00:44:52.0966735Z """ 2025-01-24T00:44:52.0967282Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:44:52.0967985Z return parse_settings_from_text(settings_text) 2025-01-24T00:44:52.0968360Z 2025-01-24T00:44:52.0968366Z 2025-01-24T00:44:52.0968594Z def parse_users(rollout_state: str) -> UserOptins: 2025-01-24T00:44:52.0969111Z """ 2025-01-24T00:44:52.0969464Z Parse users from the rollout state. 2025-01-24T00:44:52.0969802Z 2025-01-24T00:44:52.0969954Z """ 2025-01-24T00:44:52.0970455Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-01-24T00:44:52.0971120Z return parse_user_opt_in_from_text(users_text) 2025-01-24T00:44:52.0971486Z 2025-01-24T00:44:52.0971492Z 2025-01-24T00:44:52.0972259Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:44:52.0973463Z """ 2025-01-24T00:44:52.0974102Z Check if a user is opted into an experiment 2025-01-24T00:44:52.0974956Z """ 2025-01-24T00:44:52.0975564Z return experiment_name in user_optins.get(user, []) 2025-01-24T00:44:52.0975957Z 2025-01-24T00:44:52.0975963Z 2025-01-24T00:44:52.0976355Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-01-24T00:44:52.0977192Z """ 2025-01-24T00:44:52.0977624Z Check if a user explicitly opted out of an experiment 2025-01-24T00:44:52.0978160Z """ 2025-01-24T00:44:52.0978625Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-01-24T00:44:52.0979459Z experiment_optout = "-" + experiment_name 2025-01-24T00:44:52.0980050Z if experiment_optout not in user_optins.get(user, []): 2025-01-24T00:44:52.0980605Z return False 2025-01-24T00:44:52.0980837Z 2025-01-24T00:44:52.0981090Z if is_user_opted_in(user, user_optins, experiment_name): 2025-01-24T00:44:52.0981641Z log.warning( 2025-01-24T00:44:52.0982382Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-01-24T00:44:52.0983199Z ) 2025-01-24T00:44:52.0983385Z 2025-01-24T00:44:52.0983534Z return True 2025-01-24T00:44:52.0983750Z 2025-01-24T00:44:52.0983755Z 2025-01-24T00:44:52.0983915Z def get_runner_prefix( 2025-01-24T00:44:52.0984322Z rollout_state: str, 2025-01-24T00:44:52.0984741Z workflow_requestors: Iterable[str], 2025-01-24T00:44:52.0985222Z branch: str, 2025-01-24T00:44:52.0985668Z eligible_experiments: FrozenSet[str] = frozenset(), 2025-01-24T00:44:52.0986225Z is_canary: bool = False, 2025-01-24T00:44:52.0986766Z ) -> str: 2025-01-24T00:44:52.0987153Z settings = parse_settings(rollout_state) 2025-01-24T00:44:52.0987678Z user_optins = parse_users(rollout_state) 2025-01-24T00:44:52.0988021Z 2025-01-24T00:44:52.0988183Z fleet_prefix = "" 2025-01-24T00:44:52.0988576Z prefixes = [] 2025-01-24T00:44:52.0989151Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-01-24T00:44:52.0990037Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-01-24T00:44:52.0990690Z log.info( 2025-01-24T00:44:52.0991304Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-01-24T00:44:52.0992006Z ) 2025-01-24T00:44:52.0992469Z continue 2025-01-24T00:44:52.0992706Z 2025-01-24T00:44:52.0992881Z if eligible_experiments: 2025-01-24T00:44:52.0993396Z if experiment_name not in eligible_experiments: 2025-01-24T00:44:52.0993969Z exp_list = ", ".join(eligible_experiments) 2025-01-24T00:44:52.0994476Z log.info( 2025-01-24T00:44:52.0995199Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-01-24T00:44:52.0995976Z ) 2025-01-24T00:44:52.0996339Z continue 2025-01-24T00:44:52.0996870Z elif not experiment_settings.default: 2025-01-24T00:44:52.0997364Z log.info( 2025-01-24T00:44:52.0997991Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-01-24T00:44:52.0998671Z ) 2025-01-24T00:44:52.0999017Z continue 2025-01-24T00:44:52.0999244Z 2025-01-24T00:44:52.0999504Z # Is any workflow_requestor opted out to this experiment? 2025-01-24T00:44:52.1000064Z opted_out_users = [ 2025-01-24T00:44:52.1000478Z requestor 2025-01-24T00:44:52.1000899Z for requestor in workflow_requestors 2025-01-24T00:44:52.1001514Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-01-24T00:44:52.1002090Z ] 2025-01-24T00:44:52.1002274Z 2025-01-24T00:44:52.1002441Z if opted_out_users: 2025-01-24T00:44:52.1002848Z log.info( 2025-01-24T00:44:52.1003446Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-01-24T00:44:52.1004094Z ) 2025-01-24T00:44:52.1004431Z continue 2025-01-24T00:44:52.1004657Z 2025-01-24T00:44:52.1004905Z # Is any workflow_requestor opted in to this experiment? 2025-01-24T00:44:52.1005470Z opted_in_users = [ 2025-01-24T00:44:52.1005878Z requestor 2025-01-24T00:44:52.1006293Z for requestor in workflow_requestors 2025-01-24T00:44:52.1007020Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-01-24T00:44:52.1007593Z ] 2025-01-24T00:44:52.1007911Z 2025-01-24T00:44:52.1008077Z enabled = False 2025-01-24T00:44:52.1008482Z if opted_in_users: 2025-01-24T00:44:52.1008885Z log.info( 2025-01-24T00:44:52.1009437Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-01-24T00:44:52.1010072Z ) 2025-01-24T00:44:52.1010425Z enabled = True 2025-01-24T00:44:52.1010691Z 2025-01-24T00:44:52.1010891Z elif experiment_settings.rollout_perc: 2025-01-24T00:44:52.1011660Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-01-24T00:44:52.1012518Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-01-24T00:44:52.1013117Z log.info( 2025-01-24T00:44:52.1014186Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-01-24T00:44:52.1015637Z ) 2025-01-24T00:44:52.1016226Z enabled = True 2025-01-24T00:44:52.1016875Z 2025-01-24T00:44:52.1017150Z if enabled: 2025-01-24T00:44:52.1017804Z label = experiment_name 2025-01-24T00:44:52.1018650Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-01-24T00:44:52.1019977Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-01-24T00:44:52.1021381Z # - If it's enabled, then we always list it's prefix first 2025-01-24T00:44:52.1022562Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-01-24T00:44:52.1023612Z if is_canary: 2025-01-24T00:44:52.1024360Z label += CANARY_FLEET_SUFFIX 2025-01-24T00:44:52.1025227Z fleet_prefix = label 2025-01-24T00:44:52.1026219Z else: 2025-01-24T00:44:52.1027290Z prefixes.append(label) 2025-01-24T00:44:52.1027870Z 2025-01-24T00:44:52.1028158Z if len(prefixes) > 1: 2025-01-24T00:44:52.1028833Z log.error( 2025-01-24T00:44:52.1030417Z 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-01-24T00:44:52.1032196Z ) 2025-01-24T00:44:52.1032797Z prefixes = prefixes[:1] 2025-01-24T00:44:52.1033271Z 2025-01-24T00:44:52.1033560Z # Fleet always comes first 2025-01-24T00:44:52.1034301Z if fleet_prefix: 2025-01-24T00:44:52.1034981Z prefixes.insert(0, fleet_prefix) 2025-01-24T00:44:52.1035539Z 2025-01-24T00:44:52.1035923Z return ".".join(prefixes) + "." if prefixes else "" 2025-01-24T00:44:52.1036775Z 2025-01-24T00:44:52.1036786Z 2025-01-24T00:44:52.1037504Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-01-24T00:44:52.1038752Z """ 2025-01-24T00:44:52.1039623Z Gets the first comment of the issue, which contains the desired rollout state. 2025-01-24T00:44:52.1040489Z 2025-01-24T00:44:52.1041097Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-01-24T00:44:52.1042151Z """ 2025-01-24T00:44:52.1042520Z gh = get_gh_client(github_token) 2025-01-24T00:44:52.1043013Z issue = get_issue(gh, repo, issue_num) 2025-01-24T00:44:52.1043599Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-01-24T00:44:52.1044007Z 2025-01-24T00:44:52.1044015Z 2025-01-24T00:44:52.1044392Z def download_json(url: str, headers: Dict[str, str], num_retries: int = 3) -> Any: 2025-01-24T00:44:52.1045084Z for _ in range(num_retries): 2025-01-24T00:44:52.1045517Z try: 2025-01-24T00:44:52.1045914Z req = Request(url=url, headers=headers) 2025-01-24T00:44:52.1046762Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-01-24T00:44:52.1047397Z return json.loads(content) 2025-01-24T00:44:52.1047895Z except Exception as e: 2025-01-24T00:44:52.1048393Z log.warning(f"Could not download {url}: {e}") 2025-01-24T00:44:52.1048974Z 2025-01-24T00:44:52.1049328Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-01-24T00:44:52.1049992Z return {} 2025-01-24T00:44:52.1050195Z 2025-01-24T00:44:52.1050201Z 2025-01-24T00:44:52.1050365Z @lru_cache(maxsize=None) 2025-01-24T00:44:52.1051007Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> Dict[str, Any]: 2025-01-24T00:44:52.1051700Z """ 2025-01-24T00:44:52.1052050Z Dynamically get PR information 2025-01-24T00:44:52.1052506Z """ 2025-01-24T00:44:52.1052957Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-01-24T00:44:52.1053525Z headers = { 2025-01-24T00:44:52.1053940Z "Accept": "application/vnd.github.v3+json", 2025-01-24T00:44:52.1054500Z "Authorization": f"token {github_token}", 2025-01-24T00:44:52.1054984Z } 2025-01-24T00:44:52.1055369Z json_response: Dict[str, Any] = download_json( 2025-01-24T00:44:52.1055930Z url=f"{github_api}/issues/{pr_number}", 2025-01-24T00:44:52.1056580Z headers=headers, 2025-01-24T00:44:52.1056980Z ) 2025-01-24T00:44:52.1057157Z 2025-01-24T00:44:52.1057325Z if not json_response: 2025-01-24T00:44:52.1057838Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-01-24T00:44:52.1058400Z return {} 2025-01-24T00:44:52.1058615Z 2025-01-24T00:44:52.1058783Z return json_response 2025-01-24T00:44:52.1059029Z 2025-01-24T00:44:52.1059035Z 2025-01-24T00:44:52.1059397Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> Set[str]: 2025-01-24T00:44:52.1060059Z """ 2025-01-24T00:44:52.1060535Z Dynamically get the latest list of labels from the pull request 2025-01-24T00:44:52.1061122Z """ 2025-01-24T00:44:52.1061690Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-01-24T00:44:52.1062255Z return { 2025-01-24T00:44:52.1062774Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-01-24T00:44:52.1063422Z } 2025-01-24T00:44:52.1063596Z 2025-01-24T00:44:52.1063602Z 2025-01-24T00:44:52.1063764Z def main() -> None: 2025-01-24T00:44:52.1064137Z args = parse_args() 2025-01-24T00:44:52.1064387Z 2025-01-24T00:44:52.1064585Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-01-24T00:44:52.1064941Z 2025-01-24T00:44:52.1065110Z # Check if the PR is opt-out 2025-01-24T00:44:52.1065549Z if args.pr_number: 2025-01-24T00:44:52.1066140Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-01-24T00:44:52.1066936Z if OPT_OUT_LABEL in labels: 2025-01-24T00:44:52.1067392Z log.info( 2025-01-24T00:44:52.1068010Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-01-24T00:44:52.1068714Z ) 2025-01-24T00:44:52.1069211Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:44:52.1069818Z sys.exit() 2025-01-24T00:44:52.1070054Z 2025-01-24T00:44:52.1070204Z try: 2025-01-24T00:44:52.1070592Z rollout_state = get_rollout_state_from_issue( 2025-01-24T00:44:52.1071228Z args.github_token, args.github_issue_repo, args.github_issue 2025-01-24T00:44:52.1071811Z ) 2025-01-24T00:44:52.1071993Z 2025-01-24T00:44:52.1072179Z username = get_potential_pr_author( 2025-01-24T00:44:52.1072666Z args.github_token, 2025-01-24T00:44:52.1073101Z args.github_repo, 2025-01-24T00:44:52.1073537Z args.github_actor, 2025-01-24T00:44:52.1073970Z args.github_ref_type, 2025-01-24T00:44:52.1074419Z args.github_branch, 2025-01-24T00:44:52.1074835Z ) 2025-01-24T00:44:52.1075027Z 2025-01-24T00:44:52.1075290Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-01-24T00:44:52.1075706Z 2025-01-24T00:44:52.1075902Z runner_label_prefix = get_runner_prefix( 2025-01-24T00:44:52.1076508Z rollout_state, 2025-01-24T00:44:52.1077113Z (args.github_issue_owner, username), 2025-01-24T00:44:52.1077613Z args.github_branch, 2025-01-24T00:44:52.1078071Z args.eligible_experiments, 2025-01-24T00:44:52.1078545Z is_canary, 2025-01-24T00:44:52.1078926Z ) 2025-01-24T00:44:52.1079110Z 2025-01-24T00:44:52.1079283Z except Exception as e: 2025-01-24T00:44:52.1079691Z log.error( 2025-01-24T00:44:52.1080306Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-01-24T00:44:52.1081005Z ) 2025-01-24T00:44:52.1081192Z 2025-01-24T00:44:52.1081494Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-01-24T00:44:52.1081955Z 2025-01-24T00:44:52.1081961Z 2025-01-24T00:44:52.1082120Z if __name__ == "__main__": 2025-01-24T00:44:52.1082526Z main() 2025-01-24T00:44:52.1082710Z 2025-01-24T00:44:52.1175177Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-01-24T00:44:52.1176027Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-01-24T00:44:52.1204050Z shell: /usr/bin/bash -e {0} 2025-01-24T00:44:52.1204494Z env: 2025-01-24T00:44:52.1205053Z GITHUB_TOKEN: *** 2025-01-24T00:44:52.1205447Z ISSUE_NUMBER: 5132 2025-01-24T00:44:52.1205854Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:44:52.1206319Z ISSUE_OWNER: 2025-01-24T00:44:52.1206820Z CHECK_EXPERIMENTS: 2025-01-24T00:44:52.1207220Z PR_NUMBER: 2025-01-24T00:44:52.1207574Z ##[endgroup] 2025-01-24T00:44:53.1911554Z Defaulting to user installation because normal site-packages is not writeable 2025-01-24T00:44:53.9648537Z Collecting urllib3==1.26.18 2025-01-24T00:44:54.0209000Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-01-24T00:44:54.0433720Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 4.2 MB/s eta 0:00:00 2025-01-24T00:44:54.0706840Z Collecting PyGithub==2.3.0 2025-01-24T00:44:54.0779670Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-01-24T00:44:54.1258003Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-01-24T00:44:54.1350620Z 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-01-24T00:44:54.1397047Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-01-24T00:44:54.1414228Z 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-01-24T00:44:54.1429820Z Requirement already satisfied: typing-extensions>=4.0.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (4.10.0) 2025-01-24T00:44:54.1799018Z Collecting Deprecated (from PyGithub==2.3.0) 2025-01-24T00:44:54.1877792Z Downloading Deprecated-1.2.15-py2.py3-none-any.whl.metadata (5.5 kB) 2025-01-24T00:44:54.2113699Z 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-01-24T00:44:54.3298787Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-01-24T00:44:54.3406332Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-01-24T00:44:54.4567249Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-01-24T00:44:54.4648020Z 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-01-24T00:44:54.4884786Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-01-24T00:44:54.4965344Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-01-24T00:44:54.5249228Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-01-24T00:44:54.5352480Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 16.1 MB/s eta 0:00:00 2025-01-24T00:44:54.5473168Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-01-24T00:44:54.5573193Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 41.9 MB/s eta 0:00:00 2025-01-24T00:44:54.5644667Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (856 kB) 2025-01-24T00:44:54.5764763Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 83.7 MB/s eta 0:00:00 2025-01-24T00:44:54.5850481Z Downloading Deprecated-1.2.15-py2.py3-none-any.whl (9.9 kB) 2025-01-24T00:44:54.5945712Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-01-24T00:44:54.6014554Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 90.0 MB/s eta 0:00:00 2025-01-24T00:44:54.6086292Z 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-01-24T00:44:54.6136941Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 22.4 MB/s eta 0:00:00 2025-01-24T00:44:54.6208746Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-01-24T00:44:54.6263401Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 28.7 MB/s eta 0:00:00 2025-01-24T00:44:54.9563186Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-01-24T00:44:55.4871669Z Successfully installed Deprecated-1.2.15 PyGithub-2.3.0 cffi-1.17.1 pycparser-2.22 pynacl-1.5.0 urllib3-1.26.18 wrapt-1.17.2 2025-01-24T00:44:55.5761882Z ##[group]Run curr_branch="ciflow/trunk/145539" 2025-01-24T00:44:55.5762266Z curr_branch="ciflow/trunk/145539" 2025-01-24T00:44:55.5762517Z curr_ref_type="tag" 2025-01-24T00:44:55.5762757Z echo "Current branch is '$curr_branch'" 2025-01-24T00:44:55.5763018Z  2025-01-24T00:44:55.5763206Z python3 runner_determinator.py \ 2025-01-24T00:44:55.5763484Z  --github-token "$GITHUB_TOKEN" \ 2025-01-24T00:44:55.5763753Z  --github-issue "$ISSUE_NUMBER" \ 2025-01-24T00:44:55.5764008Z  --github-branch "$curr_branch" \ 2025-01-24T00:44:55.5764287Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-01-24T00:44:55.5764566Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-01-24T00:44:55.5764841Z  --github-ref-type "$curr_ref_type" \ 2025-01-24T00:44:55.5765103Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-01-24T00:44:55.5765447Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-01-24T00:44:55.5765734Z  --pr-number "${PR_NUMBER}" 2025-01-24T00:44:55.5793855Z shell: /usr/bin/bash -e {0} 2025-01-24T00:44:55.5794089Z env: 2025-01-24T00:44:55.5794667Z GITHUB_TOKEN: *** 2025-01-24T00:44:55.5794862Z ISSUE_NUMBER: 5132 2025-01-24T00:44:55.5795073Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-01-24T00:44:55.5795305Z ISSUE_OWNER: 2025-01-24T00:44:55.5795475Z CHECK_EXPERIMENTS: 2025-01-24T00:44:55.5795657Z PR_NUMBER: 2025-01-24T00:44:55.5795822Z ##[endgroup] 2025-01-24T00:44:55.5844033Z Current branch is 'ciflow/trunk/145539' 2025-01-24T00:44:57.8906228Z INFO : Based on rollout percentage of 55%, enabling experiment lf. 2025-01-24T00:44:57.8907924Z INFO : Skipping experiment 'awsa100', as it is not a default experiment 2025-01-24T00:44:57.8908897Z INFO : Setting output: label-type='lf.' 2025-01-24T00:44:57.9229064Z Evaluate and set job outputs 2025-01-24T00:44:57.9235896Z Set output 'label-type' 2025-01-24T00:44:57.9237984Z Cleaning up orphan processes