diff --git a/.circleci/config.yml b/.circleci/config.yml index 53a1b061fb..2b2cd60c3d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,3 @@ -#adapted from https://raw.githubusercontent.com/ethereum-optimism/optimism/develop/.circleci/config.yml version: 2.1 executors: @@ -11,6 +10,19 @@ executors: DOCKER_BUILDKIT: 1 commands: + check-changed: + description: "Conditionally halts a step if certain modules change" + parameters: + patterns: + type: string + description: "Comma-separated list of dependencies" + steps: + - run: + name: "Check for changes" + command: | + cd ops/check-changed + pip3 install -r requirements.txt + python3 main.py "<>" download-solidity-compilers: steps: - checkout @@ -53,6 +65,9 @@ commands: description: Folder to run tests in type: string steps: + - check-changed: + patterns: common-ts,contracts,core-utils,data-transport-layer,message-relayer,regenesis-surgery,sdk,integration-tests,l2geth,packages/boba/account-abstraction,packages/boba/bobalink,packages/boba/bundler,packages/boba/bundler_sdk,packages/boba/bundler_utils,packages/boba/contracts,packages/boba/gas-price-oracle,packages/boba/register,packages/boba/teleportation,ops,ops_boba + - run: name: Build the project command: yarn && yarn build @@ -140,15 +155,8 @@ jobs: - image: cimg/postgres:14.1 steps: - checkout - - run: - name: Check if we should run - command: | - shopt -s inherit_errexit - CHANGED=$(check-changed "(<>|<>)" || echo "TRUE") - echo $CHANGED - if [[ "$CHANGED" = "FALSE" ]]; then - circleci step halt - fi + - check-changed: + patterns: go/proxyd - run: name: Lint command: golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint -e "errors.As" -e "errors.Is" ./... diff --git a/ops/check-changed/.gitignore b/ops/check-changed/.gitignore new file mode 100644 index 0000000000..f5e96dbfae --- /dev/null +++ b/ops/check-changed/.gitignore @@ -0,0 +1 @@ +venv \ No newline at end of file diff --git a/ops/check-changed/main.py b/ops/check-changed/main.py new file mode 100644 index 0000000000..256d2163e4 --- /dev/null +++ b/ops/check-changed/main.py @@ -0,0 +1,134 @@ +import logging.config +import os +import re +import subprocess +import sys + +from github import Github + +REBUILD_ALL_PATTERNS = [ + r'^\.circleci/\.*', + r'^\.github/\.*', + r'^package\.json', + r'^yarn\.lock', + r'ops/check-changed/.*' +] + +WHITELISTED_BRANCHES = { + 'master', + 'develop' +} + +LOGGING_CONFIG = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'standard': { + 'format': '%(asctime)s [%(levelname)s]: %(message)s' + }, + }, + 'handlers': { + 'default': { + 'level': 'INFO', + 'formatter': 'standard', + 'class': 'logging.StreamHandler', + 'stream': 'ext://sys.stderr' + }, + }, + 'loggers': { + '': { + 'handlers': ['default'], + 'level': 'INFO', + 'propagate': False + }, + } +} + +logging.config.dictConfig(LOGGING_CONFIG) +log = logging.getLogger(__name__) + + +def main(): + patterns = sys.argv[1].split(',') + patterns = patterns + REBUILD_ALL_PATTERNS + + fp = os.path.realpath(__file__) + monorepo_path = os.path.realpath(os.path.join(fp, '..', '..')) + + log.info('Discovered monorepo path: %s', monorepo_path) + current_branch = git_cmd('rev-parse --abbrev-ref HEAD', monorepo_path) + log.info('Current branch: %s', current_branch) + + if current_branch in WHITELISTED_BRANCHES: + log.info('Current branch %s is whitelisted, triggering build', current_branch) + exit_build() + + pr_urls = os.getenv('CIRCLE_PULL_REQUESTS', None) + pr_urls = pr_urls.split(',') if pr_urls else [] + if len(pr_urls) == 0: + log.info('Not a PR build, triggering build') + exit_build() + if len(pr_urls) > 1: + log.warning('Multiple PR URLs found, choosing the first one. PRs found:') + for url in pr_urls: + log.warning(url) + + gh_token = os.getenv('GITHUB_ACCESS_TOKEN') + if gh_token is None: + log.info('No GitHub access token found - likely a fork. Triggering build') + exit_build() + + g = Github(gh_token) + try: + g.get_user() + repo = g.get_repo(os.getenv('CIRCLE_PROJECT_USERNAME') + '/' + os.getenv('CIRCLE_PROJECT_REPONAME')) + except Exception: + log.exception('Failed to get repo from GitHub') + exit_build() + + pr = repo.get_pull(int(pr_urls[0].split('/')[-1])) + log.info('Found PR: %s', pr.url) + + base_sha = pr.base.sha + head_sha = pr.head.sha + + diffs = git_cmd('diff --name-only {}...{}'.format(base_sha, head_sha), monorepo_path).split('\n') + log.info('Found diff. Checking for matches...') + for diff in diffs: + if match_path(diff, patterns): + log.info('Match found, triggering build') + exit_build() + else: + log.info('❌ no match found on %s', diff) + + log.info('No matches found, skipping build') + exit_nobuild() + + +def git_cmd(cmd, cwd): + return subprocess.check_output(['git'] + cmd.split(' '), cwd=cwd).decode('utf-8').strip() + + +def match_path(path, patterns): + for pattern in patterns: + if re.search(pattern, path): + log.info('✅ match found on %s: %s', path, pattern) + return True + return False + + +def exit_build(): + sys.exit(0) + + +def exit_nobuild(): + subprocess.check_call(['circleci', 'step', 'halt']) + sys.exit(0) + + +if __name__ == '__main__': + try: + main() + except Exception: + log.exception('Unhandled exception, triggering build') + exit_build() diff --git a/ops/check-changed/requirements.txt b/ops/check-changed/requirements.txt new file mode 100644 index 0000000000..0b1fcc85a0 --- /dev/null +++ b/ops/check-changed/requirements.txt @@ -0,0 +1,12 @@ +certifi==2022.12.7 +cffi==1.15.1 +charset-normalizer==2.1.1 +Deprecated==1.2.13 +idna==3.4 +pycparser==2.21 +PyGithub==1.57 +PyJWT==2.6.0 +PyNaCl==1.5.0 +requests==2.28.1 +urllib3==1.26.13 +wrapt==1.14.1