diff --git a/.github/bin/bump_dependency.py b/.github/bin/bump_dependency.py new file mode 100644 index 000000000000..e658e4c3d5b1 --- /dev/null +++ b/.github/bin/bump_dependency.py @@ -0,0 +1,194 @@ +import argparse +import os +import re +import subprocess +import sys +from datetime import datetime + + +def get_remote_commit_sha(repo_url: str, branch: str) -> str: + output = subprocess.check_output( + ["git", "ls-remote", repo_url, f"refs/heads/{branch}"], text=True + ) + return output.split("\t")[0] + + +def get_remote_latest_tag(repo_url: str, tag_pattern: str) -> str: + output = subprocess.check_output( + ["git", "ls-remote", "--tags", repo_url], text=True + ) + tags = [] + for line in output.split("\n"): + if line.strip(): + parts = line.split("\t") + if len(parts) == 2: + ref = parts[1] + if ref.startswith("refs/tags/") and not ref.endswith("^{}"): + tag = ref.replace("refs/tags/", "") + if re.match(tag_pattern + "$", tag): + tags.append(tag) + + def version_key(tag: str) -> tuple[int, ...]: + version = tag.lstrip("v") + return tuple(map(int, version.split("."))) + + return sorted(tags, key=version_key)[-1] + + +def get_current_version_from_file(file_path: str, pattern: str) -> str: + with open(file_path) as f: + content = f.read() + + match = re.search(pattern, content) + return match.group(1) + + +def update_file_version( + file_path: str, old_pattern: str, new_value: str, comment_pattern: str +) -> None: + with open(file_path) as f: + content = f.read() + + new_content = re.sub(old_pattern, new_value, content) + + current_date = datetime.now().strftime("%b %d, %Y") + new_content = re.sub( + comment_pattern, + lambda m: m.group(0).split(", as of")[0] + f", as of {current_date}.", + new_content, + ) + + with open(file_path, "w") as f: + f.write(new_content) + + +def generate_commit_message( + repo_name: str, + repo_url: str, + old_version: str, + new_version: str, + is_tag: bool, + commit_url_template: str, + diff_url_template: str, +) -> str: + if is_tag: + version_link = ( + f"[Tag: {new_version}]({repo_url}/releases/tag/{new_version})" + ) + diff_url = diff_url_template.format( + repo_url=repo_url, old_version=old_version, new_version=new_version + ) + diff_link = f"[Diff]({diff_url})" + description = "between the previously used tag and the new tag." + else: + commit_url = commit_url_template.format( + repo_url=repo_url, version=new_version + ) + version_link = f"[Commit: {new_version}]({commit_url})" + diff_url = diff_url_template.format( + repo_url=repo_url, old_version=old_version, new_version=new_version + ) + diff_link = f"[Diff]({diff_url})" + description = ( + "between the last commit hash merged to this repository " + "and the new commit." + ) + + return f"## {repo_name}\n{version_link}\n\n{diff_link} {description}" + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Bump a single dependency version" + ) + parser.add_argument( + "--name", required=True, help="Display name for the dependency" + ) + parser.add_argument("--repo-url", required=True, help="Git repository URL") + parser.add_argument( + "--branch", default="main", help="Branch to check (default: main)" + ) + parser.add_argument( + "--file-path", required=True, help="File containing current version" + ) + parser.add_argument( + "--current-version-pattern", + required=True, + help="Regex to extract current version (group 1)", + ) + parser.add_argument( + "--update-pattern", required=True, help="Regex pattern for replacement" + ) + parser.add_argument( + "--comment-pattern", + required=True, + help="Regex pattern for comment update", + ) + parser.add_argument( + "--tag", action="store_true", help="Check tags instead of commits" + ) + parser.add_argument( + "--tag-pattern", default=r"v[0-9\.]*", help="Pattern for tag matching" + ) + parser.add_argument( + "--commit-url-template", + default="{repo_url}/commit/{version}", + help="Template for commit URLs", + ) + parser.add_argument( + "--diff-url-template", + default="{repo_url}/compare/{old_version}...{new_version}", + help="Template for diff URLs", + ) + + args = parser.parse_args() + + current_version = get_current_version_from_file( + args.file_path, args.current_version_pattern + ) + + if args.tag: + latest_version = get_remote_latest_tag(args.repo_url, args.tag_pattern) + else: + latest_version = get_remote_commit_sha(args.repo_url, args.branch) + + if current_version == latest_version: + print(f"{args.name}: No update needed (current: {current_version})") + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write("HAS_UPDATES=false\n") + return 0 + + print( + f"{args.name}: Update available " + f"({current_version} -> {latest_version})" + ) + + replacement = args.update_pattern.replace("{new_version}", latest_version) + update_file_version( + args.file_path, + args.current_version_pattern, + replacement, + args.comment_pattern, + ) + + commit_msg = generate_commit_message( + args.name, + args.repo_url, + current_version, + latest_version, + args.tag, + args.commit_url_template, + args.diff_url_template, + ) + + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write("COMMIT_MSG<> $GITHUB_OUTPUT - echo "COMMIT_MSG<> $GITHUB_OUTPUT - echo -e "## BoringSSL\n[Commit: ${SHA}](https://boringssl.googlesource.com/boringssl/+/${SHA})\n\n[Diff](https://boringssl.googlesource.com/boringssl/+/${LAST_COMMIT}..${SHA}) between the last commit hash merged to this repository and the new commit." >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - fi - - id: check-sha-openssl - run: | - SHA=$(git ls-remote https://github.com/openssl/openssl refs/heads/master | cut -f1) - LAST_COMMIT=$(grep openssl .github/workflows/ci.yml | grep TYPE | grep -oE '[a-f0-9]{40}') - if ! grep -q "$SHA" .github/workflows/ci.yml; then - echo "COMMIT_SHA=${SHA}" >> $GITHUB_OUTPUT - echo "COMMIT_MSG<> $GITHUB_OUTPUT - echo -e "## OpenSSL\n[Commit: ${SHA}](https://github.com/openssl/openssl/commit/${SHA})\n\n[Diff](https://github.com/openssl/openssl/compare/${LAST_COMMIT}...${SHA}) between the last commit hash merged to this repository and the new commit." >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - fi - - id: check-tag-aws-lc - run: | - # Get the latest tag from AWS-LC repository - LATEST_TAG=$(git ls-remote --tags https://github.com/aws/aws-lc.git | grep -o 'refs/tags/v[0-9\.]*$' | sort -V | tail -1 | sed 's|refs/tags/||') - CURRENT_TAG=$(grep aws-lc .github/workflows/ci.yml | grep VERSION | grep -o 'v[0-9\.]*') - - if [ "$LATEST_TAG" != "$CURRENT_TAG" ]; then - echo "NEW_TAG=${LATEST_TAG}" >> $GITHUB_OUTPUT - echo "COMMIT_MSG<> $GITHUB_OUTPUT - echo -e "## AWS-LC\n[Tag: ${LATEST_TAG}](https://github.com/aws/aws-lc/releases/tag/${LATEST_TAG})\n\n[Diff](https://github.com/aws/aws-lc/compare/${CURRENT_TAG}...${LATEST_TAG}) between the previously used tag and the new tag." >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - fi - - name: Update boring - run: | - set -xe - CURRENT_DATE=$(date "+%b %d, %Y") - sed -E -i "s/Latest commit on the BoringSSL main branch.*/Latest commit on the BoringSSL main branch, as of ${CURRENT_DATE}./" .github/workflows/ci.yml - sed -E -i "s/TYPE: \"boringssl\", VERSION: \"[0-9a-f]{40}\"/TYPE: \"boringssl\", VERSION: \"${COMMIT_SHA}\"/" .github/workflows/ci.yml - git status - if: steps.check-sha-boring.outputs.COMMIT_SHA - env: - COMMIT_SHA: ${{ steps.check-sha-boring.outputs.COMMIT_SHA }} - - name: Update OpenSSL + python3 .github/bin/bump_dependency.py \ + --name "BoringSSL" \ + --repo-url "https://boringssl.googlesource.com/boringssl" \ + --branch "main" \ + --file-path ".github/workflows/ci.yml" \ + --current-version-pattern 'TYPE: "boringssl", VERSION: "([a-f0-9]{40})"' \ + --update-pattern 'TYPE: "boringssl", VERSION: "{new_version}"' \ + --comment-pattern 'Latest commit on the BoringSSL main branch.*?\.' \ + --commit-url-template "{repo_url}/+/{version}" \ + --diff-url-template "{repo_url}/+/{old_version}..{new_version}" + - id: bump-openssl run: | - set -xe - CURRENT_DATE=$(date "+%b %d, %Y") - sed -E -i "s/Latest commit on the OpenSSL master branch.*/Latest commit on the OpenSSL master branch, as of ${CURRENT_DATE}./" .github/workflows/ci.yml - sed -E -i "s/TYPE: \"openssl\", VERSION: \"[0-9a-f]{40}\"/TYPE: \"openssl\", VERSION: \"${COMMIT_SHA}\"/" .github/workflows/ci.yml - git status - if: steps.check-sha-openssl.outputs.COMMIT_SHA - env: - COMMIT_SHA: ${{ steps.check-sha-openssl.outputs.COMMIT_SHA }} - - name: Update AWS-LC + python3 .github/bin/bump_dependency.py \ + --name "OpenSSL" \ + --repo-url "https://github.com/openssl/openssl" \ + --branch "master" \ + --file-path ".github/workflows/ci.yml" \ + --current-version-pattern 'TYPE: "openssl", VERSION: "([a-f0-9]{40})"' \ + --update-pattern 'TYPE: "openssl", VERSION: "{new_version}"' \ + --comment-pattern 'Latest commit on the OpenSSL master branch.*?\.' + - id: bump-awslc run: | - set -xe - CURRENT_DATE=$(date "+%b %d, %Y") - sed -E -i "s/Latest tag of AWS-LC main branch, as of .*/Latest tag of AWS-LC main branch, as of ${CURRENT_DATE}./" .github/workflows/ci.yml - sed -E -i "s/TYPE: \"aws-lc\", VERSION: \"v[0-9\.]*\"/TYPE: \"aws-lc\", VERSION: \"${NEW_TAG}\"/" .github/workflows/ci.yml - git status - if: steps.check-tag-aws-lc.outputs.NEW_TAG - env: - NEW_TAG: ${{ steps.check-tag-aws-lc.outputs.NEW_TAG }} + python3 .github/bin/bump_dependency.py \ + --name "AWS-LC" \ + --repo-url "https://github.com/aws/aws-lc" \ + --branch "main" \ + --file-path ".github/workflows/ci.yml" \ + --current-version-pattern 'TYPE: "aws-lc", VERSION: "(v[0-9\.]*)"' \ + --update-pattern 'TYPE: "aws-lc", VERSION: "{new_version}"' \ + --comment-pattern 'Latest tag of AWS-LC main branch, as of .*?\.' \ + --tag \ + --tag-pattern 'v[0-9\.]*' + - name: Check for updates + run: git status - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0 id: generate-token with: app_id: ${{ secrets.BORINGBOT_APP_ID }} private_key: ${{ secrets.BORINGBOT_PRIVATE_KEY }} - if: steps.check-sha-boring.outputs.COMMIT_SHA || steps.check-sha-openssl.outputs.COMMIT_SHA || steps.check-tag-aws-lc.outputs.NEW_TAG + if: steps.bump-boringssl.outputs.HAS_UPDATES == 'true' || steps.bump-openssl.outputs.HAS_UPDATES == 'true' || steps.bump-awslc.outputs.HAS_UPDATES == 'true' - name: Create Pull Request uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: @@ -94,8 +67,8 @@ jobs: title: "Bump BoringSSL, OpenSSL, AWS-LC in CI" author: "pyca-boringbot[bot] " body: | - ${{ steps.check-sha-boring.outputs.COMMIT_MSG }} - ${{ steps.check-sha-openssl.outputs.COMMIT_MSG }} - ${{ steps.check-tag-aws-lc.outputs.COMMIT_MSG }} + ${{ steps.bump-boringssl.outputs.COMMIT_MSG }} + ${{ steps.bump-openssl.outputs.COMMIT_MSG }} + ${{ steps.bump-awslc.outputs.COMMIT_MSG }} token: ${{ steps.generate-token.outputs.token }} - if: steps.check-sha-boring.outputs.COMMIT_SHA || steps.check-sha-openssl.outputs.COMMIT_SHA || steps.check-tag-aws-lc.outputs.NEW_TAG + if: steps.bump-boringssl.outputs.HAS_UPDATES == 'true' || steps.bump-openssl.outputs.HAS_UPDATES == 'true' || steps.bump-awslc.outputs.HAS_UPDATES == 'true' diff --git a/.github/workflows/x509-limbo-version-bump.yml b/.github/workflows/x509-limbo-version-bump.yml index 2e5583fb1f20..e15b4b132cdc 100644 --- a/.github/workflows/x509-limbo-version-bump.yml +++ b/.github/workflows/x509-limbo-version-bump.yml @@ -17,52 +17,34 @@ jobs: with: # Needed so we can push back to the repo persist-credentials: true - - id: check-sha-x509-limbo + - id: bump-x509-limbo run: | - SHA=$(git ls-remote https://github.com/C2SP/x509-limbo refs/heads/main | cut -f1) - LAST_COMMIT=$(grep x509-limbo-ref .github/actions/fetch-vectors/action.yml | grep -oE '[a-f0-9]{40}') - if ! grep -q "$SHA" .github/actions/fetch-vectors/action.yml; then - echo "COMMIT_SHA=${SHA}" >> $GITHUB_OUTPUT - echo "COMMIT_MSG<> $GITHUB_OUTPUT - echo -e "## x509-limbo\n[Commit: ${SHA}](https://github.com/C2SP/x509-limbo/commit/${SHA})\n\n[Diff](https://github.com/C2SP/x509-limbo/compare/${LAST_COMMIT}...${SHA}) between the last commit hash merged to this repository and the new commit." >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - fi - - name: Update x509-limbo + python3 .github/bin/bump_dependency.py \ + --name "x509-limbo" \ + --repo-url "https://github.com/C2SP/x509-limbo" \ + --branch "main" \ + --file-path ".github/actions/fetch-vectors/action.yml" \ + --current-version-pattern 'ref: "([a-f0-9]{40})" # x509-limbo-ref' \ + --update-pattern 'ref: "{new_version}" # x509-limbo-ref' \ + --comment-pattern 'Latest commit on the x509-limbo main branch.*?\.' + - id: bump-wycheproof run: | - set -xe - CURRENT_DATE=$(date "+%b %d, %Y") - sed -E -i "s/Latest commit on the x509-limbo main branch.*/Latest commit on the x509-limbo main branch, as of ${CURRENT_DATE}./" .github/actions/fetch-vectors/action.yml - sed -E -i "s/ref: \"[0-9a-f]{40}\" # x509-limbo-ref/ref: \"${COMMIT_SHA}\" # x509-limbo-ref/" .github/actions/fetch-vectors/action.yml - git status - if: steps.check-sha-x509-limbo.outputs.COMMIT_SHA - env: - COMMIT_SHA: ${{ steps.check-sha-x509-limbo.outputs.COMMIT_SHA }} - - id: check-sha-wycheproof - run: | - SHA=$(git ls-remote https://github.com/C2SP/wycheproof refs/heads/main | cut -f1) - LAST_COMMIT=$(grep wycheproof-ref .github/actions/fetch-vectors/action.yml | grep -oE '[a-f0-9]{40}') - if ! grep -q "$SHA" .github/actions/fetch-vectors/action.yml; then - echo "COMMIT_SHA=${SHA}" >> $GITHUB_OUTPUT - echo "COMMIT_MSG<> $GITHUB_OUTPUT - echo -e "## wycheproof\n[Commit: ${SHA}](https://github.com/C2SP/wycheproof/commit/${SHA})\n\n[Diff](https://github.com/C2SP/wycheproof/compare/${LAST_COMMIT}...${SHA}) between the last commit hash merged to this repository and the new commit." >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - fi - - name: Update wycheproof - run: | - set -xe - CURRENT_DATE=$(date "+%b %d, %Y") - sed -E -i "s/Latest commit on the wycheproof main branch.*/Latest commit on the wycheproof main branch, as of ${CURRENT_DATE}./" .github/actions/fetch-vectors/action.yml - sed -E -i "s/ref: \"[0-9a-f]{40}\" # wycheproof-ref/ref: \"${COMMIT_SHA}\" # wycheproof-ref/" .github/actions/fetch-vectors/action.yml - git status - if: steps.check-sha-wycheproof.outputs.COMMIT_SHA - env: - COMMIT_SHA: ${{ steps.check-sha-wycheproof.outputs.COMMIT_SHA }} + python3 .github/bin/bump_dependency.py \ + --name "wycheproof" \ + --repo-url "https://github.com/C2SP/wycheproof" \ + --branch "main" \ + --file-path ".github/actions/fetch-vectors/action.yml" \ + --current-version-pattern 'ref: "([a-f0-9]{40})" # wycheproof-ref' \ + --update-pattern 'ref: "{new_version}" # wycheproof-ref' \ + --comment-pattern 'Latest commit on the wycheproof main branch.*?\.' + - name: Check for updates + run: git status - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0 id: generate-token with: app_id: ${{ secrets.BORINGBOT_APP_ID }} private_key: ${{ secrets.BORINGBOT_PRIVATE_KEY }} - if: steps.check-sha-x509-limbo.outputs.COMMIT_SHA || steps.check-sha-wycheproof.outputs.COMMIT_SHA + if: steps.bump-x509-limbo.outputs.HAS_UPDATES == 'true' || steps.bump-wycheproof.outputs.HAS_UPDATES == 'true' - name: Create Pull Request uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: @@ -71,7 +53,7 @@ jobs: title: "Bump x509-limbo and/or wycheproof in CI" author: "pyca-boringbot[bot] " body: | - ${{ steps.check-sha-x509-limbo.outputs.COMMIT_MSG }} - ${{ steps.check-sha-wycheproof.outputs.COMMIT_MSG }} + ${{ steps.bump-x509-limbo.outputs.COMMIT_MSG }} + ${{ steps.bump-wycheproof.outputs.COMMIT_MSG }} token: ${{ steps.generate-token.outputs.token }} - if: steps.check-sha-x509-limbo.outputs.COMMIT_SHA || steps.check-sha-wycheproof.outputs.COMMIT_SHA + if: steps.bump-x509-limbo.outputs.HAS_UPDATES == 'true' || steps.bump-wycheproof.outputs.HAS_UPDATES == 'true'