2025-04-25T04:10:05.8896293Z Current runner version: '2.323.0' 2025-04-25T04:10:05.8920055Z ##[group]Operating System 2025-04-25T04:10:05.8920812Z Ubuntu 2025-04-25T04:10:05.8921238Z 24.04.2 2025-04-25T04:10:05.8921808Z LTS 2025-04-25T04:10:05.8922239Z ##[endgroup] 2025-04-25T04:10:05.8922762Z ##[group]Runner Image 2025-04-25T04:10:05.8923372Z Image: ubuntu-24.04 2025-04-25T04:10:05.8923901Z Version: 20250420.1.0 2025-04-25T04:10:05.8924873Z Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20250420.1/images/ubuntu/Ubuntu2404-Readme.md 2025-04-25T04:10:05.8926610Z Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20250420.1 2025-04-25T04:10:05.8927487Z ##[endgroup] 2025-04-25T04:10:05.8927981Z ##[group]Runner Image Provisioner 2025-04-25T04:10:05.8928597Z 2.0.422.1 2025-04-25T04:10:05.8929014Z ##[endgroup] 2025-04-25T04:10:05.8930009Z ##[group]GITHUB_TOKEN Permissions 2025-04-25T04:10:05.8931857Z Contents: read 2025-04-25T04:10:05.8932419Z Metadata: read 2025-04-25T04:10:05.8933120Z Packages: read 2025-04-25T04:10:05.8933662Z ##[endgroup] 2025-04-25T04:10:05.8936160Z Secret source: Actions 2025-04-25T04:10:05.8936823Z Prepare workflow directory 2025-04-25T04:10:05.9450562Z Prepare all required actions 2025-04-25T04:10:05.9502983Z Complete job name: get-label-type / runner-determinator 2025-04-25T04:10:06.0087181Z ##[group]Run cat < runner_determinator.py 2025-04-25T04:10:06.0089360Z cat < runner_determinator.py 2025-04-25T04:10:06.0089989Z # flake8: noqa: G004 2025-04-25T04:10:06.0090596Z  2025-04-25T04:10:06.0091291Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-04-25T04:10:06.0092300Z # must be kept in sync. You can do it easily by running the following command: 2025-04-25T04:10:06.0093226Z # python .github/scripts/update_runner_determinator.py 2025-04-25T04:10:06.0093954Z  2025-04-25T04:10:06.0094349Z """ 2025-04-25T04:10:06.0095187Z This runner determinator is used to determine which set of runners to run a 2025-04-25T04:10:06.0096349Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-04-25T04:10:06.0097501Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-04-25T04:10:06.0098449Z of which runners should be used to run which job. 2025-04-25T04:10:06.0099104Z  2025-04-25T04:10:06.0100019Z The configuration has two parts, the settings and a list of opted-in users, 2025-04-25T04:10:06.0101028Z separated by a line containing "---". If the line is not present, the 2025-04-25T04:10:06.0101968Z settings are considered to be empty with only the second part, the user 2025-04-25T04:10:06.0102831Z list, defined. 2025-04-25T04:10:06.0103290Z  2025-04-25T04:10:06.0103951Z The first part is a YAML block that defines the rollout settings. This can be 2025-04-25T04:10:06.0105026Z used to define any settings that are needed to determine which runners to use. 2025-04-25T04:10:06.0106223Z It's fields are defined by the RolloutSettings class below. 2025-04-25T04:10:06.0106914Z  2025-04-25T04:10:06.0107637Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-04-25T04:10:06.0108662Z The user list is also a comma separated list of additional features or 2025-04-25T04:10:06.0109510Z experiments which the user could be opted in to. 2025-04-25T04:10:06.0110200Z  2025-04-25T04:10:06.0110694Z The user list has the following rules: 2025-04-25T04:10:06.0111257Z  2025-04-25T04:10:06.0111954Z - Users are GitHub usernames, which must start with the @ prefix 2025-04-25T04:10:06.0112877Z - Each user is also a comma-separated list of features/experiments to enable 2025-04-25T04:10:06.0113774Z - A "#" prefix opts the user out of all experiments 2025-04-25T04:10:06.0114704Z  2025-04-25T04:10:06.0115128Z Example config: 2025-04-25T04:10:06.0115865Z  # A list of experiments that can be opted into. 2025-04-25T04:10:06.0116690Z  # This defines the behavior they'll induce when opted into. 2025-04-25T04:10:06.0117434Z  # Expected syntax is: 2025-04-25T04:10:06.0118211Z  # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-04-25T04:10:06.0119349Z  # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-04-25T04:10:06.0120186Z  2025-04-25T04:10:06.0120589Z  experiments: 2025-04-25T04:10:06.0121167Z  lf: 2025-04-25T04:10:06.0121619Z  rollout_percent: 25 2025-04-25T04:10:06.0122195Z  all_branches: false 2025-04-25T04:10:06.0122821Z  default: true 2025-04-25T04:10:06.0123322Z  --- 2025-04-25T04:10:06.0123778Z  2025-04-25T04:10:06.0124243Z  # Opt-ins: 2025-04-25T04:10:06.0124969Z  # Users can opt into the LF fleet by adding their GitHub username to this list 2025-04-25T04:10:06.0126323Z  # and specifying experiments to enable in a comma-separated list. 2025-04-25T04:10:06.0127313Z  # To always opt out of an experiment, prefix it with a "-". 2025-04-25T04:10:06.0128087Z  # Experiments should be from the above list. 2025-04-25T04:10:06.0128667Z  2025-04-25T04:10:06.0129196Z  @User1,-lf,split_build 2025-04-25T04:10:06.0129727Z  @User2,lf 2025-04-25T04:10:06.0130233Z  @User3,split_build 2025-04-25T04:10:06.0130756Z """ 2025-04-25T04:10:06.0131208Z  2025-04-25T04:10:06.0131670Z import json 2025-04-25T04:10:06.0132160Z import logging 2025-04-25T04:10:06.0132673Z import os 2025-04-25T04:10:06.0133126Z import random 2025-04-25T04:10:06.0133653Z import re 2025-04-25T04:10:06.0134101Z import sys 2025-04-25T04:10:06.0134624Z from argparse import ArgumentParser 2025-04-25T04:10:06.0135434Z from collections.abc import Iterable 2025-04-25T04:10:06.0136099Z from functools import cache 2025-04-25T04:10:06.0136694Z from logging import LogRecord 2025-04-25T04:10:06.0137325Z from typing import Any, NamedTuple 2025-04-25T04:10:06.0138033Z from urllib.request import Request, urlopen 2025-04-25T04:10:06.0138636Z  2025-04-25T04:10:06.0139153Z import yaml 2025-04-25T04:10:06.0139709Z from github import Auth, Github 2025-04-25T04:10:06.0140376Z from github.Issue import Issue 2025-04-25T04:10:06.0140991Z  2025-04-25T04:10:06.0141403Z  2025-04-25T04:10:06.0141936Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-04-25T04:10:06.0142839Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-04-25T04:10:06.0143819Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-04-25T04:10:06.0144614Z  2025-04-25T04:10:06.0145101Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-04-25T04:10:06.0146120Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-04-25T04:10:06.0146812Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-04-25T04:10:06.0147512Z OPT_OUT_LABEL = "no-runner-experiments" 2025-04-25T04:10:06.0148146Z  2025-04-25T04:10:06.0148605Z SETTING_EXPERIMENTS = "experiments" 2025-04-25T04:10:06.0149234Z  2025-04-25T04:10:06.0149675Z LF_FLEET_EXPERIMENT = "lf" 2025-04-25T04:10:06.0150258Z CANARY_FLEET_SUFFIX = ".c" 2025-04-25T04:10:06.0150787Z  2025-04-25T04:10:06.0151275Z  2025-04-25T04:10:06.0151747Z class Experiment(NamedTuple): 2025-04-25T04:10:06.0152484Z  rollout_perc: float = ( 2025-04-25T04:10:06.0153358Z  0 # Percentage of workflows to experiment on when user is not opted-in. 2025-04-25T04:10:06.0154176Z  ) 2025-04-25T04:10:06.0154643Z  all_branches: bool = ( 2025-04-25T04:10:06.0155647Z  False # If True, the experiment is also enabled on the exception branches 2025-04-25T04:10:06.0156435Z  ) 2025-04-25T04:10:06.0156957Z  default: bool = ( 2025-04-25T04:10:06.0157666Z  True # If True, the experiment is enabled by default for all queries 2025-04-25T04:10:06.0158406Z  ) 2025-04-25T04:10:06.0158849Z  2025-04-25T04:10:06.0159390Z  # Add more fields as needed 2025-04-25T04:10:06.0159930Z  2025-04-25T04:10:06.0160339Z  2025-04-25T04:10:06.0160869Z class Settings(NamedTuple): 2025-04-25T04:10:06.0161402Z  """ 2025-04-25T04:10:06.0162011Z  Settings for the experiments that can be opted into. 2025-04-25T04:10:06.0162716Z  """ 2025-04-25T04:10:06.0163172Z  2025-04-25T04:10:06.0164171Z  experiments: dict[str, Experiment] = {} 2025-04-25T04:10:06.0164868Z  2025-04-25T04:10:06.0165610Z  2025-04-25T04:10:06.0166210Z class ColorFormatter(logging.Formatter): 2025-04-25T04:10:06.0167052Z  """Color codes the log messages based on the log level""" 2025-04-25T04:10:06.0167706Z  2025-04-25T04:10:06.0168144Z  COLORS = { 2025-04-25T04:10:06.0168712Z  "WARNING": "\033[33m", # Yellow 2025-04-25T04:10:06.0169371Z  "ERROR": "\033[31m", # Red 2025-04-25T04:10:06.0169953Z  "CRITICAL": "\033[31m", # Red 2025-04-25T04:10:06.0170623Z  "INFO": "\033[0m", # Reset 2025-04-25T04:10:06.0171238Z  "DEBUG": "\033[0m", # Reset 2025-04-25T04:10:06.0171779Z  } 2025-04-25T04:10:06.0172275Z  2025-04-25T04:10:06.0172769Z  def format(self, record: LogRecord) -> str: 2025-04-25T04:10:06.0173657Z  log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-04-25T04:10:06.0174609Z  record.msg = f"{log_color}{record.msg}\033[0m" 2025-04-25T04:10:06.0175271Z  return super().format(record) 2025-04-25T04:10:06.0175996Z  2025-04-25T04:10:06.0176469Z  2025-04-25T04:10:06.0176961Z handler = logging.StreamHandler() 2025-04-25T04:10:06.0177814Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-04-25T04:10:06.0178690Z  2025-04-25T04:10:06.0179243Z log = logging.getLogger(os.path.basename(__file__)) 2025-04-25T04:10:06.0179908Z log.addHandler(handler) 2025-04-25T04:10:06.0180530Z log.setLevel(logging.INFO) 2025-04-25T04:10:06.0181064Z  2025-04-25T04:10:06.0181518Z  2025-04-25T04:10:06.0182106Z def set_github_output(key: str, value: str) -> None: 2025-04-25T04:10:06.0182787Z  """ 2025-04-25T04:10:06.0183412Z  Defines outputs of the github action that invokes this script 2025-04-25T04:10:06.0184162Z  """ 2025-04-25T04:10:06.0184650Z  if not GITHUB_OUTPUT: 2025-04-25T04:10:06.0186081Z  # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-04-25T04:10:06.0187349Z  log.warning( 2025-04-25T04:10:06.0188329Z  "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-04-25T04:10:06.0189288Z  ) 2025-04-25T04:10:06.0189910Z  print(f"::set-output name={key}::{value}") 2025-04-25T04:10:06.0190515Z  return 2025-04-25T04:10:06.0191045Z  2025-04-25T04:10:06.0191663Z  with open(GITHUB_OUTPUT, "a") as f: 2025-04-25T04:10:06.0192354Z  log.info(f"Setting output: {key}='{value}'") 2025-04-25T04:10:06.0193019Z  f.write(f"{key}={value}\n") 2025-04-25T04:10:06.0193638Z  2025-04-25T04:10:06.0194110Z  2025-04-25T04:10:06.0194689Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-04-25T04:10:06.0195615Z  return frozenset( 2025-04-25T04:10:06.0196343Z  filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-04-25T04:10:06.0197107Z  ) 2025-04-25T04:10:06.0197615Z  2025-04-25T04:10:06.0198052Z  2025-04-25T04:10:06.0198511Z def parse_args() -> Any: 2025-04-25T04:10:06.0199219Z  parser = ArgumentParser("Get dynamic rollout settings") 2025-04-25T04:10:06.0200204Z  parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-04-25T04:10:06.0201033Z  parser.add_argument( 2025-04-25T04:10:06.0201668Z  "--github-issue-repo", 2025-04-25T04:10:06.0202292Z  type=str, 2025-04-25T04:10:06.0202790Z  required=False, 2025-04-25T04:10:06.0203550Z  default="pytorch/test-infra", 2025-04-25T04:10:06.0204192Z  help="GitHub repo to get the issue", 2025-04-25T04:10:06.0204806Z  ) 2025-04-25T04:10:06.0205447Z  parser.add_argument( 2025-04-25T04:10:06.0206139Z  "--github-repo", 2025-04-25T04:10:06.0206695Z  type=str, 2025-04-25T04:10:06.0275981Z  required=True, 2025-04-25T04:10:06.0277014Z  help="GitHub repo where CI is running", 2025-04-25T04:10:06.0277898Z  ) 2025-04-25T04:10:06.0278535Z  parser.add_argument( 2025-04-25T04:10:06.0279627Z  "--github-issue", type=int, required=True, help="GitHub issue number" 2025-04-25T04:10:06.0280903Z  ) 2025-04-25T04:10:06.0281547Z  parser.add_argument( 2025-04-25T04:10:06.0282681Z  "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-04-25T04:10:06.0283886Z  ) 2025-04-25T04:10:06.0284529Z  parser.add_argument( 2025-04-25T04:10:06.0285897Z  "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-04-25T04:10:06.0287113Z  ) 2025-04-25T04:10:06.0287767Z  parser.add_argument( 2025-04-25T04:10:06.0288952Z  "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-04-25T04:10:06.0290197Z  ) 2025-04-25T04:10:06.0290843Z  parser.add_argument( 2025-04-25T04:10:06.0291636Z  "--github-ref-type", 2025-04-25T04:10:06.0292437Z  type=str, 2025-04-25T04:10:06.0293164Z  required=True, 2025-04-25T04:10:06.0294067Z  help="Current GitHub ref type, branch or tag", 2025-04-25T04:10:06.0294984Z  ) 2025-04-25T04:10:06.0295825Z  parser.add_argument( 2025-04-25T04:10:06.0296669Z  "--eligible-experiments", 2025-04-25T04:10:06.0297578Z  type=_str_comma_separated_to_set, 2025-04-25T04:10:06.0298464Z  required=False, 2025-04-25T04:10:06.0299236Z  default="", 2025-04-25T04:10:06.0300745Z  help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-04-25T04:10:06.0302365Z  ) 2025-04-25T04:10:06.0303014Z  parser.add_argument( 2025-04-25T04:10:06.0303796Z  "--pr-number", 2025-04-25T04:10:06.0304552Z  type=str, 2025-04-25T04:10:06.0305451Z  required=False, 2025-04-25T04:10:06.0306235Z  default="", 2025-04-25T04:10:06.0307349Z  help="the optional PR number where this is run", 2025-04-25T04:10:06.0308324Z  ) 2025-04-25T04:10:06.0308927Z  2025-04-25T04:10:06.0309555Z  return parser.parse_args() 2025-04-25T04:10:06.0310362Z  2025-04-25T04:10:06.0310913Z  2025-04-25T04:10:06.0311962Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-04-25T04:10:06.0313277Z  auth = Auth.Token(github_token) 2025-04-25T04:10:06.0314158Z  return Github(auth=auth) 2025-04-25T04:10:06.0314940Z  2025-04-25T04:10:06.0316176Z  2025-04-25T04:10:06.0317284Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-04-25T04:10:06.0318701Z  repo = gh.get_repo(repo) 2025-04-25T04:10:06.0319606Z  return repo.get_issue(number=issue_num) 2025-04-25T04:10:06.0320483Z  2025-04-25T04:10:06.0321071Z  2025-04-25T04:10:06.0321664Z def get_potential_pr_author( 2025-04-25T04:10:06.0322804Z  github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-04-25T04:10:06.0323958Z ) -> str: 2025-04-25T04:10:06.0325085Z  # If the trigger was a new tag added by a bot, this is a ciflow case 2025-04-25T04:10:06.0326692Z  # Fetch the actual username from the original PR. The PR number is 2025-04-25T04:10:06.0328007Z  # embedded in the tag name: ciflow// 2025-04-25T04:10:06.0328983Z  2025-04-25T04:10:06.0329609Z  gh = get_gh_client(github_token) 2025-04-25T04:10:06.0330456Z  2025-04-25T04:10:06.0331248Z  if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-04-25T04:10:06.0332327Z  split_tag = ref_name.split("/") 2025-04-25T04:10:06.0333178Z  if ( 2025-04-25T04:10:06.0333870Z  len(split_tag) == 3 2025-04-25T04:10:06.0334751Z  and split_tag[0] == "ciflow" 2025-04-25T04:10:06.0335843Z  and split_tag[2].isnumeric() 2025-04-25T04:10:06.0336696Z  ): 2025-04-25T04:10:06.0337414Z  pr_number = split_tag[2] 2025-04-25T04:10:06.0338244Z  try: 2025-04-25T04:10:06.0339008Z  repository = gh.get_repo(repo) 2025-04-25T04:10:06.0340076Z  pull = repository.get_pull(number=int(pr_number)) 2025-04-25T04:10:06.0341141Z  except Exception as e: 2025-04-25T04:10:06.0342047Z  raise Exception( # noqa: TRY002 2025-04-25T04:10:06.0343216Z  f"issue with pull request {pr_number} from repo {repository}" 2025-04-25T04:10:06.0344325Z  ) from e 2025-04-25T04:10:06.0345804Z  return pull.user.login # type: ignore[no-any-return] 2025-04-25T04:10:06.0347099Z  # In all other cases, return the original input username 2025-04-25T04:10:06.0348115Z  return username 2025-04-25T04:10:06.0348818Z  2025-04-25T04:10:06.0349368Z  2025-04-25T04:10:06.0350108Z def is_exception_branch(branch: str) -> bool: 2025-04-25T04:10:06.0351001Z  """ 2025-04-25T04:10:06.0352129Z  Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-04-25T04:10:06.0353464Z  """ 2025-04-25T04:10:06.0354473Z  return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-04-25T04:10:06.0356059Z  2025-04-25T04:10:06.0356626Z  2025-04-25T04:10:06.0357267Z def load_yaml(yaml_text: str) -> Any: 2025-04-25T04:10:06.0358115Z  try: 2025-04-25T04:10:06.0358788Z  data = yaml.safe_load(yaml_text) 2025-04-25T04:10:06.0359655Z  return data 2025-04-25T04:10:06.0360652Z  except yaml.YAMLError: 2025-04-25T04:10:06.0361523Z  log.exception("Error loading YAML") 2025-04-25T04:10:06.0362411Z  raise 2025-04-25T04:10:06.0363070Z  2025-04-25T04:10:06.0363613Z  2025-04-25T04:10:06.0364663Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-04-25T04:10:06.0366239Z  """ 2025-04-25T04:10:06.0367356Z  Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-04-25T04:10:06.0368632Z  2025-04-25T04:10:06.0369564Z  If the issue body contains "---" then the text above that is the settings 2025-04-25T04:10:06.0370883Z  and the text below is the list of opted in users. 2025-04-25T04:10:06.0371833Z  2025-04-25T04:10:06.0372795Z  If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-04-25T04:10:06.0373998Z  """ 2025-04-25T04:10:06.0374786Z  rollout_state_parts = rollout_state.split("---") 2025-04-25T04:10:06.0376041Z  if len(rollout_state_parts) >= 2: 2025-04-25T04:10:06.0377310Z  return rollout_state_parts[0], rollout_state_parts[1] 2025-04-25T04:10:06.0378354Z  else: 2025-04-25T04:10:06.0379029Z  return "", rollout_state 2025-04-25T04:10:06.0379823Z  2025-04-25T04:10:06.0380359Z  2025-04-25T04:10:06.0381024Z class UserOptins(dict[str, list[str]]): 2025-04-25T04:10:06.0381938Z  """ 2025-04-25T04:10:06.0382852Z  Dictionary of users with a list of features they have opted into 2025-04-25T04:10:06.0383949Z  """ 2025-04-25T04:10:06.0384548Z  2025-04-25T04:10:06.0385115Z  2025-04-25T04:10:06.0386204Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-04-25T04:10:06.0387342Z  """ 2025-04-25T04:10:06.0388588Z  Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-04-25T04:10:06.0390008Z  2025-04-25T04:10:06.0391414Z  Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-04-25T04:10:06.0393141Z  - Example line: "@User1,lf,split_build" 2025-04-25T04:10:06.0394345Z  - A "#" prefix indicates the user is opted out of all experiments 2025-04-25T04:10:06.0395595Z  2025-04-25T04:10:06.0396154Z  2025-04-25T04:10:06.0396694Z  """ 2025-04-25T04:10:06.0397346Z  optins = UserOptins() 2025-04-25T04:10:06.0398221Z  for user in user_optin_text.split("\n"): 2025-04-25T04:10:06.0399161Z  user = user.strip("\r\n\t -") 2025-04-25T04:10:06.0400112Z  if not user or not user.startswith("@"): 2025-04-25T04:10:06.0401087Z  # Not a valid user. Skip 2025-04-25T04:10:06.0401930Z  continue 2025-04-25T04:10:06.0402613Z  2025-04-25T04:10:06.0403191Z  if user: 2025-04-25T04:10:06.0403991Z  usr_name = user.split(",")[0].strip("@") 2025-04-25T04:10:06.0405179Z  optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-04-25T04:10:06.0406472Z  2025-04-25T04:10:06.0407058Z  return optins 2025-04-25T04:10:06.0407733Z  2025-04-25T04:10:06.0408274Z  2025-04-25T04:10:06.0409091Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-04-25T04:10:06.0410146Z  """ 2025-04-25T04:10:06.0410856Z  Check if the experiment name is valid. 2025-04-25T04:10:06.0411723Z  A valid name: 2025-04-25T04:10:06.0412885Z  - Contains only alphanumeric characters and the special characters "_" & "-" 2025-04-25T04:10:06.0414737Z  - The special characters "_" & "-" shouldn't be the first or last characters 2025-04-25T04:10:06.0416156Z  - Cannot contain spaces 2025-04-25T04:10:06.0416961Z  """ 2025-04-25T04:10:06.0417551Z  2025-04-25T04:10:06.0418298Z  valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-04-25T04:10:06.0419529Z  valid = bool(re.match(valid_char_regex, experiment_name)) 2025-04-25T04:10:06.0420533Z  2025-04-25T04:10:06.0421091Z  if valid: 2025-04-25T04:10:06.0421741Z  return True 2025-04-25T04:10:06.0422461Z  2025-04-25T04:10:06.0423024Z  log.error( 2025-04-25T04:10:06.0425728Z  f"Invalid experiment name: {experiment_name}. Experiment names should only contain alphanumeric characters, '_', and '-'. They cannot contain spaces, and the special characters '_' and '-' cannot be the first or last characters." 2025-04-25T04:10:06.0428405Z  ) 2025-04-25T04:10:06.0429023Z  return False 2025-04-25T04:10:06.0429705Z  2025-04-25T04:10:06.0430246Z  2025-04-25T04:10:06.0431318Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-04-25T04:10:06.0432433Z  """ 2025-04-25T04:10:06.0433453Z  Parse the experiments from the issue body into a list of ExperimentSettings 2025-04-25T04:10:06.0434651Z  """ 2025-04-25T04:10:06.0435252Z  try: 2025-04-25T04:10:06.0436096Z  if settings_text: 2025-04-25T04:10:06.0437354Z  # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-04-25T04:10:06.0438708Z  # for easy reading 2025-04-25T04:10:06.0440113Z  # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-04-25T04:10:06.0441661Z  # the backtick character in shell commands. 2025-04-25T04:10:06.0442724Z  backtick = chr(96) # backtick character 2025-04-25T04:10:06.0443865Z  settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-04-25T04:10:06.0445039Z  settings = load_yaml(settings_text) 2025-04-25T04:10:06.0446087Z  2025-04-25T04:10:06.0447078Z  # For now we just load experiments. We can expand this if/when we add more settings 2025-04-25T04:10:06.0448356Z  experiments = {} 2025-04-25T04:10:06.0449135Z  2025-04-25T04:10:06.0450079Z  for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-04-25T04:10:06.0451401Z  if not is_valid_experiment_name(exp_name): 2025-04-25T04:10:06.0453295Z  # Exclude invalid experiments from the list. We log an error, but don't raise an exception so that other experiments can still be processed. 2025-04-25T04:10:06.0455159Z  continue 2025-04-25T04:10:06.0456094Z  2025-04-25T04:10:06.0456710Z  valid_settings = {} 2025-04-25T04:10:06.0457632Z  for setting in exp_settings: 2025-04-25T04:10:06.0458609Z  if setting not in Experiment._fields: 2025-04-25T04:10:06.0459547Z  log.warning( 2025-04-25T04:10:06.0460787Z  f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-04-25T04:10:06.0462002Z  ) 2025-04-25T04:10:06.0462756Z  else: 2025-04-25T04:10:06.0463698Z  valid_settings[setting] = exp_settings[setting] 2025-04-25T04:10:06.0464658Z  2025-04-25T04:10:06.0465613Z  experiments[exp_name] = Experiment(**valid_settings) 2025-04-25T04:10:06.0466986Z  return Settings(experiments) 2025-04-25T04:10:06.0467829Z  2025-04-25T04:10:06.0468410Z  except Exception: 2025-04-25T04:10:06.0469273Z  log.exception("Failed to parse settings") 2025-04-25T04:10:06.0470205Z  2025-04-25T04:10:06.0470782Z  return Settings() 2025-04-25T04:10:06.0471493Z  2025-04-25T04:10:06.0472037Z  2025-04-25T04:10:06.0472817Z def parse_settings(rollout_state: str) -> Settings: 2025-04-25T04:10:06.0473819Z  """ 2025-04-25T04:10:06.0474578Z  Parse settings, if any, from the rollout state. 2025-04-25T04:10:06.0475709Z  2025-04-25T04:10:06.0476613Z  If the issue body contains "---" then the text above that is the settings 2025-04-25T04:10:06.0477912Z  and the text below is the list of opted in users. 2025-04-25T04:10:06.0478866Z  2025-04-25T04:10:06.0479909Z  If it doesn't contain "---" then the settings are empty and the default values are used. 2025-04-25T04:10:06.0481136Z  """ 2025-04-25T04:10:06.0482118Z  settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-04-25T04:10:06.0483654Z  return parse_settings_from_text(settings_text) 2025-04-25T04:10:06.0484597Z  2025-04-25T04:10:06.0485158Z  2025-04-25T04:10:06.0486094Z def parse_users(rollout_state: str) -> UserOptins: 2025-04-25T04:10:06.0487060Z  """ 2025-04-25T04:10:06.0487731Z  Parse users from the rollout state. 2025-04-25T04:10:06.0488592Z  2025-04-25T04:10:06.0489141Z  """ 2025-04-25T04:10:06.0490076Z  _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-04-25T04:10:06.0491382Z  return parse_user_opt_in_from_text(users_text) 2025-04-25T04:10:06.0492318Z  2025-04-25T04:10:06.0492884Z  2025-04-25T04:10:06.0493908Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-04-25T04:10:06.0495201Z  """ 2025-04-25T04:10:06.0496112Z  Check if a user is opted into an experiment 2025-04-25T04:10:06.0497019Z  """ 2025-04-25T04:10:06.0497826Z  return experiment_name in user_optins.get(user, []) 2025-04-25T04:10:06.0498817Z  2025-04-25T04:10:06.0499367Z  2025-04-25T04:10:06.0500402Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-04-25T04:10:06.0501706Z  """ 2025-04-25T04:10:06.0502514Z  Check if a user explicitly opted out of an experiment 2025-04-25T04:10:06.0503476Z  """ 2025-04-25T04:10:06.0504380Z  # if the experiment is prefixed with a "-", then it's an opt-out 2025-04-25T04:10:06.0505736Z  experiment_optout = "-" + experiment_name 2025-04-25T04:10:06.0506858Z  if experiment_optout not in user_optins.get(user, []): 2025-04-25T04:10:06.0507889Z  return False 2025-04-25T04:10:06.0508594Z  2025-04-25T04:10:06.0509365Z  if is_user_opted_in(user, user_optins, experiment_name): 2025-04-25T04:10:06.0510389Z  log.warning( 2025-04-25T04:10:06.0511795Z  f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-04-25T04:10:06.0513268Z  ) 2025-04-25T04:10:06.0513877Z  2025-04-25T04:10:06.0514436Z  return True 2025-04-25T04:10:06.0515086Z  2025-04-25T04:10:06.0515833Z  2025-04-25T04:10:06.0516423Z def get_runner_prefix( 2025-04-25T04:10:06.0517215Z  rollout_state: str, 2025-04-25T04:10:06.0518055Z  workflow_requestors: Iterable[str], 2025-04-25T04:10:06.0518916Z  branch: str, 2025-04-25T04:10:06.0520070Z  eligible_experiments: frozenset[str] = frozenset(), 2025-04-25T04:10:06.0521095Z  is_canary: bool = False, 2025-04-25T04:10:06.0521863Z ) -> str: 2025-04-25T04:10:06.0522616Z  settings = parse_settings(rollout_state) 2025-04-25T04:10:06.0523631Z  user_optins = parse_users(rollout_state) 2025-04-25T04:10:06.0524508Z  2025-04-25T04:10:06.0525083Z  fleet_prefix = "" 2025-04-25T04:10:06.0526020Z  prefixes = [] 2025-04-25T04:10:06.0527163Z  for experiment_name, experiment_settings in settings.experiments.items(): 2025-04-25T04:10:06.0528792Z  if not experiment_settings.all_branches and is_exception_branch(branch): 2025-04-25T04:10:06.0530016Z  log.info( 2025-04-25T04:10:06.0531211Z  f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-04-25T04:10:06.0532514Z  ) 2025-04-25T04:10:06.0533190Z  continue 2025-04-25T04:10:06.0533874Z  2025-04-25T04:10:06.0534480Z  if eligible_experiments: 2025-04-25T04:10:06.0535840Z  if experiment_name not in eligible_experiments: 2025-04-25T04:10:06.0537002Z  exp_list = ", ".join(eligible_experiments) 2025-04-25T04:10:06.0537935Z  log.info( 2025-04-25T04:10:06.0539368Z  f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-04-25T04:10:06.0540789Z  ) 2025-04-25T04:10:06.0541483Z  continue 2025-04-25T04:10:06.0542345Z  elif not experiment_settings.default: 2025-04-25T04:10:06.0543219Z  log.info( 2025-04-25T04:10:06.0544370Z  f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-04-25T04:10:06.0545859Z  ) 2025-04-25T04:10:06.0546538Z  continue 2025-04-25T04:10:06.0547227Z  2025-04-25T04:10:06.0547997Z  # Is any workflow_requestor opted out to this experiment? 2025-04-25T04:10:06.0549084Z  opted_out_users = [ 2025-04-25T04:10:06.0549879Z  requestor 2025-04-25T04:10:06.0550692Z  for requestor in workflow_requestors 2025-04-25T04:10:06.0551884Z  if is_user_opted_out(requestor, user_optins, experiment_name) 2025-04-25T04:10:06.0552951Z  ] 2025-04-25T04:10:06.0553548Z  2025-04-25T04:10:06.0554192Z  if opted_out_users: 2025-04-25T04:10:06.0555007Z  log.info( 2025-04-25T04:10:06.0556283Z  f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-04-25T04:10:06.0557478Z  ) 2025-04-25T04:10:06.0558190Z  continue 2025-04-25T04:10:06.0558892Z  2025-04-25T04:10:06.0559656Z  # Is any workflow_requestor opted in to this experiment? 2025-04-25T04:10:06.0560684Z  opted_in_users = [ 2025-04-25T04:10:06.0561512Z  requestor 2025-04-25T04:10:06.0562348Z  for requestor in workflow_requestors 2025-04-25T04:10:06.0563492Z  if is_user_opted_in(requestor, user_optins, experiment_name) 2025-04-25T04:10:06.0564559Z  ] 2025-04-25T04:10:06.0565182Z  2025-04-25T04:10:06.0565938Z  enabled = False 2025-04-25T04:10:06.0566715Z  if opted_in_users: 2025-04-25T04:10:06.0567486Z  log.info( 2025-04-25T04:10:06.0568562Z  f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-04-25T04:10:06.0569720Z  ) 2025-04-25T04:10:06.0570606Z  enabled = True 2025-04-25T04:10:06.0571377Z  2025-04-25T04:10:06.0572047Z  elif experiment_settings.rollout_perc: 2025-04-25T04:10:06.0573476Z  # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-04-25T04:10:06.0575092Z  if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-04-25T04:10:06.0576372Z  log.info( 2025-04-25T04:10:06.0577899Z  f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-04-25T04:10:06.0579465Z  ) 2025-04-25T04:10:06.0580201Z  enabled = True 2025-04-25T04:10:06.0580997Z  2025-04-25T04:10:06.0581567Z  if enabled: 2025-04-25T04:10:06.0582323Z  label = experiment_name 2025-04-25T04:10:06.0583285Z  if experiment_name == LF_FLEET_EXPERIMENT: 2025-04-25T04:10:06.0584714Z  # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-04-25T04:10:06.0586623Z  # - If it's enabled, then we always list it's prefix first 2025-04-25T04:10:06.0587962Z  # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-04-25T04:10:06.0589075Z  if is_canary: 2025-04-25T04:10:06.0589953Z  label += CANARY_FLEET_SUFFIX 2025-04-25T04:10:06.0590880Z  fleet_prefix = label 2025-04-25T04:10:06.0591695Z  else: 2025-04-25T04:10:06.0592462Z  prefixes.append(label) 2025-04-25T04:10:06.0593291Z  2025-04-25T04:10:06.0593888Z  if len(prefixes) > 1: 2025-04-25T04:10:06.0594657Z  log.error( 2025-04-25T04:10:06.0596660Z  f"Only a fleet and one other experiment can be enabled for a job at any time. Enabling {prefixes[0]} and ignoring the rest, which are {', '.join(prefixes[1:])}" 2025-04-25T04:10:06.0598591Z  ) 2025-04-25T04:10:06.0599271Z  prefixes = prefixes[:1] 2025-04-25T04:10:06.0600083Z  2025-04-25T04:10:06.0600672Z  # Fleet always comes first 2025-04-25T04:10:06.0601490Z  if fleet_prefix: 2025-04-25T04:10:06.0602290Z  prefixes.insert(0, fleet_prefix) 2025-04-25T04:10:06.0603136Z  2025-04-25T04:10:06.0603865Z  return ".".join(prefixes) + "." if prefixes else "" 2025-04-25T04:10:06.0604820Z  2025-04-25T04:10:06.0605558Z  2025-04-25T04:10:06.0606625Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-04-25T04:10:06.0607943Z  """ 2025-04-25T04:10:06.0608947Z  Gets the first comment of the issue, which contains the desired rollout state. 2025-04-25T04:10:06.0610143Z  2025-04-25T04:10:06.0611118Z  The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-04-25T04:10:06.0612304Z  """ 2025-04-25T04:10:06.0612990Z  gh = get_gh_client(github_token) 2025-04-25T04:10:06.0613913Z  issue = get_issue(gh, repo, issue_num) 2025-04-25T04:10:06.0615019Z  return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-04-25T04:10:06.0616202Z  2025-04-25T04:10:06.0616741Z  2025-04-25T04:10:06.0617750Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-04-25T04:10:06.0619027Z  for _ in range(num_retries): 2025-04-25T04:10:06.0619829Z  try: 2025-04-25T04:10:06.0620570Z  req = Request(url=url, headers=headers) 2025-04-25T04:10:06.0621683Z  content = urlopen(req, timeout=5).read().decode("utf-8") 2025-04-25T04:10:06.0623001Z  return json.loads(content) 2025-04-25T04:10:06.0623908Z  except Exception as e: 2025-04-25T04:10:06.0624861Z  log.warning(f"Could not download {url}: {e}") 2025-04-25T04:10:06.0625959Z  2025-04-25T04:10:06.0626934Z  log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-04-25T04:10:06.0628134Z  return {} 2025-04-25T04:10:06.0628773Z  2025-04-25T04:10:06.0629303Z  2025-04-25T04:10:06.0629871Z @cache 2025-04-25T04:10:06.0630939Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-04-25T04:10:06.0632191Z  """ 2025-04-25T04:10:06.0632873Z  Dynamically get PR information 2025-04-25T04:10:06.0633703Z  """ 2025-04-25T04:10:06.0634565Z  github_api = f"https://api.github.com/repos/{github_repo}" 2025-04-25T04:10:06.0635803Z  headers = { 2025-04-25T04:10:06.0636641Z  "Accept": "application/vnd.github.v3+json", 2025-04-25T04:10:06.0637679Z  "Authorization": f"token {github_token}", 2025-04-25T04:10:06.0638553Z  } 2025-04-25T04:10:06.0639514Z  json_response: dict[str, Any] = download_json( 2025-04-25T04:10:06.0640575Z  url=f"{github_api}/issues/{pr_number}", 2025-04-25T04:10:06.0641476Z  headers=headers, 2025-04-25T04:10:06.0642232Z  ) 2025-04-25T04:10:06.0642836Z  2025-04-25T04:10:06.0643426Z  if not json_response: 2025-04-25T04:10:06.0644433Z  log.warning(f"Failed to get the labels for #{pr_number}") 2025-04-25T04:10:06.0645683Z  return {} 2025-04-25T04:10:06.0646369Z  2025-04-25T04:10:06.0646951Z  return json_response 2025-04-25T04:10:06.0647678Z  2025-04-25T04:10:06.0648227Z  2025-04-25T04:10:06.0649238Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-04-25T04:10:06.0650458Z  """ 2025-04-25T04:10:06.0651367Z  Dynamically get the latest list of labels from the pull request 2025-04-25T04:10:06.0652493Z  """ 2025-04-25T04:10:06.0653320Z  pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-04-25T04:10:06.0654393Z  return { 2025-04-25T04:10:06.0655599Z  label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-04-25T04:10:06.0656761Z  } 2025-04-25T04:10:06.0657338Z  2025-04-25T04:10:06.0657873Z  2025-04-25T04:10:06.0658462Z def main() -> None: 2025-04-25T04:10:06.0659202Z  args = parse_args() 2025-04-25T04:10:06.0659929Z  2025-04-25T04:10:06.0660609Z  runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-04-25T04:10:06.0661503Z  2025-04-25T04:10:06.0662125Z  # Check if the PR is opt-out 2025-04-25T04:10:06.0662956Z  if args.pr_number: 2025-04-25T04:10:06.0664107Z  labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-04-25T04:10:06.0665608Z  if OPT_OUT_LABEL in labels: 2025-04-25T04:10:06.0666456Z  log.info( 2025-04-25T04:10:06.0667660Z  f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-04-25T04:10:06.0668940Z  ) 2025-04-25T04:10:06.0669907Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-04-25T04:10:06.0671057Z  sys.exit() 2025-04-25T04:10:06.0671786Z  2025-04-25T04:10:06.0672338Z  try: 2025-04-25T04:10:06.0673083Z  rollout_state = get_rollout_state_from_issue( 2025-04-25T04:10:06.0674315Z  args.github_token, args.github_issue_repo, args.github_issue 2025-04-25T04:10:06.0675812Z  ) 2025-04-25T04:10:06.0676421Z  2025-04-25T04:10:06.0677064Z  username = get_potential_pr_author( 2025-04-25T04:10:06.0677984Z  args.github_token, 2025-04-25T04:10:06.0678817Z  args.github_repo, 2025-04-25T04:10:06.0679638Z  args.github_actor, 2025-04-25T04:10:06.0680482Z  args.github_ref_type, 2025-04-25T04:10:06.0681334Z  args.github_branch, 2025-04-25T04:10:06.0682120Z  ) 2025-04-25T04:10:06.0682706Z  2025-04-25T04:10:06.0683501Z  is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-04-25T04:10:06.0684519Z  2025-04-25T04:10:06.0685196Z  runner_label_prefix = get_runner_prefix( 2025-04-25T04:10:06.0686306Z  rollout_state, 2025-04-25T04:10:06.0687185Z  (args.github_issue_owner, username), 2025-04-25T04:10:06.0688126Z  args.github_branch, 2025-04-25T04:10:06.0688986Z  args.eligible_experiments, 2025-04-25T04:10:06.0689840Z  is_canary, 2025-04-25T04:10:06.0690826Z  ) 2025-04-25T04:10:06.0691453Z  2025-04-25T04:10:06.0692031Z  except Exception as e: 2025-04-25T04:10:06.0692829Z  log.error( 2025-04-25T04:10:06.0694027Z  f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-04-25T04:10:06.0695462Z  ) 2025-04-25T04:10:06.0696093Z  2025-04-25T04:10:06.0696981Z  set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-04-25T04:10:06.0698069Z  2025-04-25T04:10:06.0698601Z  2025-04-25T04:10:06.0699174Z if __name__ == "__main__": 2025-04-25T04:10:06.0699944Z  main() 2025-04-25T04:10:06.0700573Z  2025-04-25T04:10:06.0701113Z EOF 2025-04-25T04:10:06.0701671Z  2025-04-25T04:10:06.0702271Z cat runner_determinator.py 2025-04-25T04:10:06.1045781Z shell: /usr/bin/bash -e {0} 2025-04-25T04:10:06.1047019Z env: 2025-04-25T04:10:06.1048087Z GITHUB_TOKEN: *** 2025-04-25T04:10:06.1048795Z ISSUE_NUMBER: 5132 2025-04-25T04:10:06.1049511Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-04-25T04:10:06.1050319Z ISSUE_OWNER: 2025-04-25T04:10:06.1050950Z CHECK_EXPERIMENTS: 2025-04-25T04:10:06.1051597Z PR_NUMBER: 2025-04-25T04:10:06.1052194Z ##[endgroup] 2025-04-25T04:10:06.1341455Z # flake8: noqa: G004 2025-04-25T04:10:06.1341965Z 2025-04-25T04:10:06.1342680Z # Note: Copies of this script in runner_determinator.py and _runner-determinator.yml 2025-04-25T04:10:06.1344213Z # must be kept in sync. You can do it easily by running the following command: 2025-04-25T04:10:06.1345463Z # python .github/scripts/update_runner_determinator.py 2025-04-25T04:10:06.1346043Z 2025-04-25T04:10:06.1346197Z """ 2025-04-25T04:10:06.1346761Z This runner determinator is used to determine which set of runners to run a 2025-04-25T04:10:06.1347619Z GitHub job on. It uses the first comment of a GitHub issue (by default 2025-04-25T04:10:06.1348485Z https://github.com/pytorch/test-infra/issues/5132) to define the configuration 2025-04-25T04:10:06.1349268Z of which runners should be used to run which job. 2025-04-25T04:10:06.1349663Z 2025-04-25T04:10:06.1350025Z The configuration has two parts, the settings and a list of opted-in users, 2025-04-25T04:10:06.1350871Z separated by a line containing "---". If the line is not present, the 2025-04-25T04:10:06.1351691Z settings are considered to be empty with only the second part, the user 2025-04-25T04:10:06.1352349Z list, defined. 2025-04-25T04:10:06.1352578Z 2025-04-25T04:10:06.1352919Z The first part is a YAML block that defines the rollout settings. This can be 2025-04-25T04:10:06.1353767Z used to define any settings that are needed to determine which runners to use. 2025-04-25T04:10:06.1354826Z It's fields are defined by the RolloutSettings class below. 2025-04-25T04:10:06.1355256Z 2025-04-25T04:10:06.1355813Z The second part is a list of users who are explicitly opted in to the LF fleet. 2025-04-25T04:10:06.1356647Z The user list is also a comma separated list of additional features or 2025-04-25T04:10:06.1357352Z experiments which the user could be opted in to. 2025-04-25T04:10:06.1357738Z 2025-04-25T04:10:06.1357924Z The user list has the following rules: 2025-04-25T04:10:06.1358270Z 2025-04-25T04:10:06.1358572Z - Users are GitHub usernames, which must start with the @ prefix 2025-04-25T04:10:06.1359389Z - Each user is also a comma-separated list of features/experiments to enable 2025-04-25T04:10:06.1360119Z - A "#" prefix opts the user out of all experiments 2025-04-25T04:10:06.1360506Z 2025-04-25T04:10:06.1360708Z Example config: 2025-04-25T04:10:06.1361149Z # A list of experiments that can be opted into. 2025-04-25T04:10:06.1361905Z # This defines the behavior they'll induce when opted into. 2025-04-25T04:10:06.1362613Z # Expected syntax is: 2025-04-25T04:10:06.1363232Z # [experiment_name]: # Name of the experiment. Also used for the label prefix. 2025-04-25T04:10:06.1364319Z # rollout_perc: [int] # % of workflows to run with this experiment when users are not opted in. 2025-04-25T04:10:06.1364928Z 2025-04-25T04:10:06.1365087Z experiments: 2025-04-25T04:10:06.1365721Z lf: 2025-04-25T04:10:06.1366092Z rollout_percent: 25 2025-04-25T04:10:06.1366540Z all_branches: false 2025-04-25T04:10:06.1366977Z default: true 2025-04-25T04:10:06.1367383Z --- 2025-04-25T04:10:06.1367589Z 2025-04-25T04:10:06.1367745Z # Opt-ins: 2025-04-25T04:10:06.1368308Z # Users can opt into the LF fleet by adding their GitHub username to this list 2025-04-25T04:10:06.1369134Z # and specifying experiments to enable in a comma-separated list. 2025-04-25T04:10:06.1369887Z # To always opt out of an experiment, prefix it with a "-". 2025-04-25T04:10:06.1370522Z # Experiments should be from the above list. 2025-04-25T04:10:06.1370894Z 2025-04-25T04:10:06.1371061Z @User1,-lf,split_build 2025-04-25T04:10:06.1371486Z @User2,lf 2025-04-25T04:10:06.1371852Z @User3,split_build 2025-04-25T04:10:06.1372242Z """ 2025-04-25T04:10:06.1372428Z 2025-04-25T04:10:06.1372582Z import json 2025-04-25T04:10:06.1372943Z import logging 2025-04-25T04:10:06.1373304Z import os 2025-04-25T04:10:06.1373663Z import random 2025-04-25T04:10:06.1374027Z import re 2025-04-25T04:10:06.1374368Z import sys 2025-04-25T04:10:06.1374750Z from argparse import ArgumentParser 2025-04-25T04:10:06.1375259Z from collections.abc import Iterable 2025-04-25T04:10:06.1376008Z from functools import cache 2025-04-25T04:10:06.1376462Z from logging import LogRecord 2025-04-25T04:10:06.1376947Z from typing import Any, NamedTuple 2025-04-25T04:10:06.1377466Z from urllib.request import Request, urlopen 2025-04-25T04:10:06.1377832Z 2025-04-25T04:10:06.1377990Z import yaml 2025-04-25T04:10:06.1378358Z from github import Auth, Github 2025-04-25T04:10:06.1378825Z from github.Issue import Issue 2025-04-25T04:10:06.1379122Z 2025-04-25T04:10:06.1379128Z 2025-04-25T04:10:06.1379330Z DEFAULT_LABEL_PREFIX = "" # use meta runners 2025-04-25T04:10:06.1379999Z WORKFLOW_LABEL_LF = "lf." # use runners from the linux foundation 2025-04-25T04:10:06.1380821Z WORKFLOW_LABEL_LF_CANARY = "lf.c." # use canary runners from the linux foundation 2025-04-25T04:10:06.1381374Z 2025-04-25T04:10:06.1381587Z GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT", "") 2025-04-25T04:10:06.1382141Z GH_OUTPUT_KEY_AMI = "runner-ami" 2025-04-25T04:10:06.1382632Z GH_OUTPUT_KEY_LABEL_TYPE = "label-type" 2025-04-25T04:10:06.1383165Z OPT_OUT_LABEL = "no-runner-experiments" 2025-04-25T04:10:06.1383519Z 2025-04-25T04:10:06.1383698Z SETTING_EXPERIMENTS = "experiments" 2025-04-25T04:10:06.1384029Z 2025-04-25T04:10:06.1384203Z LF_FLEET_EXPERIMENT = "lf" 2025-04-25T04:10:06.1384796Z CANARY_FLEET_SUFFIX = ".c" 2025-04-25T04:10:06.1385080Z 2025-04-25T04:10:06.1385086Z 2025-04-25T04:10:06.1385260Z class Experiment(NamedTuple): 2025-04-25T04:10:06.1385998Z rollout_perc: float = ( 2025-04-25T04:10:06.1386607Z 0 # Percentage of workflows to experiment on when user is not opted-in. 2025-04-25T04:10:06.1387261Z ) 2025-04-25T04:10:06.1387616Z all_branches: bool = ( 2025-04-25T04:10:06.1388215Z False # If True, the experiment is also enabled on the exception branches 2025-04-25T04:10:06.1388865Z ) 2025-04-25T04:10:06.1389208Z default: bool = ( 2025-04-25T04:10:06.1389749Z True # If True, the experiment is enabled by default for all queries 2025-04-25T04:10:06.1390366Z ) 2025-04-25T04:10:06.1390552Z 2025-04-25T04:10:06.1390725Z # Add more fields as needed 2025-04-25T04:10:06.1391017Z 2025-04-25T04:10:06.1391024Z 2025-04-25T04:10:06.1391195Z class Settings(NamedTuple): 2025-04-25T04:10:06.1391622Z """ 2025-04-25T04:10:06.1392051Z Settings for the experiments that can be opted into. 2025-04-25T04:10:06.1392590Z """ 2025-04-25T04:10:06.1392784Z 2025-04-25T04:10:06.1392975Z experiments: dict[str, Experiment] = {} 2025-04-25T04:10:06.1393334Z 2025-04-25T04:10:06.1393341Z 2025-04-25T04:10:06.1393668Z class ColorFormatter(logging.Formatter): 2025-04-25T04:10:06.1394273Z """Color codes the log messages based on the log level""" 2025-04-25T04:10:06.1394698Z 2025-04-25T04:10:06.1394848Z COLORS = { 2025-04-25T04:10:06.1395228Z "WARNING": "\033[33m", # Yellow 2025-04-25T04:10:06.1395903Z "ERROR": "\033[31m", # Red 2025-04-25T04:10:06.1396374Z "CRITICAL": "\033[31m", # Red 2025-04-25T04:10:06.1396849Z "INFO": "\033[0m", # Reset 2025-04-25T04:10:06.1397314Z "DEBUG": "\033[0m", # Reset 2025-04-25T04:10:06.1397754Z } 2025-04-25T04:10:06.1397946Z 2025-04-25T04:10:06.1398148Z def format(self, record: LogRecord) -> str: 2025-04-25T04:10:06.1398863Z log_color = self.COLORS.get(record.levelname, "\033[0m") # Default to reset 2025-04-25T04:10:06.1399614Z record.msg = f"{log_color}{record.msg}\033[0m" 2025-04-25T04:10:06.1400193Z return super().format(record) 2025-04-25T04:10:06.1400532Z 2025-04-25T04:10:06.1400539Z 2025-04-25T04:10:06.1400727Z handler = logging.StreamHandler() 2025-04-25T04:10:06.1401408Z handler.setFormatter(ColorFormatter(fmt="%(levelname)-8s: %(message)s")) 2025-04-25T04:10:06.1401952Z 2025-04-25T04:10:06.1402190Z log = logging.getLogger(os.path.basename(__file__)) 2025-04-25T04:10:06.1402747Z log.addHandler(handler) 2025-04-25T04:10:06.1403177Z log.setLevel(logging.INFO) 2025-04-25T04:10:06.1403454Z 2025-04-25T04:10:06.1403460Z 2025-04-25T04:10:06.1404279Z def set_github_output(key: str, value: str) -> None: 2025-04-25T04:10:06.1404861Z """ 2025-04-25T04:10:06.1405663Z Defines outputs of the github action that invokes this script 2025-04-25T04:10:06.1406334Z """ 2025-04-25T04:10:06.1406678Z if not GITHUB_OUTPUT: 2025-04-25T04:10:06.1407705Z # See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ for deprecation notice 2025-04-25T04:10:06.1408766Z log.warning( 2025-04-25T04:10:06.1409567Z "No env var found for GITHUB_OUTPUT, you must be running this code locally. Falling back to the deprecated print method." 2025-04-25T04:10:06.1410441Z ) 2025-04-25T04:10:06.1420639Z print(f"::set-output name={key}::{value}") 2025-04-25T04:10:06.1421222Z return 2025-04-25T04:10:06.1421449Z 2025-04-25T04:10:06.1421633Z with open(GITHUB_OUTPUT, "a") as f: 2025-04-25T04:10:06.1422198Z log.info(f"Setting output: {key}='{value}'") 2025-04-25T04:10:06.1422750Z f.write(f"{key}={value}\n") 2025-04-25T04:10:06.1423069Z 2025-04-25T04:10:06.1423075Z 2025-04-25T04:10:06.1423357Z def _str_comma_separated_to_set(value: str) -> frozenset[str]: 2025-04-25T04:10:06.1423965Z return frozenset( 2025-04-25T04:10:06.1424740Z filter(lambda itm: itm != "", map(str.strip, value.strip(" \n\t").split(","))) 2025-04-25T04:10:06.1425661Z ) 2025-04-25T04:10:06.1425874Z 2025-04-25T04:10:06.1425881Z 2025-04-25T04:10:06.1426051Z def parse_args() -> Any: 2025-04-25T04:10:06.1426597Z parser = ArgumentParser("Get dynamic rollout settings") 2025-04-25T04:10:06.1427590Z parser.add_argument("--github-token", type=str, required=True, help="GitHub token") 2025-04-25T04:10:06.1428328Z parser.add_argument( 2025-04-25T04:10:06.1428769Z "--github-issue-repo", 2025-04-25T04:10:06.1429246Z type=str, 2025-04-25T04:10:06.1429630Z required=False, 2025-04-25T04:10:06.1430058Z default="pytorch/test-infra", 2025-04-25T04:10:06.1430583Z help="GitHub repo to get the issue", 2025-04-25T04:10:06.1431096Z ) 2025-04-25T04:10:06.1431447Z parser.add_argument( 2025-04-25T04:10:06.1431874Z "--github-repo", 2025-04-25T04:10:06.1432260Z type=str, 2025-04-25T04:10:06.1432648Z required=True, 2025-04-25T04:10:06.1433085Z help="GitHub repo where CI is running", 2025-04-25T04:10:06.1433584Z ) 2025-04-25T04:10:06.1433931Z parser.add_argument( 2025-04-25T04:10:06.1434516Z "--github-issue", type=int, required=True, help="GitHub issue number" 2025-04-25T04:10:06.1435431Z ) 2025-04-25T04:10:06.1435874Z parser.add_argument( 2025-04-25T04:10:06.1436493Z "--github-actor", type=str, required=True, help="GitHub triggering_actor" 2025-04-25T04:10:06.1437142Z ) 2025-04-25T04:10:06.1437483Z parser.add_argument( 2025-04-25T04:10:06.1438087Z "--github-issue-owner", type=str, required=True, help="GitHub issue owner" 2025-04-25T04:10:06.1438747Z ) 2025-04-25T04:10:06.1439089Z parser.add_argument( 2025-04-25T04:10:06.1439708Z "--github-branch", type=str, required=True, help="Current GitHub branch or tag" 2025-04-25T04:10:06.1440387Z ) 2025-04-25T04:10:06.1440730Z parser.add_argument( 2025-04-25T04:10:06.1441156Z "--github-ref-type", 2025-04-25T04:10:06.1441592Z type=str, 2025-04-25T04:10:06.1441972Z required=True, 2025-04-25T04:10:06.1442435Z help="Current GitHub ref type, branch or tag", 2025-04-25T04:10:06.1442965Z ) 2025-04-25T04:10:06.1443306Z parser.add_argument( 2025-04-25T04:10:06.1443751Z "--eligible-experiments", 2025-04-25T04:10:06.1444241Z type=_str_comma_separated_to_set, 2025-04-25T04:10:06.1444735Z required=False, 2025-04-25T04:10:06.1445127Z default="", 2025-04-25T04:10:06.1446082Z help="comma separated list of experiments to check, if omitted all experiments marked with default=True are checked", 2025-04-25T04:10:06.1446971Z ) 2025-04-25T04:10:06.1447316Z parser.add_argument( 2025-04-25T04:10:06.1447715Z "--pr-number", 2025-04-25T04:10:06.1448102Z type=str, 2025-04-25T04:10:06.1448476Z required=False, 2025-04-25T04:10:06.1448859Z default="", 2025-04-25T04:10:06.1449297Z help="the optional PR number where this is run", 2025-04-25T04:10:06.1449825Z ) 2025-04-25T04:10:06.1450010Z 2025-04-25T04:10:06.1450186Z return parser.parse_args() 2025-04-25T04:10:06.1450480Z 2025-04-25T04:10:06.1450487Z 2025-04-25T04:10:06.1450866Z def get_gh_client(github_token: str) -> Github: # type: ignore[no-any-unimported] 2025-04-25T04:10:06.1451592Z auth = Auth.Token(github_token) 2025-04-25T04:10:06.1452073Z return Github(auth=auth) 2025-04-25T04:10:06.1452365Z 2025-04-25T04:10:06.1452371Z 2025-04-25T04:10:06.1452792Z def get_issue(gh: Github, repo: str, issue_num: int) -> Issue: # type: ignore[no-any-unimported] 2025-04-25T04:10:06.1454145Z repo = gh.get_repo(repo) 2025-04-25T04:10:06.1455098Z return repo.get_issue(number=issue_num) 2025-04-25T04:10:06.1455939Z 2025-04-25T04:10:06.1455951Z 2025-04-25T04:10:06.1456268Z def get_potential_pr_author( 2025-04-25T04:10:06.1457363Z github_token: str, repo: str, username: str, ref_type: str, ref_name: str 2025-04-25T04:10:06.1458818Z ) -> str: 2025-04-25T04:10:06.1459706Z # If the trigger was a new tag added by a bot, this is a ciflow case 2025-04-25T04:10:06.1461096Z # Fetch the actual username from the original PR. The PR number is 2025-04-25T04:10:06.1462429Z # embedded in the tag name: ciflow// 2025-04-25T04:10:06.1463141Z 2025-04-25T04:10:06.1463446Z gh = get_gh_client(github_token) 2025-04-25T04:10:06.1464043Z 2025-04-25T04:10:06.1464490Z if username == "pytorch-bot[bot]" and ref_type == "tag": 2025-04-25T04:10:06.1465857Z split_tag = ref_name.split("/") 2025-04-25T04:10:06.1466693Z if ( 2025-04-25T04:10:06.1467314Z len(split_tag) == 3 2025-04-25T04:10:06.1468103Z and split_tag[0] == "ciflow" 2025-04-25T04:10:06.1468966Z and split_tag[2].isnumeric() 2025-04-25T04:10:06.1469752Z ): 2025-04-25T04:10:06.1470425Z pr_number = split_tag[2] 2025-04-25T04:10:06.1471209Z try: 2025-04-25T04:10:06.1471889Z repository = gh.get_repo(repo) 2025-04-25T04:10:06.1472912Z pull = repository.get_pull(number=int(pr_number)) 2025-04-25T04:10:06.1473978Z except Exception as e: 2025-04-25T04:10:06.1474807Z raise Exception( # noqa: TRY002 2025-04-25T04:10:06.1476348Z f"issue with pull request {pr_number} from repo {repository}" 2025-04-25T04:10:06.1477485Z ) from e 2025-04-25T04:10:06.1478349Z return pull.user.login # type: ignore[no-any-return] 2025-04-25T04:10:06.1479491Z # In all other cases, return the original input username 2025-04-25T04:10:06.1480481Z return username 2025-04-25T04:10:06.1480880Z 2025-04-25T04:10:06.1480891Z 2025-04-25T04:10:06.1481240Z def is_exception_branch(branch: str) -> bool: 2025-04-25T04:10:06.1482105Z """ 2025-04-25T04:10:06.1483166Z Branches that get opted out of experiments by default, until they're explicitly enabled. 2025-04-25T04:10:06.1484450Z """ 2025-04-25T04:10:06.1485505Z return branch.split("/")[0] in {"main", "nightly", "release", "landchecks"} 2025-04-25T04:10:06.1486441Z 2025-04-25T04:10:06.1486470Z 2025-04-25T04:10:06.1486794Z def load_yaml(yaml_text: str) -> Any: 2025-04-25T04:10:06.1487587Z try: 2025-04-25T04:10:06.1488228Z data = yaml.safe_load(yaml_text) 2025-04-25T04:10:06.1489106Z return data 2025-04-25T04:10:06.1489788Z except yaml.YAMLError: 2025-04-25T04:10:06.1490553Z log.exception("Error loading YAML") 2025-04-25T04:10:06.1491431Z raise 2025-04-25T04:10:06.1491865Z 2025-04-25T04:10:06.1491877Z 2025-04-25T04:10:06.1492595Z def extract_settings_user_opt_in_from_text(rollout_state: str) -> tuple[str, str]: 2025-04-25T04:10:06.1493884Z """ 2025-04-25T04:10:06.1494935Z Extracts the text with settings, if any, and the opted in users from the rollout state. 2025-04-25T04:10:06.1496236Z 2025-04-25T04:10:06.1496814Z If the issue body contains "---" then the text above that is the settings 2025-04-25T04:10:06.1498084Z and the text below is the list of opted in users. 2025-04-25T04:10:06.1498799Z 2025-04-25T04:10:06.1499467Z If it doesn't contain "---" then the settings are empty and the rest is the users. 2025-04-25T04:10:06.1500667Z """ 2025-04-25T04:10:06.1501405Z rollout_state_parts = rollout_state.split("---") 2025-04-25T04:10:06.1502425Z if len(rollout_state_parts) >= 2: 2025-04-25T04:10:06.1503446Z return rollout_state_parts[0], rollout_state_parts[1] 2025-04-25T04:10:06.1504452Z else: 2025-04-25T04:10:06.1505091Z return "", rollout_state 2025-04-25T04:10:06.1505854Z 2025-04-25T04:10:06.1505866Z 2025-04-25T04:10:06.1506205Z class UserOptins(dict[str, list[str]]): 2025-04-25T04:10:06.1507035Z """ 2025-04-25T04:10:06.1507922Z Dictionary of users with a list of features they have opted into 2025-04-25T04:10:06.1509094Z """ 2025-04-25T04:10:06.1509447Z 2025-04-25T04:10:06.1509470Z 2025-04-25T04:10:06.1510043Z def parse_user_opt_in_from_text(user_optin_text: str) -> UserOptins: 2025-04-25T04:10:06.1511490Z """ 2025-04-25T04:10:06.1512732Z Parse the user opt-in text into a key value pair of username and the list of features they have opted into 2025-04-25T04:10:06.1513955Z 2025-04-25T04:10:06.1515011Z Users are GitHub usernames with the @ prefix. Each user is also a comma-separated list of features/experiments to enable. 2025-04-25T04:10:06.1516769Z - Example line: "@User1,lf,split_build" 2025-04-25T04:10:06.1517870Z - A "#" prefix indicates the user is opted out of all experiments 2025-04-25T04:10:06.1518889Z 2025-04-25T04:10:06.1518900Z 2025-04-25T04:10:06.1519151Z """ 2025-04-25T04:10:06.1519771Z optins = UserOptins() 2025-04-25T04:10:06.1520528Z for user in user_optin_text.split("\n"): 2025-04-25T04:10:06.1521323Z user = user.strip("\r\n\t -") 2025-04-25T04:10:06.1522231Z if not user or not user.startswith("@"): 2025-04-25T04:10:06.1523225Z # Not a valid user. Skip 2025-04-25T04:10:06.1524027Z continue 2025-04-25T04:10:06.1524448Z 2025-04-25T04:10:06.1524705Z if user: 2025-04-25T04:10:06.1525643Z usr_name = user.split(",")[0].strip("@") 2025-04-25T04:10:06.1527060Z optins[usr_name] = [exp.strip(" ") for exp in user.split(",")[1:]] 2025-04-25T04:10:06.1527920Z 2025-04-25T04:10:06.1528183Z return optins 2025-04-25T04:10:06.1528590Z 2025-04-25T04:10:06.1528607Z 2025-04-25T04:10:06.1529096Z def is_valid_experiment_name(experiment_name: str) -> bool: 2025-04-25T04:10:06.1578946Z """ 2025-04-25T04:10:06.1579734Z Check if the experiment name is valid. 2025-04-25T04:10:06.1580664Z A valid name: 2025-04-25T04:10:06.1581816Z - Contains only alphanumeric characters and the special characters "_" & "-" 2025-04-25T04:10:06.1583481Z - The special characters "_" & "-" shouldn't be the first or last characters 2025-04-25T04:10:06.1584775Z - Cannot contain spaces 2025-04-25T04:10:06.1585734Z """ 2025-04-25T04:10:06.1586119Z 2025-04-25T04:10:06.1586597Z valid_char_regex = r"^[a-zA-Z0-9]([\w-]*[a-zA-Z0-9])?$" 2025-04-25T04:10:06.1587803Z valid = bool(re.match(valid_char_regex, experiment_name)) 2025-04-25T04:10:06.1588549Z 2025-04-25T04:10:06.1588809Z if valid: 2025-04-25T04:10:06.1589419Z return True 2025-04-25T04:10:06.1589804Z 2025-04-25T04:10:06.1590047Z log.error( 2025-04-25T04:10:06.1592486Z f"Invalid experiment name: {experiment_name}. Experiment names should only contain alphanumeric characters, '_', and '-'. They cannot contain spaces, and the special characters '_' and '-' cannot be the first or last characters." 2025-04-25T04:10:06.1595473Z ) 2025-04-25T04:10:06.1596081Z return False 2025-04-25T04:10:06.1596473Z 2025-04-25T04:10:06.1596495Z 2025-04-25T04:10:06.1596986Z def parse_settings_from_text(settings_text: str) -> Settings: 2025-04-25T04:10:06.1598115Z """ 2025-04-25T04:10:06.1599045Z Parse the experiments from the issue body into a list of ExperimentSettings 2025-04-25T04:10:06.1600299Z """ 2025-04-25T04:10:06.1600870Z try: 2025-04-25T04:10:06.1601531Z if settings_text: 2025-04-25T04:10:06.1602695Z # Escape the backtick as well so that we can have the settings in a code block on the GH issue 2025-04-25T04:10:06.1603984Z # for easy reading 2025-04-25T04:10:06.1605250Z # Note: Using ascii for the backtick so that the cat step in _runner-determinator.yml doesn't choke on 2025-04-25T04:10:06.1607213Z # the backtick character in shell commands. 2025-04-25T04:10:06.1608239Z backtick = chr(96) # backtick character 2025-04-25T04:10:06.1609279Z settings_text = settings_text.strip(f"\r\n\t{backtick} ") 2025-04-25T04:10:06.1610386Z settings = load_yaml(settings_text) 2025-04-25T04:10:06.1611018Z 2025-04-25T04:10:06.1611753Z # For now we just load experiments. We can expand this if/when we add more settings 2025-04-25T04:10:06.1612960Z experiments = {} 2025-04-25T04:10:06.1613695Z 2025-04-25T04:10:06.1614294Z for exp_name, exp_settings in settings.get(SETTING_EXPERIMENTS).items(): 2025-04-25T04:10:06.1615713Z if not is_valid_experiment_name(exp_name): 2025-04-25T04:10:06.1617518Z # Exclude invalid experiments from the list. We log an error, but don't raise an exception so that other experiments can still be processed. 2025-04-25T04:10:06.1619287Z continue 2025-04-25T04:10:06.1619744Z 2025-04-25T04:10:06.1620015Z valid_settings = {} 2025-04-25T04:10:06.1620844Z for setting in exp_settings: 2025-04-25T04:10:06.1621777Z if setting not in Experiment._fields: 2025-04-25T04:10:06.1622681Z log.warning( 2025-04-25T04:10:06.1623886Z f"Unexpected setting in experiment: {setting} = {exp_settings[setting]}" 2025-04-25T04:10:06.1625151Z ) 2025-04-25T04:10:06.1626121Z else: 2025-04-25T04:10:06.1627009Z valid_settings[setting] = exp_settings[setting] 2025-04-25T04:10:06.1627762Z 2025-04-25T04:10:06.1628194Z experiments[exp_name] = Experiment(**valid_settings) 2025-04-25T04:10:06.1629199Z return Settings(experiments) 2025-04-25T04:10:06.1629556Z 2025-04-25T04:10:06.1629728Z except Exception: 2025-04-25T04:10:06.1630179Z log.exception("Failed to parse settings") 2025-04-25T04:10:06.1630569Z 2025-04-25T04:10:06.1630726Z return Settings() 2025-04-25T04:10:06.1630975Z 2025-04-25T04:10:06.1630982Z 2025-04-25T04:10:06.1631216Z def parse_settings(rollout_state: str) -> Settings: 2025-04-25T04:10:06.1631753Z """ 2025-04-25T04:10:06.1632169Z Parse settings, if any, from the rollout state. 2025-04-25T04:10:06.1632563Z 2025-04-25T04:10:06.1632883Z If the issue body contains "---" then the text above that is the settings 2025-04-25T04:10:06.1633603Z and the text below is the list of opted in users. 2025-04-25T04:10:06.1634008Z 2025-04-25T04:10:06.1634405Z If it doesn't contain "---" then the settings are empty and the default values are used. 2025-04-25T04:10:06.1635100Z """ 2025-04-25T04:10:06.1635916Z settings_text, _ = extract_settings_user_opt_in_from_text(rollout_state) 2025-04-25T04:10:06.1636653Z return parse_settings_from_text(settings_text) 2025-04-25T04:10:06.1637037Z 2025-04-25T04:10:06.1637044Z 2025-04-25T04:10:06.1637282Z def parse_users(rollout_state: str) -> UserOptins: 2025-04-25T04:10:06.1637816Z """ 2025-04-25T04:10:06.1638179Z Parse users from the rollout state. 2025-04-25T04:10:06.1638522Z 2025-04-25T04:10:06.1638664Z """ 2025-04-25T04:10:06.1639157Z _, users_text = extract_settings_user_opt_in_from_text(rollout_state) 2025-04-25T04:10:06.1639852Z return parse_user_opt_in_from_text(users_text) 2025-04-25T04:10:06.1640232Z 2025-04-25T04:10:06.1640239Z 2025-04-25T04:10:06.1640619Z def is_user_opted_in(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-04-25T04:10:06.1641319Z """ 2025-04-25T04:10:06.1641700Z Check if a user is opted into an experiment 2025-04-25T04:10:06.1642210Z """ 2025-04-25T04:10:06.1642625Z return experiment_name in user_optins.get(user, []) 2025-04-25T04:10:06.1643033Z 2025-04-25T04:10:06.1643039Z 2025-04-25T04:10:06.1643426Z def is_user_opted_out(user: str, user_optins: UserOptins, experiment_name: str) -> bool: 2025-04-25T04:10:06.1644131Z """ 2025-04-25T04:10:06.1644551Z Check if a user explicitly opted out of an experiment 2025-04-25T04:10:06.1645101Z """ 2025-04-25T04:10:06.1646083Z # if the experiment is prefixed with a "-", then it's an opt-out 2025-04-25T04:10:06.1646739Z experiment_optout = "-" + experiment_name 2025-04-25T04:10:06.1647346Z if experiment_optout not in user_optins.get(user, []): 2025-04-25T04:10:06.1647912Z return False 2025-04-25T04:10:06.1648153Z 2025-04-25T04:10:06.1648397Z if is_user_opted_in(user, user_optins, experiment_name): 2025-04-25T04:10:06.1649130Z log.warning( 2025-04-25T04:10:06.1649873Z f"User {user} is opted into experiment {experiment_name}, but also opted out of it. Defaulting to opting out" 2025-04-25T04:10:06.1650677Z ) 2025-04-25T04:10:06.1650879Z 2025-04-25T04:10:06.1651033Z return True 2025-04-25T04:10:06.1651284Z 2025-04-25T04:10:06.1651291Z 2025-04-25T04:10:06.1651451Z def get_runner_prefix( 2025-04-25T04:10:06.1651856Z rollout_state: str, 2025-04-25T04:10:06.1652285Z workflow_requestors: Iterable[str], 2025-04-25T04:10:06.1652773Z branch: str, 2025-04-25T04:10:06.1653237Z eligible_experiments: frozenset[str] = frozenset(), 2025-04-25T04:10:06.1653799Z is_canary: bool = False, 2025-04-25T04:10:06.1654266Z ) -> str: 2025-04-25T04:10:06.1654652Z settings = parse_settings(rollout_state) 2025-04-25T04:10:06.1655198Z user_optins = parse_users(rollout_state) 2025-04-25T04:10:06.1655721Z 2025-04-25T04:10:06.1655913Z fleet_prefix = "" 2025-04-25T04:10:06.1656318Z prefixes = [] 2025-04-25T04:10:06.1656902Z for experiment_name, experiment_settings in settings.experiments.items(): 2025-04-25T04:10:06.1657789Z if not experiment_settings.all_branches and is_exception_branch(branch): 2025-04-25T04:10:06.1658586Z log.info( 2025-04-25T04:10:06.1659220Z f"Branch {branch} is an exception branch. Not enabling experiment {experiment_name}." 2025-04-25T04:10:06.1659933Z ) 2025-04-25T04:10:06.1660279Z continue 2025-04-25T04:10:06.1660520Z 2025-04-25T04:10:06.1660691Z if eligible_experiments: 2025-04-25T04:10:06.1661207Z if experiment_name not in eligible_experiments: 2025-04-25T04:10:06.1661808Z exp_list = ", ".join(eligible_experiments) 2025-04-25T04:10:06.1662329Z log.info( 2025-04-25T04:10:06.1663067Z f"Skipping experiment '{experiment_name}', as it is not in the eligible_experiments list: {exp_list}" 2025-04-25T04:10:06.1663860Z ) 2025-04-25T04:10:06.1664231Z continue 2025-04-25T04:10:06.1664683Z elif not experiment_settings.default: 2025-04-25T04:10:06.1665178Z log.info( 2025-04-25T04:10:06.1665929Z f"Skipping experiment '{experiment_name}', as it is not a default experiment" 2025-04-25T04:10:06.1666614Z ) 2025-04-25T04:10:06.1666968Z continue 2025-04-25T04:10:06.1667201Z 2025-04-25T04:10:06.1667462Z # Is any workflow_requestor opted out to this experiment? 2025-04-25T04:10:06.1668043Z opted_out_users = [ 2025-04-25T04:10:06.1668469Z requestor 2025-04-25T04:10:06.1668887Z for requestor in workflow_requestors 2025-04-25T04:10:06.1669543Z if is_user_opted_out(requestor, user_optins, experiment_name) 2025-04-25T04:10:06.1670133Z ] 2025-04-25T04:10:06.1670340Z 2025-04-25T04:10:06.1670503Z if opted_out_users: 2025-04-25T04:10:06.1670926Z log.info( 2025-04-25T04:10:06.1671505Z f"{', '.join(opted_out_users)} have opted out of experiment {experiment_name}." 2025-04-25T04:10:06.1672161Z ) 2025-04-25T04:10:06.1672510Z continue 2025-04-25T04:10:06.1672743Z 2025-04-25T04:10:06.1673002Z # Is any workflow_requestor opted in to this experiment? 2025-04-25T04:10:06.1673576Z opted_in_users = [ 2025-04-25T04:10:06.1673996Z requestor 2025-04-25T04:10:06.1674414Z for requestor in workflow_requestors 2025-04-25T04:10:06.1675041Z if is_user_opted_in(requestor, user_optins, experiment_name) 2025-04-25T04:10:06.1675875Z ] 2025-04-25T04:10:06.1676093Z 2025-04-25T04:10:06.1676248Z enabled = False 2025-04-25T04:10:06.1676657Z if opted_in_users: 2025-04-25T04:10:06.1677071Z log.info( 2025-04-25T04:10:06.1677642Z f"{', '.join(opted_in_users)} have opted into experiment {experiment_name}." 2025-04-25T04:10:06.1678421Z ) 2025-04-25T04:10:06.1678782Z enabled = True 2025-04-25T04:10:06.1679052Z 2025-04-25T04:10:06.1679249Z elif experiment_settings.rollout_perc: 2025-04-25T04:10:06.1680029Z # If no user is opted in, then we randomly enable the experiment based on the rollout percentage 2025-04-25T04:10:06.1680907Z if random.uniform(0, 100) <= experiment_settings.rollout_perc: 2025-04-25T04:10:06.1681522Z log.info( 2025-04-25T04:10:06.1682328Z f"Based on rollout percentage of {experiment_settings.rollout_perc}%, enabling experiment {experiment_name}." 2025-04-25T04:10:06.1683182Z ) 2025-04-25T04:10:06.1683571Z enabled = True 2025-04-25T04:10:06.1683856Z 2025-04-25T04:10:06.1684004Z if enabled: 2025-04-25T04:10:06.1684398Z label = experiment_name 2025-04-25T04:10:06.1684915Z if experiment_name == LF_FLEET_EXPERIMENT: 2025-04-25T04:10:06.1685817Z # We give some special treatment to the "lf" experiment since determines the fleet we use 2025-04-25T04:10:06.1686658Z # - If it's enabled, then we always list it's prefix first 2025-04-25T04:10:06.1687489Z # - If we're in the canary branch, then we append ".c" to the lf prefix 2025-04-25T04:10:06.1688125Z if is_canary: 2025-04-25T04:10:06.1688591Z label += CANARY_FLEET_SUFFIX 2025-04-25T04:10:06.1689115Z fleet_prefix = label 2025-04-25T04:10:06.1689576Z else: 2025-04-25T04:10:06.1689986Z prefixes.append(label) 2025-04-25T04:10:06.1690319Z 2025-04-25T04:10:06.1690490Z if len(prefixes) > 1: 2025-04-25T04:10:06.1690911Z log.error( 2025-04-25T04:10:06.1691872Z f"Only a fleet and one other experiment can be enabled for a job at any time. Enabling {prefixes[0]} and ignoring the rest, which are {', '.join(prefixes[1:])}" 2025-04-25T04:10:06.1692908Z ) 2025-04-25T04:10:06.1693272Z prefixes = prefixes[:1] 2025-04-25T04:10:06.1693563Z 2025-04-25T04:10:06.1693734Z # Fleet always comes first 2025-04-25T04:10:06.1694169Z if fleet_prefix: 2025-04-25T04:10:06.1694602Z prefixes.insert(0, fleet_prefix) 2025-04-25T04:10:06.1694953Z 2025-04-25T04:10:06.1695190Z return ".".join(prefixes) + "." if prefixes else "" 2025-04-25T04:10:06.1695698Z 2025-04-25T04:10:06.1695705Z 2025-04-25T04:10:06.1696118Z def get_rollout_state_from_issue(github_token: str, repo: str, issue_num: int) -> str: 2025-04-25T04:10:06.1696843Z """ 2025-04-25T04:10:06.1697400Z Gets the first comment of the issue, which contains the desired rollout state. 2025-04-25T04:10:06.1697937Z 2025-04-25T04:10:06.1698298Z The default issue we use - https://github.com/pytorch/test-infra/issues/5132 2025-04-25T04:10:06.1698962Z """ 2025-04-25T04:10:06.1699328Z gh = get_gh_client(github_token) 2025-04-25T04:10:06.1699835Z issue = get_issue(gh, repo, issue_num) 2025-04-25T04:10:06.1700443Z return str(issue.get_comments()[0].body.strip("\n\t ")) 2025-04-25T04:10:06.1700860Z 2025-04-25T04:10:06.1700866Z 2025-04-25T04:10:06.1701236Z def download_json(url: str, headers: dict[str, str], num_retries: int = 3) -> Any: 2025-04-25T04:10:06.1701941Z for _ in range(num_retries): 2025-04-25T04:10:06.1702386Z try: 2025-04-25T04:10:06.1702787Z req = Request(url=url, headers=headers) 2025-04-25T04:10:06.1703417Z content = urlopen(req, timeout=5).read().decode("utf-8") 2025-04-25T04:10:06.1704020Z return json.loads(content) 2025-04-25T04:10:06.1704513Z except Exception as e: 2025-04-25T04:10:06.1705014Z log.warning(f"Could not download {url}: {e}") 2025-04-25T04:10:06.1705528Z 2025-04-25T04:10:06.1705879Z log.warning(f"All {num_retries} retries exhausted, downloading {url} failed") 2025-04-25T04:10:06.1706535Z return {} 2025-04-25T04:10:06.1706755Z 2025-04-25T04:10:06.1706762Z 2025-04-25T04:10:06.1706902Z @cache 2025-04-25T04:10:06.1707610Z def get_pr_info(github_repo: str, github_token: str, pr_number: int) -> dict[str, Any]: 2025-04-25T04:10:06.1708309Z """ 2025-04-25T04:10:06.1708678Z Dynamically get PR information 2025-04-25T04:10:06.1709146Z """ 2025-04-25T04:10:06.1709605Z github_api = f"https://api.github.com/repos/{github_repo}" 2025-04-25T04:10:06.1710188Z headers = { 2025-04-25T04:10:06.1710602Z "Accept": "application/vnd.github.v3+json", 2025-04-25T04:10:06.1711167Z "Authorization": f"token {github_token}", 2025-04-25T04:10:06.1711674Z } 2025-04-25T04:10:06.1712068Z json_response: dict[str, Any] = download_json( 2025-04-25T04:10:06.1712635Z url=f"{github_api}/issues/{pr_number}", 2025-04-25T04:10:06.1713141Z headers=headers, 2025-04-25T04:10:06.1713544Z ) 2025-04-25T04:10:06.1713739Z 2025-04-25T04:10:06.1713905Z if not json_response: 2025-04-25T04:10:06.1714435Z log.warning(f"Failed to get the labels for #{pr_number}") 2025-04-25T04:10:06.1715015Z return {} 2025-04-25T04:10:06.1715254Z 2025-04-25T04:10:06.1715577Z return json_response 2025-04-25T04:10:06.1715923Z 2025-04-25T04:10:06.1715929Z 2025-04-25T04:10:06.1716298Z def get_labels(github_repo: str, github_token: str, pr_number: int) -> set[str]: 2025-04-25T04:10:06.1717111Z """ 2025-04-25T04:10:06.1717874Z Dynamically get the latest list of labels from the pull request 2025-04-25T04:10:06.1718812Z """ 2025-04-25T04:10:06.1719477Z pr_info = get_pr_info(github_repo, github_token, pr_number) 2025-04-25T04:10:06.1720432Z return { 2025-04-25T04:10:06.1721324Z label.get("name") for label in pr_info.get("labels", []) if label.get("name") 2025-04-25T04:10:06.1722396Z } 2025-04-25T04:10:06.1722701Z 2025-04-25T04:10:06.1722711Z 2025-04-25T04:10:06.1722955Z def main() -> None: 2025-04-25T04:10:06.1723565Z args = parse_args() 2025-04-25T04:10:06.1723987Z 2025-04-25T04:10:06.1724339Z runner_label_prefix = DEFAULT_LABEL_PREFIX 2025-04-25T04:10:06.1725014Z 2025-04-25T04:10:06.1725525Z # Check if the PR is opt-out 2025-04-25T04:10:06.1726364Z if args.pr_number: 2025-04-25T04:10:06.1727454Z labels = get_labels(args.github_repo, args.github_token, int(args.pr_number)) 2025-04-25T04:10:06.1728771Z if OPT_OUT_LABEL in labels: 2025-04-25T04:10:06.1729572Z log.info( 2025-04-25T04:10:06.1730677Z f"Opt-out runner determinator because #{args.pr_number} has {OPT_OUT_LABEL} label" 2025-04-25T04:10:06.1731947Z ) 2025-04-25T04:10:06.1732856Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-04-25T04:10:06.1733927Z sys.exit() 2025-04-25T04:10:06.1734344Z 2025-04-25T04:10:06.1734584Z try: 2025-04-25T04:10:06.1735237Z rollout_state = get_rollout_state_from_issue( 2025-04-25T04:10:06.1736582Z args.github_token, args.github_issue_repo, args.github_issue 2025-04-25T04:10:06.1737611Z ) 2025-04-25T04:10:06.1737940Z 2025-04-25T04:10:06.1738289Z username = get_potential_pr_author( 2025-04-25T04:10:06.1739204Z args.github_token, 2025-04-25T04:10:06.1739982Z args.github_repo, 2025-04-25T04:10:06.1740792Z args.github_actor, 2025-04-25T04:10:06.1741550Z args.github_ref_type, 2025-04-25T04:10:06.1742342Z args.github_branch, 2025-04-25T04:10:06.1743067Z ) 2025-04-25T04:10:06.1743420Z 2025-04-25T04:10:06.1743873Z is_canary = args.github_repo == "pytorch/pytorch-canary" 2025-04-25T04:10:06.1744624Z 2025-04-25T04:10:06.1744984Z runner_label_prefix = get_runner_prefix( 2025-04-25T04:10:06.1746106Z rollout_state, 2025-04-25T04:10:06.1746904Z (args.github_issue_owner, username), 2025-04-25T04:10:06.1747807Z args.github_branch, 2025-04-25T04:10:06.1748674Z args.eligible_experiments, 2025-04-25T04:10:06.1749506Z is_canary, 2025-04-25T04:10:06.1750183Z ) 2025-04-25T04:10:06.1750517Z 2025-04-25T04:10:06.1751103Z except Exception as e: 2025-04-25T04:10:06.1751867Z log.error( 2025-04-25T04:10:06.1753043Z f"Failed to get issue. Defaulting to Meta runners and no experiments. Exception: {e}" 2025-04-25T04:10:06.1754386Z ) 2025-04-25T04:10:06.1754746Z 2025-04-25T04:10:06.1755450Z set_github_output(GH_OUTPUT_KEY_LABEL_TYPE, runner_label_prefix) 2025-04-25T04:10:06.1756294Z 2025-04-25T04:10:06.1756309Z 2025-04-25T04:10:06.1756586Z if __name__ == "__main__": 2025-04-25T04:10:06.1757277Z main() 2025-04-25T04:10:06.1757605Z 2025-04-25T04:10:06.1887624Z ##[group]Run python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-04-25T04:10:06.1889014Z python3 -m pip install urllib3==1.26.18 PyGithub==2.3.0 2025-04-25T04:10:06.1935721Z shell: /usr/bin/bash -e {0} 2025-04-25T04:10:06.1936496Z env: 2025-04-25T04:10:06.1937449Z GITHUB_TOKEN: *** 2025-04-25T04:10:06.1938126Z ISSUE_NUMBER: 5132 2025-04-25T04:10:06.1938820Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-04-25T04:10:06.1939619Z ISSUE_OWNER: 2025-04-25T04:10:06.1940239Z CHECK_EXPERIMENTS: 2025-04-25T04:10:06.1940891Z PR_NUMBER: 2025-04-25T04:10:06.1941476Z ##[endgroup] 2025-04-25T04:10:06.5914147Z Defaulting to user installation because normal site-packages is not writeable 2025-04-25T04:10:06.9804048Z Collecting urllib3==1.26.18 2025-04-25T04:10:07.0594419Z Downloading urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB) 2025-04-25T04:10:07.0898705Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48.9/48.9 kB 2.0 MB/s eta 0:00:00 2025-04-25T04:10:07.1122294Z Collecting PyGithub==2.3.0 2025-04-25T04:10:07.1310238Z Downloading PyGithub-2.3.0-py3-none-any.whl.metadata (3.8 kB) 2025-04-25T04:10:07.1733167Z Collecting pynacl>=1.4.0 (from PyGithub==2.3.0) 2025-04-25T04:10:07.1923586Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl.metadata (8.6 kB) 2025-04-25T04:10:07.1974963Z Requirement already satisfied: requests>=2.14.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (2.31.0) 2025-04-25T04:10:07.1996413Z Requirement already satisfied: pyjwt>=2.4.0 in /usr/lib/python3/dist-packages (from pyjwt[crypto]>=2.4.0->PyGithub==2.3.0) (2.7.0) 2025-04-25T04:10:07.2019344Z Requirement already satisfied: typing-extensions>=4.0.0 in /usr/lib/python3/dist-packages (from PyGithub==2.3.0) (4.10.0) 2025-04-25T04:10:07.2266362Z Collecting Deprecated (from PyGithub==2.3.0) 2025-04-25T04:10:07.2463655Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl.metadata (5.7 kB) 2025-04-25T04:10:07.2691953Z Requirement already satisfied: cryptography>=3.4.0 in /usr/lib/python3/dist-packages (from pyjwt[crypto]>=2.4.0->PyGithub==2.3.0) (41.0.7) 2025-04-25T04:10:07.3756065Z Collecting cffi>=1.4.1 (from pynacl>=1.4.0->PyGithub==2.3.0) 2025-04-25T04:10:07.3946407Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB) 2025-04-25T04:10:07.4969331Z Collecting wrapt<2,>=1.10 (from Deprecated->PyGithub==2.3.0) 2025-04-25T04:10:07.5159548Z Downloading wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.4 kB) 2025-04-25T04:10:07.5349486Z Collecting pycparser (from cffi>=1.4.1->pynacl>=1.4.0->PyGithub==2.3.0) 2025-04-25T04:10:07.5539636Z Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes) 2025-04-25T04:10:07.5923213Z Downloading urllib3-1.26.18-py2.py3-none-any.whl (143 kB) 2025-04-25T04:10:07.6173220Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 143.8/143.8 kB 6.0 MB/s eta 0:00:00 2025-04-25T04:10:07.6366909Z Downloading PyGithub-2.3.0-py3-none-any.whl (354 kB) 2025-04-25T04:10:07.6617535Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.4/354.4 kB 15.4 MB/s eta 0:00:00 2025-04-25T04:10:07.6805093Z Downloading PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (856 kB) 2025-04-25T04:10:07.7084794Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 856.7/856.7 kB 32.2 MB/s eta 0:00:00 2025-04-25T04:10:07.7274108Z Downloading Deprecated-1.2.18-py2.py3-none-any.whl (10.0 kB) 2025-04-25T04:10:07.7488644Z Downloading cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (479 kB) 2025-04-25T04:10:07.7565710Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 479.4/479.4 kB 78.8 MB/s eta 0:00:00 2025-04-25T04:10:07.7757154Z Downloading wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (89 kB) 2025-04-25T04:10:07.7822400Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.2/89.2 kB 18.6 MB/s eta 0:00:00 2025-04-25T04:10:07.8018815Z Downloading pycparser-2.22-py3-none-any.whl (117 kB) 2025-04-25T04:10:07.8069388Z ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.6/117.6 kB 30.6 MB/s eta 0:00:00 2025-04-25T04:10:08.0907192Z Installing collected packages: wrapt, urllib3, pycparser, Deprecated, cffi, pynacl, PyGithub 2025-04-25T04:10:08.6151420Z Successfully installed Deprecated-1.2.18 PyGithub-2.3.0 cffi-1.17.1 pycparser-2.22 pynacl-1.5.0 urllib3-1.26.18 wrapt-1.17.2 2025-04-25T04:10:08.6913009Z ##[group]Run curr_branch="ciflow/trunk/148893" 2025-04-25T04:10:08.6913421Z curr_branch="ciflow/trunk/148893" 2025-04-25T04:10:08.6913707Z curr_ref_type="tag" 2025-04-25T04:10:08.6913989Z echo "Current branch is '$curr_branch'" 2025-04-25T04:10:08.6914286Z  2025-04-25T04:10:08.6914513Z python3 runner_determinator.py \ 2025-04-25T04:10:08.6914834Z  --github-token "$GITHUB_TOKEN" \ 2025-04-25T04:10:08.6915190Z  --github-issue "$ISSUE_NUMBER" \ 2025-04-25T04:10:08.6915780Z  --github-branch "$curr_branch" \ 2025-04-25T04:10:08.6916088Z  --github-actor "$TRIGGERING_ACTOR" \ 2025-04-25T04:10:08.6916411Z  --github-issue-owner "$ISSUE_OWNER" \ 2025-04-25T04:10:08.6916735Z  --github-ref-type "$curr_ref_type" \ 2025-04-25T04:10:08.6917042Z  --github-repo "$GITHUB_REPOSITORY" \ 2025-04-25T04:10:08.6917440Z  --eligible-experiments "$CHECK_EXPERIMENTS" \ 2025-04-25T04:10:08.6917818Z  --pr-number "${PR_NUMBER}" 2025-04-25T04:10:08.6949016Z shell: /usr/bin/bash -e {0} 2025-04-25T04:10:08.6949272Z env: 2025-04-25T04:10:08.6949909Z GITHUB_TOKEN: *** 2025-04-25T04:10:08.6950141Z ISSUE_NUMBER: 5132 2025-04-25T04:10:08.6950380Z TRIGGERING_ACTOR: pytorch-bot[bot] 2025-04-25T04:10:08.6950658Z ISSUE_OWNER: 2025-04-25T04:10:08.6950865Z CHECK_EXPERIMENTS: 2025-04-25T04:10:08.6951095Z PR_NUMBER: 2025-04-25T04:10:08.6951295Z ##[endgroup] 2025-04-25T04:10:08.7000947Z Current branch is 'ciflow/trunk/148893' 2025-04-25T04:10:11.4834230Z INFO : Based on rollout percentage of 100%, enabling experiment ephemeral. 2025-04-25T04:10:11.4835202Z INFO : Setting output: label-type='ephemeral.' 2025-04-25T04:10:11.5143785Z Evaluate and set job outputs 2025-04-25T04:10:11.5150422Z Set output 'label-type' 2025-04-25T04:10:11.5152207Z Cleaning up orphan processes