# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. import logging import os import re import subprocess import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent assert (REPO_ROOT / "Jenkinsfile").exists class RelativePathFilter(logging.Filter): def filter(self, record): path = Path(record.pathname).resolve() record.relativepath = str(path.relative_to(REPO_ROOT)) return True def init_log(): logging.basicConfig( format="[%(relativepath)s:%(lineno)d %(levelname)-1s] %(message)s", level=logging.INFO ) # Flush on every log call (logging and then calling subprocess.run can make # the output look confusing) logging.root.handlers[0].addFilter(RelativePathFilter()) logging.root.handlers[0].flush = sys.stderr.flush class Sh: def __init__(self, env=None, cwd=None): self.env = os.environ.copy() if env is not None: self.env.update(env) self.cwd = cwd def tee(self, cmd: str, **kwargs): """ Run 'cmd' in a shell then return the (process, stdout) as a tuple """ logging.info(f"+ {cmd}") kwargs = { **self._default_popen_flags(), **kwargs, "stdout": subprocess.PIPE, } proc = subprocess.Popen(cmd, **kwargs) stdout = [] def _tee_output(s): stdout.append(s) print(s, end="") while proc.poll() is None: _tee_output(proc.stdout.readline()) _tee_output(proc.stdout.read()) stdout = "".join(stdout) if proc.returncode: raise subprocess.CalledProcessError(proc.returncode, proc.args, stdout) return proc, stdout def run(self, cmd: str, **kwargs): logging.info(f"+ {cmd}") kwargs = { **self._default_popen_flags(), "check": True, **kwargs, } return subprocess.run(cmd, **kwargs) def _default_popen_flags(self): return { "shell": True, "env": self.env, "encoding": "utf-8", "cwd": self.cwd, } def tags_from_title(title: str) -> list[str]: tags = re.findall(r"\[(.*?)\]", title) tags = [t.strip() for t in tags] return tags