From 5e3050696e78d84ee44fe1f83e964a2f6ddc15fb Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Mon, 22 Dec 2025 15:52:48 -0500 Subject: [PATCH 01/44] chore: add monorepo migration script --- monorepo-migration/migrate.sh | 119 ++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 monorepo-migration/migrate.sh diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh new file mode 100644 index 000000000000..856ca724417e --- /dev/null +++ b/monorepo-migration/migrate.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +# Exit on error +set -e + +# Configuration +MONOREPO_URL="https://github.com/googleapis/google-cloud-java" +SOURCE_REPO_URL="https://github.com/googleapis/java-logging" + +# Derive names from URLs to avoid duplication +SOURCE_REPO_NAME="${SOURCE_REPO_URL##*/}" +MONOREPO_NAME="${MONOREPO_URL##*/}" + +# Use a temporary working directory sibling to the current monorepo +WORKING_DIR="../../../migration-work" +SOURCE_DIR="$WORKING_DIR/$SOURCE_REPO_NAME-source" +TARGET_DIR="$WORKING_DIR/$MONOREPO_NAME-target" + +echo "Starting migration using git read-tree with isolated clones..." + +# 0. Create working directory +mkdir -p "$WORKING_DIR" + +# 1. Clone the source repository +if [ ! -d "$SOURCE_DIR" ]; then + echo "Cloning source repo: $SOURCE_REPO_URL into $SOURCE_DIR" + git clone "$SOURCE_REPO_URL" "$SOURCE_DIR" +else + echo "Source directory $SOURCE_DIR already exists. Ensuring it is clean and up-to-date..." + cd "$SOURCE_DIR" + git fetch origin + git checkout -f main + git reset --hard origin/main + git clean -fd + cd - > /dev/null +fi + +# 2. Clone the target monorepo (the "isolated clone") +if [ ! -d "$TARGET_DIR" ]; then + echo "Cloning target monorepo: $MONOREPO_URL into $TARGET_DIR" + git clone "$MONOREPO_URL" "$TARGET_DIR" +else + echo "Target directory $TARGET_DIR already exists. Ensuring it is clean and up-to-date..." + cd "$TARGET_DIR" + git fetch origin + git checkout -f main + git reset --hard origin/main + git clean -fd + cd - > /dev/null +fi + +cd "$TARGET_DIR" + +# Ensure we are on a clean main branch in the target clone +echo "Ensuring clean state in target monorepo..." +git fetch origin +git reset --hard HEAD +git clean -fd +git checkout -f main +git reset --hard origin/main + +# 2.5 Create a new feature branch for the migration +BRANCH_NAME="feat/migrate-$SOURCE_REPO_NAME" +echo "Creating feature branch: $BRANCH_NAME" +if git branch | grep -q "$BRANCH_NAME"; then + git branch -D "$BRANCH_NAME" +fi +git checkout -b "$BRANCH_NAME" + +# 3. Add the source repo as a remote +echo "Adding remote for $SOURCE_REPO_NAME: $SOURCE_DIR" +if git remote | grep -q "^$SOURCE_REPO_NAME$"; then + git remote remove "$SOURCE_REPO_NAME" +fi +git remote add "$SOURCE_REPO_NAME" "../$SOURCE_REPO_NAME-source" + +# 4. Fetch the source repo +echo "Fetching $SOURCE_REPO_NAME..." +git fetch "$SOURCE_REPO_NAME" + +# 5. Merge the histories using 'ours' strategy to keep monorepo content +echo "Merging histories (strategy: ours)..." +git merge --allow-unrelated-histories --no-ff "$SOURCE_REPO_NAME/main" -s ours --no-commit -m "feat($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" + +# 6. Read the tree from the source repo into the desired subdirectory +echo "Reading tree into prefix $SOURCE_REPO_NAME/..." +if [ -d "$SOURCE_REPO_NAME" ]; then + rm -rf "$SOURCE_REPO_NAME" +fi +git read-tree --prefix="$SOURCE_REPO_NAME/" -u "$SOURCE_REPO_NAME/main" + +# 7. Commit the changes +echo "Committing migration..." +git commit -n --no-gpg-sign -m "feat($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" + +# 8. Update the root pom.xml to include the new module +echo "Updating root pom.xml..." +if [ -f "pom.xml" ]; then + # Check if module already exists + if grep -q "$SOURCE_REPO_NAME" pom.xml; then + echo "Module $SOURCE_REPO_NAME already exists in pom.xml" + else + # Insert the module before the closing tag + sed -i '' "/<\/modules>/i\\ +\\ $SOURCE_REPO_NAME +" pom.xml + echo "Added $SOURCE_REPO_NAME to pom.xml" + fi +else + echo "Warning: root pom.xml not found" +fi + +# 9. Cleanup +echo "Cleaning up temporary source clone..." +rm -rf "$SOURCE_DIR" + +echo "Migration complete!" +echo "The migrated codebase is available in: $TARGET_DIR" +echo "You are on the $BRANCH_NAME branch in that clone." From 2d2b80c56f9df57d5f8199dcc33ebb246b1e0ebb Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Mon, 22 Dec 2025 15:59:08 -0500 Subject: [PATCH 02/44] chore: fix path --- monorepo-migration/migrate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 monorepo-migration/migrate.sh diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh old mode 100644 new mode 100755 index 856ca724417e..4eba1c3908c1 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -12,7 +12,7 @@ SOURCE_REPO_NAME="${SOURCE_REPO_URL##*/}" MONOREPO_NAME="${MONOREPO_URL##*/}" # Use a temporary working directory sibling to the current monorepo -WORKING_DIR="../../../migration-work" +WORKING_DIR="../../migration-work" SOURCE_DIR="$WORKING_DIR/$SOURCE_REPO_NAME-source" TARGET_DIR="$WORKING_DIR/$MONOREPO_NAME-target" From c9aa394a6737f05246f246aaf7322754da65acda Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Mon, 22 Dec 2025 16:11:25 -0500 Subject: [PATCH 03/44] chore: refine migration script for atomic commits and isolation --- monorepo-migration/migrate.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 4eba1c3908c1..21c1f134d899 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -60,9 +60,9 @@ git checkout -f main git reset --hard origin/main # 2.5 Create a new feature branch for the migration -BRANCH_NAME="feat/migrate-$SOURCE_REPO_NAME" +BRANCH_NAME="migrate-$SOURCE_REPO_NAME" echo "Creating feature branch: $BRANCH_NAME" -if git branch | grep -q "$BRANCH_NAME"; then +if git rev-parse --verify "$BRANCH_NAME" >/dev/null 2>&1; then git branch -D "$BRANCH_NAME" fi git checkout -b "$BRANCH_NAME" @@ -80,7 +80,7 @@ git fetch "$SOURCE_REPO_NAME" # 5. Merge the histories using 'ours' strategy to keep monorepo content echo "Merging histories (strategy: ours)..." -git merge --allow-unrelated-histories --no-ff "$SOURCE_REPO_NAME/main" -s ours --no-commit -m "feat($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" +git merge --allow-unrelated-histories --no-ff "$SOURCE_REPO_NAME/main" -s ours --no-commit -m "chore($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" # 6. Read the tree from the source repo into the desired subdirectory echo "Reading tree into prefix $SOURCE_REPO_NAME/..." @@ -89,9 +89,9 @@ if [ -d "$SOURCE_REPO_NAME" ]; then fi git read-tree --prefix="$SOURCE_REPO_NAME/" -u "$SOURCE_REPO_NAME/main" -# 7. Commit the changes +# 7. Commit the migration echo "Committing migration..." -git commit -n --no-gpg-sign -m "feat($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" +git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" # 8. Update the root pom.xml to include the new module echo "Updating root pom.xml..." @@ -105,6 +105,8 @@ if [ -f "pom.xml" ]; then \\ $SOURCE_REPO_NAME " pom.xml echo "Added $SOURCE_REPO_NAME to pom.xml" + git add pom.xml + git commit -n --no-gpg-sign -m "chore: add $SOURCE_REPO_NAME module to root pom.xml" fi else echo "Warning: root pom.xml not found" From 98e420ba90a4357e659a46e2609c969e5f2dcc2f Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 11:10:52 -0500 Subject: [PATCH 04/44] chore: remove pom.xml update logic from migration script --- monorepo-migration/migrate.sh | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 21c1f134d899..c9f279d52871 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -93,26 +93,7 @@ git read-tree --prefix="$SOURCE_REPO_NAME/" -u "$SOURCE_REPO_NAME/main" echo "Committing migration..." git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" -# 8. Update the root pom.xml to include the new module -echo "Updating root pom.xml..." -if [ -f "pom.xml" ]; then - # Check if module already exists - if grep -q "$SOURCE_REPO_NAME" pom.xml; then - echo "Module $SOURCE_REPO_NAME already exists in pom.xml" - else - # Insert the module before the closing tag - sed -i '' "/<\/modules>/i\\ -\\ $SOURCE_REPO_NAME -" pom.xml - echo "Added $SOURCE_REPO_NAME to pom.xml" - git add pom.xml - git commit -n --no-gpg-sign -m "chore: add $SOURCE_REPO_NAME module to root pom.xml" - fi -else - echo "Warning: root pom.xml not found" -fi - -# 9. Cleanup +# 8. Cleanup echo "Cleaning up temporary source clone..." rm -rf "$SOURCE_DIR" From 5c333b8c73f6d340c6d69e21c7567ae3cd948707 Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 11:45:16 -0500 Subject: [PATCH 05/44] chore: implement GitHub Actions workflow migration using paths-filter --- monorepo-migration/migrate.sh | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index c9f279d52871..49e32164e312 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -93,6 +93,87 @@ git read-tree --prefix="$SOURCE_REPO_NAME/" -u "$SOURCE_REPO_NAME/main" echo "Committing migration..." git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" +# 7.5 Migrate GitHub Actions workflows +echo "Checking for GitHub Actions workflows..." +if [ -d "$SOURCE_REPO_NAME/.github/workflows" ]; then + echo "Migrating workflows to root .github/workflows/..." + mkdir -p .github/workflows + + # Create a temporary python script for robust YAML transformation + cat << 'EOF' > transform_workflow.py +import sys +import re + +def transform(content, lib_name): + lines = content.splitlines() + new_lines = [] + inserted_defaults = False + + filter_job = f""" filter: + runs-on: ubuntu-latest + outputs: + library: ${{{{ steps.filter.outputs.library }}}} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + library: + - '{lib_name}/**'""" + + in_jobs = False + for line in lines: + if line.startswith('jobs:'): + if not inserted_defaults: + new_lines.append("defaults:") + new_lines.append(" run:") + new_lines.append(f" working-directory: {lib_name}") + inserted_defaults = True + new_lines.append(line) + new_lines.append(filter_job) + in_jobs = True + continue + + if in_jobs and line.startswith(' ') and not line.startswith(' ') and line.strip() and not line.strip().startswith('#'): + job_match = re.match(r'^ ([\w-]+):', line) + if job_match: + job_name = job_match.group(1) + if job_name != 'filter': + new_lines.append(line) + new_lines.append(" needs: filter") + new_lines.append(f" if: ${{{{ needs.filter.outputs.library == 'true' }}}}") + continue + + new_lines.append(line) + return "\n".join(new_lines) + +if __name__ == "__main__": + lib = sys.argv[1] + print(transform(sys.stdin.read(), lib)) +EOF + + for workflow in "$SOURCE_REPO_NAME/.github/workflows/"*; do + if [ -f "$workflow" ]; then + filename=$(basename "$workflow") + new_filename="${SOURCE_REPO_NAME}-${filename}" + target_path=".github/workflows/$new_filename" + + echo "Migrating and adapting $filename to $target_path" + python3 transform_workflow.py "$SOURCE_REPO_NAME" < "$workflow" > "$target_path" + fi + done + + rm transform_workflow.py + + # Cleanup empty .github directory if it exists + rm -rf "$SOURCE_REPO_NAME/.github" + + echo "Committing workflow migration..." + git add .github/workflows + git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate and adapt GitHub Actions workflows" +fi + # 8. Cleanup echo "Cleaning up temporary source clone..." rm -rf "$SOURCE_DIR" From a63523ea455ffdd2a31870f2e88299445e300d1e Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 12:21:22 -0500 Subject: [PATCH 06/44] chore: skip redundant workflows and update generation_config.yaml --- monorepo-migration/migrate.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 49e32164e312..d3c1273a09f9 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -156,6 +156,13 @@ EOF for workflow in "$SOURCE_REPO_NAME/.github/workflows/"*; do if [ -f "$workflow" ]; then filename=$(basename "$workflow") + + # Skip redundant workflows as requested by user + if [ "$filename" == "hermetic_library_generation.yaml" ] || [ "$filename" == "update_generation_config.yaml" ]; then + echo "Skipping redundant workflow: $filename" + continue + fi + new_filename="${SOURCE_REPO_NAME}-${filename}" target_path=".github/workflows/$new_filename" @@ -174,6 +181,27 @@ EOF git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate and adapt GitHub Actions workflows" fi +# 7.6 Update generation_config.yaml +echo "Updating generation_config.yaml..." +SOURCE_CONFIG="$SOURCE_REPO_NAME/generation_config.yaml" +if [ -f "$SOURCE_CONFIG" ]; then + # Extract the library entry (starts with - api_shortname) + # This assumes the source config only has one library or we want the first one + ENTRY=$(awk '/^ - api_shortname:/{flag=1; print $0; next} /^ - / && flag{flag=0} flag' "$SOURCE_CONFIG") + + # Simple cleanup: remove repo and repo_short if they exist + # Adjust indentation to match monorepo (0 spaces for -) + CLEAN_ENTRY=$(echo "$ENTRY" | sed '/repo:/d' | sed '/repo_short:/d' | sed 's/^ //') + + # Append to target generation_config.yaml + echo "" >> generation_config.yaml + echo "$CLEAN_ENTRY" >> generation_config.yaml + + echo "Committing generation_config.yaml update..." + git add generation_config.yaml + git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): add library to generation_config.yaml" +fi + # 8. Cleanup echo "Cleaning up temporary source clone..." rm -rf "$SOURCE_DIR" From 6ce68c96f2cb63bd14bd5f6b4fb8196f8610639e Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 13:34:18 -0500 Subject: [PATCH 07/44] chore: expand workflow skip list to include samples, release, and more --- monorepo-migration/migrate.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index d3c1273a09f9..f575e43a633a 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -156,12 +156,16 @@ EOF for workflow in "$SOURCE_REPO_NAME/.github/workflows/"*; do if [ -f "$workflow" ]; then filename=$(basename "$workflow") - + # Skip redundant workflows as requested by user - if [ "$filename" == "hermetic_library_generation.yaml" ] || [ "$filename" == "update_generation_config.yaml" ]; then - echo "Skipping redundant workflow: $filename" - continue - fi + case "$filename" in + "hermetic_library_generation.yaml" | "update_generation_config.yaml" | \ + "approve-readme.yaml" | "auto-release.yaml" | "renovate_config_check.yaml" | \ + "samples.yaml" | "unmanaged_dependency_check.yaml") + echo "Skipping redundant workflow: $filename" + continue + ;; + esac new_filename="${SOURCE_REPO_NAME}-${filename}" target_path=".github/workflows/$new_filename" From 3f2f4ba444e4cc3e7d52b3b41e9f41d597c12d07 Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 13:51:21 -0500 Subject: [PATCH 08/44] chore: make workflow transformation logic a permanent script and rename workflows --- monorepo-migration/migrate.sh | 62 ++---------------------- monorepo-migration/transform_workflow.py | 62 ++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 57 deletions(-) create mode 100644 monorepo-migration/transform_workflow.py diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index f575e43a633a..e7781460dd20 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -16,6 +16,10 @@ WORKING_DIR="../../migration-work" SOURCE_DIR="$WORKING_DIR/$SOURCE_REPO_NAME-source" TARGET_DIR="$WORKING_DIR/$MONOREPO_NAME-target" +# Get absolute path to the transformation script before any cd +TRANSFORM_SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +TRANSFORM_SCRIPT="$TRANSFORM_SCRIPT_DIR/transform_workflow.py" + echo "Starting migration using git read-tree with isolated clones..." # 0. Create working directory @@ -99,60 +103,6 @@ if [ -d "$SOURCE_REPO_NAME/.github/workflows" ]; then echo "Migrating workflows to root .github/workflows/..." mkdir -p .github/workflows - # Create a temporary python script for robust YAML transformation - cat << 'EOF' > transform_workflow.py -import sys -import re - -def transform(content, lib_name): - lines = content.splitlines() - new_lines = [] - inserted_defaults = False - - filter_job = f""" filter: - runs-on: ubuntu-latest - outputs: - library: ${{{{ steps.filter.outputs.library }}}} - steps: - - uses: actions/checkout@v4 - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | - library: - - '{lib_name}/**'""" - - in_jobs = False - for line in lines: - if line.startswith('jobs:'): - if not inserted_defaults: - new_lines.append("defaults:") - new_lines.append(" run:") - new_lines.append(f" working-directory: {lib_name}") - inserted_defaults = True - new_lines.append(line) - new_lines.append(filter_job) - in_jobs = True - continue - - if in_jobs and line.startswith(' ') and not line.startswith(' ') and line.strip() and not line.strip().startswith('#'): - job_match = re.match(r'^ ([\w-]+):', line) - if job_match: - job_name = job_match.group(1) - if job_name != 'filter': - new_lines.append(line) - new_lines.append(" needs: filter") - new_lines.append(f" if: ${{{{ needs.filter.outputs.library == 'true' }}}}") - continue - - new_lines.append(line) - return "\n".join(new_lines) - -if __name__ == "__main__": - lib = sys.argv[1] - print(transform(sys.stdin.read(), lib)) -EOF - for workflow in "$SOURCE_REPO_NAME/.github/workflows/"*; do if [ -f "$workflow" ]; then filename=$(basename "$workflow") @@ -171,12 +121,10 @@ EOF target_path=".github/workflows/$new_filename" echo "Migrating and adapting $filename to $target_path" - python3 transform_workflow.py "$SOURCE_REPO_NAME" < "$workflow" > "$target_path" + python3 "$TRANSFORM_SCRIPT" "$SOURCE_REPO_NAME" < "$workflow" > "$target_path" fi done - rm transform_workflow.py - # Cleanup empty .github directory if it exists rm -rf "$SOURCE_REPO_NAME/.github" diff --git a/monorepo-migration/transform_workflow.py b/monorepo-migration/transform_workflow.py new file mode 100644 index 000000000000..b40dd9f4ebd7 --- /dev/null +++ b/monorepo-migration/transform_workflow.py @@ -0,0 +1,62 @@ +import sys +import re + +def transform(content, lib_name): + lines = content.splitlines() + new_lines = [] + inserted_defaults = False + + filter_job = f""" filter: + runs-on: ubuntu-latest + outputs: + library: ${{{{ steps.filter.outputs.library }}}} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + library: + - '{lib_name}/**'""" + + in_jobs = False + for line in lines: + if line.startswith('name:') and not in_jobs: + name_match = re.match(r'^name:\s*(.*)', line) + if name_match: + orig_name = name_match.group(1).strip() + # Remove quotes if they exist + orig_name = orig_name.strip("\"'") + new_lines.append(f"name: {lib_name} {orig_name}") + continue + + if line.startswith('jobs:'): + if not inserted_defaults: + new_lines.append("defaults:") + new_lines.append(" run:") + new_lines.append(f" working-directory: {lib_name}") + inserted_defaults = True + new_lines.append(line) + new_lines.append(filter_job) + in_jobs = True + continue + + if in_jobs and line.startswith(' ') and not line.startswith(' ') and line.strip() and not line.strip().startswith('#'): + job_match = re.match(r'^ ([\w-]+):', line) + if job_match: + job_name = job_match.group(1) + if job_name != 'filter': + new_lines.append(line) + new_lines.append(" needs: filter") + new_lines.append(f" if: ${{{{ needs.filter.outputs.library == 'true' }}}}") + continue + + new_lines.append(line) + return "\n".join(new_lines) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python3 transform_workflow.py ") + sys.exit(1) + lib = sys.argv[1] + print(transform(sys.stdin.read(), lib)) From 46cc566fd9a9fb2b7664b412f662879155e04b8a Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 14:07:54 -0500 Subject: [PATCH 09/44] chore: refine versions.txt consolidation to only append data lines --- monorepo-migration/migrate.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index e7781460dd20..0168563adea8 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -154,6 +154,21 @@ if [ -f "$SOURCE_CONFIG" ]; then git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): add library to generation_config.yaml" fi +# 7.7 Consolidate versions.txt +echo "Consolidating versions.txt..." +SOURCE_VERSIONS="$SOURCE_REPO_NAME/versions.txt" +if [ -f "$SOURCE_VERSIONS" ]; then + # Append data lines only to root versions.txt (exclude comments/headers) + grep "^[a-zA-Z0-9]" "$SOURCE_VERSIONS" >> versions.txt + + # Remove the migrated subdirectory's versions.txt + rm "$SOURCE_VERSIONS" + + echo "Committing versions.txt update..." + git add versions.txt "$SOURCE_VERSIONS" + git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): consolidate versions.txt into root" +fi + # 8. Cleanup echo "Cleaning up temporary source clone..." rm -rf "$SOURCE_DIR" From cf9914a36fa51011261f282331a725279e62d6ff Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 14:27:55 -0500 Subject: [PATCH 10/44] chore: remove clirr job from workflow transformation --- monorepo-migration/transform_workflow.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/monorepo-migration/transform_workflow.py b/monorepo-migration/transform_workflow.py index b40dd9f4ebd7..0e6b1c6927cb 100644 --- a/monorepo-migration/transform_workflow.py +++ b/monorepo-migration/transform_workflow.py @@ -20,6 +20,7 @@ def transform(content, lib_name): - '{lib_name}/**'""" in_jobs = False + skip_current_job = False for line in lines: if line.startswith('name:') and not in_jobs: name_match = re.match(r'^name:\s*(.*)', line) @@ -45,13 +46,20 @@ def transform(content, lib_name): job_match = re.match(r'^ ([\w-]+):', line) if job_match: job_name = job_match.group(1) + if job_name == 'clirr': + skip_current_job = True + continue + else: + skip_current_job = False + if job_name != 'filter': new_lines.append(line) new_lines.append(" needs: filter") new_lines.append(f" if: ${{{{ needs.filter.outputs.library == 'true' }}}}") continue - new_lines.append(line) + if not skip_current_job: + new_lines.append(line) return "\n".join(new_lines) if __name__ == "__main__": From 6ddf4df0135d4593e4e2071066ba7ca148876ab5 Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 14:35:41 -0500 Subject: [PATCH 11/44] chore: add copyright header fix to migration script --- monorepo-migration/migrate.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 0168563adea8..f80382ac5bc2 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -169,6 +169,14 @@ if [ -f "$SOURCE_VERSIONS" ]; then git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): consolidate versions.txt into root" fi +# 7.8 Fix copyright headers in Java files +echo "Fixing copyright headers in Java files..." +find "$SOURCE_REPO_NAME" -name "*.java" -exec python3 -c "import sys, re; p = sys.argv[1]; c = open(p).read(); new_c = re.sub(r'Copyright \d{4} Google (Inc\.|LLC)', 'Copyright 2025 Google LLC', c); open(p, 'w').write(new_c)" {} \; + +echo "Committing copyright header fixes..." +git add "$SOURCE_REPO_NAME" +git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): update copyright headers to 2025 Google LLC" + # 8. Cleanup echo "Cleaning up temporary source clone..." rm -rf "$SOURCE_DIR" From 8d103f2623658cce969a7b3cf475d58bd408600e Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Tue, 23 Dec 2025 23:19:47 -0500 Subject: [PATCH 12/44] impl: automate reporting removal, build verification, and dynamic parent versioning --- monorepo-migration/migrate.sh | 14 ++++ monorepo-migration/modernize_pom.py | 102 ++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 monorepo-migration/modernize_pom.py diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index f80382ac5bc2..18f5e8d03dc8 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -19,6 +19,7 @@ TARGET_DIR="$WORKING_DIR/$MONOREPO_NAME-target" # Get absolute path to the transformation script before any cd TRANSFORM_SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" TRANSFORM_SCRIPT="$TRANSFORM_SCRIPT_DIR/transform_workflow.py" +MODERNIZE_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/modernize_pom.py" echo "Starting migration using git read-tree with isolated clones..." @@ -177,6 +178,19 @@ echo "Committing copyright header fixes..." git add "$SOURCE_REPO_NAME" git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): update copyright headers to 2025 Google LLC" +# 7.9 Modernize root pom.xml +echo "Modernizing root pom.xml..." +PARENT_VERSION=$(grep -m 1 ".*{x-version-update:google-cloud-java:current}" google-cloud-jar-parent/pom.xml | sed -E 's/.*(.*)<\/version>.*/\1/') +python3 "$MODERNIZE_POM_SCRIPT" "$SOURCE_REPO_NAME/pom.xml" "$PARENT_VERSION" + +echo "Committing root pom.xml modernization..." +git add "$SOURCE_REPO_NAME/pom.xml" +git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): modernize root pom.xml" + +# 7.10 Verify compilation +echo "Verifying compilation..." +(cd "$SOURCE_REPO_NAME" && mvn compile -DskipTests -T 1C) + # 8. Cleanup echo "Cleaning up temporary source clone..." rm -rf "$SOURCE_DIR" diff --git a/monorepo-migration/modernize_pom.py b/monorepo-migration/modernize_pom.py new file mode 100644 index 000000000000..983f2828261c --- /dev/null +++ b/monorepo-migration/modernize_pom.py @@ -0,0 +1,102 @@ +import sys +import re + +def modernize_pom(file_path, parent_version): + with open(file_path, 'r') as f: + lines = f.readlines() + + new_lines = [] + in_parent = False + in_dep_mgmt = False + in_dependencies = False + in_dependency = False + in_reporting = False + current_dependency_lines = [] + has_x_version_update = False + + for line in lines: + # Parent section modernization + if '' in line and not in_parent: + in_parent = True + indent = line[:line.find('<')] + new_lines.append(f"{indent}\n") + new_lines.append(f"{indent} com.google.cloud\n") + new_lines.append(f"{indent} google-cloud-jar-parent\n") + new_lines.append(f"{indent} {parent_version}\n") + new_lines.append(f"{indent} ../google-cloud-jar-parent/pom.xml\n") + continue + if '' in line and in_parent: + in_parent = False + new_lines.append(line) + continue + if in_parent: + continue # skip original parent content + + # Dependency Management pruning + if '' in line: + in_dep_mgmt = True + new_lines.append(line) + continue + if '' in line: + in_dep_mgmt = False + new_lines.append(line) + continue + + if in_dep_mgmt: + if '' in line: + in_dependencies = True + new_lines.append(line) + continue + if '' in line: + in_dependencies = False + new_lines.append(line) + continue + + if in_dependencies: + if '' in line: + in_dependency = True + current_dependency_lines = [line] + has_x_version_update = False + continue + if '' in line: + in_dependency = False + current_dependency_lines.append(line) + if has_x_version_update: + new_lines.extend(current_dependency_lines) + continue + + if in_dependency: + current_dependency_lines.append(line) + if '{x-version-update:' in line: + has_x_version_update = True + continue + + # Prune comments and extra whitespace in depMgmt for a cleaner result + if not line.strip(): + new_lines.append(line) + continue + + # Reporting section removal + if '' in line: + in_reporting = True + continue + if '' in line: + in_reporting = False + continue + if in_reporting: + continue + + new_lines.append(line) + + with open(file_path, 'w') as f: + # Clean up double empty lines potentially introduced by pruning + content = "".join(new_lines) + content = re.sub(r'\n\s*\n\s*\n', '\n\n', content) + f.write(content) + +if __name__ == "__main__": + if len(sys.argv) > 2: + modernize_pom(sys.argv[1], sys.argv[2]) + else: + print("Usage: python3 modernize_pom.py ") + sys.exit(1) From 30833e38d25f5f7403580238adf223c7781963b8 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Mon, 5 Jan 2026 21:14:13 +0000 Subject: [PATCH 13/44] feat: add CLI binary checks to migrate.sh --- monorepo-migration/migrate.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 18f5e8d03dc8..b60f32e76dc0 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -3,6 +3,19 @@ # Exit on error set -e +# Function to check if a command exists +check_command() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Error: $1 is not installed or not in PATH." >&2 + exit 1 + fi +} + +# Check for necessary CLI binaries +check_command git +check_command python3 +check_command mvn + # Configuration MONOREPO_URL="https://github.com/googleapis/google-cloud-java" SOURCE_REPO_URL="https://github.com/googleapis/java-logging" From 004af07db6678c5760bc4b356be18e165fdab096 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Tue, 6 Jan 2026 17:12:41 +0000 Subject: [PATCH 14/44] chore: allow setting SOURCE_REPO_URL via environment variable in migrate.sh --- monorepo-migration/migrate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index b60f32e76dc0..0a234982330f 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -18,7 +18,7 @@ check_command mvn # Configuration MONOREPO_URL="https://github.com/googleapis/google-cloud-java" -SOURCE_REPO_URL="https://github.com/googleapis/java-logging" +SOURCE_REPO_URL="${SOURCE_REPO_URL:-https://github.com/googleapis/java-logging}" # Derive names from URLs to avoid duplication SOURCE_REPO_NAME="${SOURCE_REPO_URL##*/}" From 0d824c4498267f70d2ffd5447a7c0335082dad70 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Tue, 6 Jan 2026 17:28:51 +0000 Subject: [PATCH 15/44] feat: add guard for checking if the repository is already migrated --- monorepo-migration/migrate.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 0a234982330f..a1639851a0de 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -77,6 +77,14 @@ git clean -fd git checkout -f main git reset --hard origin/main +# Check if the repository is already migrated +if [ -d "$SOURCE_REPO_NAME" ]; then + echo "Error: Directory $SOURCE_REPO_NAME already exists in the monorepo." >&2 + echo "This repository seems to have already been migrated." >&2 + exit 1 +fi + + # 2.5 Create a new feature branch for the migration BRANCH_NAME="migrate-$SOURCE_REPO_NAME" echo "Creating feature branch: $BRANCH_NAME" @@ -102,9 +110,6 @@ git merge --allow-unrelated-histories --no-ff "$SOURCE_REPO_NAME/main" -s ours - # 6. Read the tree from the source repo into the desired subdirectory echo "Reading tree into prefix $SOURCE_REPO_NAME/..." -if [ -d "$SOURCE_REPO_NAME" ]; then - rm -rf "$SOURCE_REPO_NAME" -fi git read-tree --prefix="$SOURCE_REPO_NAME/" -u "$SOURCE_REPO_NAME/main" # 7. Commit the migration From e7df2ba31614bd64048742c36bd5ef3852e90898 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Tue, 6 Jan 2026 18:47:43 +0000 Subject: [PATCH 16/44] feat: add environment variable for specifying codeowner team to migrate --- monorepo-migration/migrate.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index a1639851a0de..a387a9e840ad 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -19,6 +19,7 @@ check_command mvn # Configuration MONOREPO_URL="https://github.com/googleapis/google-cloud-java" SOURCE_REPO_URL="${SOURCE_REPO_URL:-https://github.com/googleapis/java-logging}" +CODEOWNER="${CODEOWNER:-}" # Derive names from URLs to avoid duplication SOURCE_REPO_NAME="${SOURCE_REPO_URL##*/}" @@ -116,6 +117,17 @@ git read-tree --prefix="$SOURCE_REPO_NAME/" -u "$SOURCE_REPO_NAME/main" echo "Committing migration..." git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" +# 7.1 Update CODEOWNERS +if [ -n "$CODEOWNER" ]; then + echo "Updating .github/CODEOWNERS..." + mkdir -p .github + echo "/$SOURCE_REPO_NAME/ $CODEOWNER @googleapis/cloud-java-team-teamsync" >> .github/CODEOWNERS + + echo "Committing CODEOWNERS update..." + git add .github/CODEOWNERS + git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): add code owners for $SOURCE_REPO_NAME" +fi + # 7.5 Migrate GitHub Actions workflows echo "Checking for GitHub Actions workflows..." if [ -d "$SOURCE_REPO_NAME/.github/workflows" ]; then From 2bd08212923370fcddd2f520718e3b172cf8f313 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 7 Jan 2026 17:28:58 +0000 Subject: [PATCH 17/44] feat: insert new module in root pom.xml --- monorepo-migration/migrate.sh | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index a387a9e840ad..4035465674b0 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -77,6 +77,7 @@ git reset --hard HEAD git clean -fd git checkout -f main git reset --hard origin/main +git clean -fdx # Check if the repository is already migrated if [ -d "$SOURCE_REPO_NAME" ]; then @@ -128,6 +129,46 @@ if [ -n "$CODEOWNER" ]; then git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): add code owners for $SOURCE_REPO_NAME" fi +# 7.2 Update root pom.xml modules +echo "Updating root pom.xml modules..." +python3 -c " +import sys +pom_path = sys.argv[1] +module_name = sys.argv[2] +new_module = f' {module_name}\n' +with open(pom_path, 'r') as f: + content = f.read() +start_tag = '' +end_tag = '' +start_idx = content.find(start_tag) +end_idx = content.find(end_tag) +if start_idx != -1 and end_idx != -1: + modules_section = content[start_idx + len(start_tag):end_idx] + lines = [l for l in modules_section.splitlines(keepends=True) if l.strip()] + + java_indices = [i for i, l in enumerate(lines) if 'java-' in l] + if java_indices: + start_java = java_indices[0] + end_java = java_indices[-1] + 1 + java_lines = lines[start_java:end_java] + if not any(f'{module_name}' in l for l in java_lines): + java_lines.append(new_module) + java_lines.sort() + lines = lines[:start_java] + java_lines + lines[end_java:] + else: + if not any(f'{module_name}' in l for l in lines): + lines.append(new_module) + + new_content = content[:start_idx + len(start_tag)] + '\n' + ''.join(lines) + ' ' + content[end_idx:] + with open(pom_path, 'w') as f: + f.write(new_content) +" "pom.xml" "$SOURCE_REPO_NAME" + +echo "Committing root pom.xml modules update..." +git add pom.xml +git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): add module to root pom.xml" + + # 7.5 Migrate GitHub Actions workflows echo "Checking for GitHub Actions workflows..." if [ -d "$SOURCE_REPO_NAME/.github/workflows" ]; then From f7232a86ff504b83913092f5cbf296b3fad3bc5f Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 7 Jan 2026 19:11:12 +0000 Subject: [PATCH 18/44] chore: exclude common files from source root in migration script --- monorepo-migration/migrate.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 4035465674b0..2d619ced0153 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -114,6 +114,15 @@ git merge --allow-unrelated-histories --no-ff "$SOURCE_REPO_NAME/main" -s ours - echo "Reading tree into prefix $SOURCE_REPO_NAME/..." git read-tree --prefix="$SOURCE_REPO_NAME/" -u "$SOURCE_REPO_NAME/main" +# 6.5 Remove common files from the root of the migrated library +echo "Removing common files from the root of $SOURCE_REPO_NAME/..." +rm -f "$SOURCE_REPO_NAME/.gitignore" +rm -f "$SOURCE_REPO_NAME/renovate.json" +rm -f "$SOURCE_REPO_NAME/LICENSE" +rm -f "$SOURCE_REPO_NAME/java.header" +rm -f "$SOURCE_REPO_NAME/license-checks.xml" +find "$SOURCE_REPO_NAME" -maxdepth 1 -name "*.md" ! -name "CHANGELOG.md" ! -name "README.md" -delete + # 7. Commit the migration echo "Committing migration..." git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate $SOURCE_REPO_NAME into monorepo" From bf352f21418f5e9e03ad21f2e5014c5ba8a101b9 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 18:02:18 +0000 Subject: [PATCH 19/44] refactor: extract inline python to a .py file --- monorepo-migration/migrate.sh | 34 ++------------------- monorepo-migration/update_root_pom.py | 43 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 monorepo-migration/update_root_pom.py diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 2d619ced0153..ddd694bcdc30 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -34,6 +34,7 @@ TARGET_DIR="$WORKING_DIR/$MONOREPO_NAME-target" TRANSFORM_SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" TRANSFORM_SCRIPT="$TRANSFORM_SCRIPT_DIR/transform_workflow.py" MODERNIZE_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/modernize_pom.py" +UPDATE_ROOT_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_root_pom.py" echo "Starting migration using git read-tree with isolated clones..." @@ -140,38 +141,7 @@ fi # 7.2 Update root pom.xml modules echo "Updating root pom.xml modules..." -python3 -c " -import sys -pom_path = sys.argv[1] -module_name = sys.argv[2] -new_module = f' {module_name}\n' -with open(pom_path, 'r') as f: - content = f.read() -start_tag = '' -end_tag = '' -start_idx = content.find(start_tag) -end_idx = content.find(end_tag) -if start_idx != -1 and end_idx != -1: - modules_section = content[start_idx + len(start_tag):end_idx] - lines = [l for l in modules_section.splitlines(keepends=True) if l.strip()] - - java_indices = [i for i, l in enumerate(lines) if 'java-' in l] - if java_indices: - start_java = java_indices[0] - end_java = java_indices[-1] + 1 - java_lines = lines[start_java:end_java] - if not any(f'{module_name}' in l for l in java_lines): - java_lines.append(new_module) - java_lines.sort() - lines = lines[:start_java] + java_lines + lines[end_java:] - else: - if not any(f'{module_name}' in l for l in lines): - lines.append(new_module) - - new_content = content[:start_idx + len(start_tag)] + '\n' + ''.join(lines) + ' ' + content[end_idx:] - with open(pom_path, 'w') as f: - f.write(new_content) -" "pom.xml" "$SOURCE_REPO_NAME" +python3 "$UPDATE_ROOT_POM_SCRIPT" "pom.xml" "$SOURCE_REPO_NAME" echo "Committing root pom.xml modules update..." git add pom.xml diff --git a/monorepo-migration/update_root_pom.py b/monorepo-migration/update_root_pom.py new file mode 100644 index 000000000000..82822dd7734d --- /dev/null +++ b/monorepo-migration/update_root_pom.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import sys + +def update_root_pom(pom_path, module_name): + new_module = f' {module_name}\n' + with open(pom_path, 'r') as f: + content = f.read() + + start_tag = '' + end_tag = '' + start_idx = content.find(start_tag) + end_idx = content.find(end_tag) + + if start_idx == -1 or end_idx == -1: + print(f"Error: {start_tag} or {end_tag} not found in {pom_path}") + sys.exit(1) + + modules_section = content[start_idx + len(start_tag):end_idx] + lines = [l for l in modules_section.splitlines(keepends=True) if l.strip()] + + java_indices = [i for i, l in enumerate(lines) if 'java-' in l] + if java_indices: + start_java = java_indices[0] + end_java = java_indices[-1] + 1 + java_lines = lines[start_java:end_java] + if not any(f'{module_name}' in l for l in java_lines): + java_lines.append(new_module) + java_lines.sort() + lines = lines[:start_java] + java_lines + lines[end_java:] + else: + if not any(f'{module_name}' in l for l in lines): + lines.append(new_module) + + new_content = content[:start_idx + len(start_tag)] + '\n' + ''.join(lines) + ' ' + content[end_idx:] + with open(pom_path, 'w') as f: + f.write(new_content) + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: update_root_pom.py ") + sys.exit(1) + update_root_pom(sys.argv[1], sys.argv[2]) From 83eaa22bb0980bdf7b95b67c17ca3210dc153246 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 18:07:55 +0000 Subject: [PATCH 20/44] chore: add license headers --- monorepo-migration/migrate.sh | 14 ++++++++++++++ monorepo-migration/modernize_pom.py | 14 ++++++++++++++ monorepo-migration/transform_workflow.py | 14 ++++++++++++++ monorepo-migration/update_root_pom.py | 13 +++++++++++++ 4 files changed, 55 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index ddd694bcdc30..c51434a562af 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -1,4 +1,18 @@ #!/bin/bash +# Copyright 2026 Google LLC +# +# Licensed 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. + # Exit on error set -e diff --git a/monorepo-migration/modernize_pom.py b/monorepo-migration/modernize_pom.py index 983f2828261c..145f08aa3b63 100644 --- a/monorepo-migration/modernize_pom.py +++ b/monorepo-migration/modernize_pom.py @@ -1,3 +1,17 @@ +# Copyright 2026 Google LLC +# +# Licensed 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 sys import re diff --git a/monorepo-migration/transform_workflow.py b/monorepo-migration/transform_workflow.py index 0e6b1c6927cb..3a719bb9710e 100644 --- a/monorepo-migration/transform_workflow.py +++ b/monorepo-migration/transform_workflow.py @@ -1,3 +1,17 @@ +# Copyright 2026 Google LLC +# +# Licensed 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 sys import re diff --git a/monorepo-migration/update_root_pom.py b/monorepo-migration/update_root_pom.py index 82822dd7734d..fec12930dee3 100644 --- a/monorepo-migration/update_root_pom.py +++ b/monorepo-migration/update_root_pom.py @@ -1,4 +1,17 @@ #!/usr/bin/env python3 +# Copyright 2026 Google LLC +# +# Licensed 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 sys From 9c82d206190e2ba0071dabe617a3bcc7e6b03d40 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 18:08:40 +0000 Subject: [PATCH 21/44] fix: use 2026 when fixing copyright headers --- monorepo-migration/migrate.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index c51434a562af..f6f20384d2ff 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -236,11 +236,11 @@ fi # 7.8 Fix copyright headers in Java files echo "Fixing copyright headers in Java files..." -find "$SOURCE_REPO_NAME" -name "*.java" -exec python3 -c "import sys, re; p = sys.argv[1]; c = open(p).read(); new_c = re.sub(r'Copyright \d{4} Google (Inc\.|LLC)', 'Copyright 2025 Google LLC', c); open(p, 'w').write(new_c)" {} \; +find "$SOURCE_REPO_NAME" -name "*.java" -exec python3 -c "import sys, re; p = sys.argv[1]; c = open(p).read(); new_c = re.sub(r'Copyright \d{4} Google (Inc\.|LLC)', 'Copyright 2026 Google LLC', c); open(p, 'w').write(new_c)" {} \; echo "Committing copyright header fixes..." git add "$SOURCE_REPO_NAME" -git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): update copyright headers to 2025 Google LLC" +git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): update copyright headers to 2026 Google LLC" # 7.9 Modernize root pom.xml echo "Modernizing root pom.xml..." From ae153dd54441969da7afd7e2e7baa059505d8f61 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 19:27:57 +0000 Subject: [PATCH 22/44] refactor: extract fix copyright inline python into .py file --- monorepo-migration/fix_copyright_headers.py | 53 +++++++++++++++++++++ monorepo-migration/migrate.sh | 3 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 monorepo-migration/fix_copyright_headers.py diff --git a/monorepo-migration/fix_copyright_headers.py b/monorepo-migration/fix_copyright_headers.py new file mode 100644 index 000000000000..cd813851d383 --- /dev/null +++ b/monorepo-migration/fix_copyright_headers.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# Copyright 2026 Google LLC +# +# Licensed 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 os +import re +import sys + +def fix_copyright(path): + if os.path.isfile(path): + if path.endswith(".java"): + _fix_file(path) + elif os.path.isdir(path): + for root, _, files in os.walk(path): + for file in files: + if file.endswith(".java"): + _fix_file(os.path.join(root, file)) + +def _fix_file(file_path): + with open(file_path, 'r') as f: + content = f.read() + + # Replace "Copyright [Year] Google LLC" or "Copyright [Year] Google Inc." + # with "Copyright 2026 Google LLC" + new_content = re.sub( + r'Copyright \d{4} Google (Inc\.|LLC)', + 'Copyright 2026 Google LLC', + content + ) + + if new_content != content: + with open(file_path, 'w') as f: + f.write(new_content) + print(f"Updated copyright in {file_path}") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: fix_copyright_headers.py ...") + sys.exit(1) + + for arg in sys.argv[1:]: + fix_copyright(arg) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index f6f20384d2ff..996d6575571d 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -49,6 +49,7 @@ TRANSFORM_SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" TRANSFORM_SCRIPT="$TRANSFORM_SCRIPT_DIR/transform_workflow.py" MODERNIZE_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/modernize_pom.py" UPDATE_ROOT_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_root_pom.py" +FIX_COPYRIGHT_SCRIPT="$TRANSFORM_SCRIPT_DIR/fix_copyright_headers.py" echo "Starting migration using git read-tree with isolated clones..." @@ -236,7 +237,7 @@ fi # 7.8 Fix copyright headers in Java files echo "Fixing copyright headers in Java files..." -find "$SOURCE_REPO_NAME" -name "*.java" -exec python3 -c "import sys, re; p = sys.argv[1]; c = open(p).read(); new_c = re.sub(r'Copyright \d{4} Google (Inc\.|LLC)', 'Copyright 2026 Google LLC', c); open(p, 'w').write(new_c)" {} \; +python3 "$FIX_COPYRIGHT_SCRIPT" "$SOURCE_REPO_NAME" echo "Committing copyright header fixes..." git add "$SOURCE_REPO_NAME" From c99b25e6dd069a7bcc21e8da532d3fee1c0cd6c3 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 20:25:14 +0000 Subject: [PATCH 23/44] feat: update urls in the migrated pom.xml --- monorepo-migration/migrate.sh | 2 +- monorepo-migration/modernize_pom.py | 34 ++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 996d6575571d..e9d59f4480fc 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -246,7 +246,7 @@ git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): update copyright heade # 7.9 Modernize root pom.xml echo "Modernizing root pom.xml..." PARENT_VERSION=$(grep -m 1 ".*{x-version-update:google-cloud-java:current}" google-cloud-jar-parent/pom.xml | sed -E 's/.*(.*)<\/version>.*/\1/') -python3 "$MODERNIZE_POM_SCRIPT" "$SOURCE_REPO_NAME/pom.xml" "$PARENT_VERSION" +python3 "$MODERNIZE_POM_SCRIPT" "$SOURCE_REPO_NAME/pom.xml" "$PARENT_VERSION" "$SOURCE_REPO_NAME" echo "Committing root pom.xml modernization..." git add "$SOURCE_REPO_NAME/pom.xml" diff --git a/monorepo-migration/modernize_pom.py b/monorepo-migration/modernize_pom.py index 145f08aa3b63..62879d77a79a 100644 --- a/monorepo-migration/modernize_pom.py +++ b/monorepo-migration/modernize_pom.py @@ -15,7 +15,7 @@ import sys import re -def modernize_pom(file_path, parent_version): +def modernize_pom(file_path, parent_version, source_repo_name=None): with open(file_path, 'r') as f: lines = f.readlines() @@ -29,6 +29,33 @@ def modernize_pom(file_path, parent_version): has_x_version_update = False for line in lines: + # URL Modernization + if any(tag in line for tag in ['', '', '']): + if 'github.com' in line and 'googleapis/' in line: + if source_repo_name: + repo_pattern = re.escape(source_repo_name) + else: + repo_pattern = r'[a-zA-Z0-9-]+' + + # Replace HTTPS URLs + line = re.sub( + r'https://github\.com/googleapis/' + repo_pattern, + 'https://github.com/googleapis/google-cloud-java', + line + ) + # Replace Git SSH URLs + line = re.sub( + r'git@github\.com:googleapis/' + repo_pattern + r'(\.git)?', + 'git@github.com:googleapis/google-cloud-java.git', + line + ) + # Handle scm:git: prefix if it has https + line = re.sub( + r'scm:git:https://github\.com/googleapis/' + repo_pattern, + 'scm:git:https://github.com/googleapis/google-cloud-java.git', + line + ) + # Parent section modernization if '' in line and not in_parent: in_parent = True @@ -110,7 +137,8 @@ def modernize_pom(file_path, parent_version): if __name__ == "__main__": if len(sys.argv) > 2: - modernize_pom(sys.argv[1], sys.argv[2]) + source_repo = sys.argv[3] if len(sys.argv) > 3 else None + modernize_pom(sys.argv[1], sys.argv[2], source_repo) else: - print("Usage: python3 modernize_pom.py ") + print("Usage: python3 modernize_pom.py [source_repo_name]") sys.exit(1) From 44bb26afa338ff69bc8135597d756ac60977d8ab Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 21:49:17 +0000 Subject: [PATCH 24/44] fix: preserve explicilty declared versions in pom.xml --- monorepo-migration/modernize_pom.py | 33 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/monorepo-migration/modernize_pom.py b/monorepo-migration/modernize_pom.py index 62879d77a79a..407a2d8c75cc 100644 --- a/monorepo-migration/modernize_pom.py +++ b/monorepo-migration/modernize_pom.py @@ -26,7 +26,9 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): in_dependency = False in_reporting = False current_dependency_lines = [] - has_x_version_update = False + should_preserve = False + current_group_id = None + has_version = False for line in lines: # URL Modernization @@ -36,7 +38,7 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): repo_pattern = re.escape(source_repo_name) else: repo_pattern = r'[a-zA-Z0-9-]+' - + # Replace HTTPS URLs line = re.sub( r'https://github\.com/googleapis/' + repo_pattern, @@ -82,7 +84,7 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): in_dep_mgmt = False new_lines.append(line) continue - + if in_dep_mgmt: if '' in line: in_dependencies = True @@ -92,26 +94,39 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): in_dependencies = False new_lines.append(line) continue - + if in_dependencies: if '' in line: in_dependency = True current_dependency_lines = [line] - has_x_version_update = False + should_preserve = False + current_group_id = None + has_version = False continue if '' in line: in_dependency = False current_dependency_lines.append(line) - if has_x_version_update: + + # Preservation logic: + # 1. Has x-version-update comment + # 2. Is NOT com.google group AND has a version tag + is_external = current_group_id and not current_group_id.startswith('com.google') + if should_preserve or (is_external and has_version): new_lines.extend(current_dependency_lines) continue - + if in_dependency: current_dependency_lines.append(line) if '{x-version-update:' in line: - has_x_version_update = True + should_preserve = True + if '' in line: + match = re.search(r'(.*?)', line) + if match: + current_group_id = match.group(1).strip() + if '' in line: + has_version = True continue - + # Prune comments and extra whitespace in depMgmt for a cleaner result if not line.strip(): new_lines.append(line) From 40c83144b373bb2006d903bab333ecb78d764a76 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 22:04:50 +0000 Subject: [PATCH 25/44] fix: also preserve explicit google-cloud-x dependency versions --- monorepo-migration/modernize_pom.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/monorepo-migration/modernize_pom.py b/monorepo-migration/modernize_pom.py index 407a2d8c75cc..bdf7f57a6027 100644 --- a/monorepo-migration/modernize_pom.py +++ b/monorepo-migration/modernize_pom.py @@ -101,6 +101,7 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): current_dependency_lines = [line] should_preserve = False current_group_id = None + current_artifact_id = None has_version = False continue if '' in line: @@ -110,8 +111,11 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): # Preservation logic: # 1. Has x-version-update comment # 2. Is NOT com.google group AND has a version tag + # 3. Is com.google.cloud group AND artifactId starts with google-cloud- AND has a version tag is_external = current_group_id and not current_group_id.startswith('com.google') - if should_preserve or (is_external and has_version): + is_google_cloud_lib = current_group_id == 'com.google.cloud' and current_artifact_id and current_artifact_id.startswith('google-cloud-') + + if should_preserve or (is_external and has_version) or (is_google_cloud_lib and has_version): new_lines.extend(current_dependency_lines) continue @@ -123,6 +127,10 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): match = re.search(r'(.*?)', line) if match: current_group_id = match.group(1).strip() + if '' in line: + match = re.search(r'(.*?)', line) + if match: + current_artifact_id = match.group(1).strip() if '' in line: has_version = True continue From 5a930dfca20831b1d72e03aac0534ce9bb693637 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 23:22:28 +0000 Subject: [PATCH 26/44] feat: prompt for CODEOWNERS value --- monorepo-migration/migrate.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index e9d59f4480fc..589b35b19126 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -34,6 +34,9 @@ check_command mvn MONOREPO_URL="https://github.com/googleapis/google-cloud-java" SOURCE_REPO_URL="${SOURCE_REPO_URL:-https://github.com/googleapis/java-logging}" CODEOWNER="${CODEOWNER:-}" +if [ -z "$CODEOWNER" ]; then + read -p "Enter CODEOWNER (e.g., @chingor13): " CODEOWNER +fi # Derive names from URLs to avoid duplication SOURCE_REPO_NAME="${SOURCE_REPO_URL##*/}" From 4c4dce53be921dccd83fe0a6cde6f841a1f4f968 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 8 Jan 2026 23:26:18 +0000 Subject: [PATCH 27/44] feat: prompt for SOURCE_REPO_URL value --- monorepo-migration/migrate.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 589b35b19126..54bddeecf44c 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -32,7 +32,10 @@ check_command mvn # Configuration MONOREPO_URL="https://github.com/googleapis/google-cloud-java" -SOURCE_REPO_URL="${SOURCE_REPO_URL:-https://github.com/googleapis/java-logging}" +if [ -z "$SOURCE_REPO_URL" ]; then + read -p "Enter SOURCE_REPO_URL [https://github.com/googleapis/java-logging]: " input_url + SOURCE_REPO_URL="${input_url:-https://github.com/googleapis/java-logging}" +fi CODEOWNER="${CODEOWNER:-}" if [ -z "$CODEOWNER" ]; then read -p "Enter CODEOWNER (e.g., @chingor13): " CODEOWNER From 415c3897c74320ce07e50ff1a97fce88fcf5f173 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Mon, 12 Jan 2026 22:07:53 +0000 Subject: [PATCH 28/44] feat: merge generator config yaml --- monorepo-migration/migrate.sh | 13 +-- .../update_generation_config.py | 107 ++++++++++++++++++ 2 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 monorepo-migration/update_generation_config.py diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 54bddeecf44c..4e425e4a596c 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -56,6 +56,7 @@ TRANSFORM_SCRIPT="$TRANSFORM_SCRIPT_DIR/transform_workflow.py" MODERNIZE_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/modernize_pom.py" UPDATE_ROOT_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_root_pom.py" FIX_COPYRIGHT_SCRIPT="$TRANSFORM_SCRIPT_DIR/fix_copyright_headers.py" +UPDATE_GENERATION_CONFIG_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_generation_config.py" echo "Starting migration using git read-tree with isolated clones..." @@ -209,17 +210,7 @@ fi echo "Updating generation_config.yaml..." SOURCE_CONFIG="$SOURCE_REPO_NAME/generation_config.yaml" if [ -f "$SOURCE_CONFIG" ]; then - # Extract the library entry (starts with - api_shortname) - # This assumes the source config only has one library or we want the first one - ENTRY=$(awk '/^ - api_shortname:/{flag=1; print $0; next} /^ - / && flag{flag=0} flag' "$SOURCE_CONFIG") - - # Simple cleanup: remove repo and repo_short if they exist - # Adjust indentation to match monorepo (0 spaces for -) - CLEAN_ENTRY=$(echo "$ENTRY" | sed '/repo:/d' | sed '/repo_short:/d' | sed 's/^ //') - - # Append to target generation_config.yaml - echo "" >> generation_config.yaml - echo "$CLEAN_ENTRY" >> generation_config.yaml + python3 "$UPDATE_GENERATION_CONFIG_SCRIPT" "generation_config.yaml" "$SOURCE_CONFIG" echo "Committing generation_config.yaml update..." git add generation_config.yaml diff --git a/monorepo-migration/update_generation_config.py b/monorepo-migration/update_generation_config.py new file mode 100644 index 000000000000..1ba7e6fc8c3e --- /dev/null +++ b/monorepo-migration/update_generation_config.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# Copyright 2026 Google LLC +# +# Licensed 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 sys +import yaml +import re + +def get_library_id(lib): + """ + Returns a unique identifier for a library. + Prefer 'library_name', then 'api_shortname'. + """ + if 'library_name' in lib: + return f"java-{lib['library_name']}" + if 'api_shortname' in lib: + return f"java-{lib['api_shortname']}" + return "unknown" + +def merge_libraries(target_libs, source_libs): + """ + Merges source_libs into target_libs. + Libraries are matched by get_library_id. + GAPICs are merged and deduplicated by proto_path. + The final list is sorted by library_id. + """ + # Map from library_id to library dict + target_map = {get_library_id(lib): lib for lib in target_libs} + + for s_lib in source_libs: + lib_id = get_library_id(s_lib) + + # Clean up source library (remove repo fields) + s_lib_cleaned = {k: v for k, v in s_lib.items() if k not in ('repo', 'repo_short')} + + if lib_id in target_map: + t_lib = target_map[lib_id] + # Merge GAPICs + t_gapics_list = t_lib.get('GAPICs', []) + s_gapics_list = s_lib_cleaned.get('GAPICs', []) + + # Map by proto_path for deduplication + proto_map = {g['proto_path']: g for g in t_gapics_list} + for g in s_gapics_list: + proto_map[g['proto_path']] = g + + # Sort GAPICs by proto_path + sorted_protos = sorted(proto_map.keys()) + t_lib['GAPICs'] = [proto_map[p] for p in sorted_protos] + + # Update other fields from source + for k, v in s_lib_cleaned.items(): + if k != 'GAPICs': + t_lib[k] = v + else: + target_map[lib_id] = s_lib_cleaned + + # Return sorted list of libraries + sorted_ids = sorted(target_map.keys()) + return [target_map[lib_id] for lib_id in sorted_ids] + +def update_config(target_path, source_path): + with open(target_path, 'r') as f: + target_content = f.read() + + with open(source_path, 'r') as f: + source_data = yaml.safe_load(f) or {} + + # Load target data + target_data = yaml.safe_load(target_content) or {} + + target_libs = target_data.get('libraries', []) + source_libs = source_data.get('libraries', []) + + merged_libs = merge_libraries(target_libs, source_libs) + target_data['libraries'] = merged_libs + + # Write back + with open(target_path, 'w') as f: + # Check if there was a license header in the original file + header_match = re.search(r'^(#.*?\n\n)', target_content, re.DOTALL) + if header_match: + f.write(header_match.group(1)) + + # Use yaml.dump to write the data. + # sort_keys=False to preserve order of fields within libraries if possible (YAML 1.2+ usually does, but pyyaml depends) + yaml.dump(target_data, f, sort_keys=False, default_flow_style=False, indent=2) + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python3 update_generation_config.py ") + sys.exit(1) + + target_path = sys.argv[1] + source_path = sys.argv[2] + update_config(target_path, source_path) From 60470ebb13176ad28ead5b543aff7b6befaf10f4 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 14 Jan 2026 22:26:50 +0000 Subject: [PATCH 29/44] chore: read root CODEOWNER entry from source --- monorepo-migration/migrate.sh | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 4e425e4a596c..682b9839b126 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -37,9 +37,6 @@ if [ -z "$SOURCE_REPO_URL" ]; then SOURCE_REPO_URL="${input_url:-https://github.com/googleapis/java-logging}" fi CODEOWNER="${CODEOWNER:-}" -if [ -z "$CODEOWNER" ]; then - read -p "Enter CODEOWNER (e.g., @chingor13): " CODEOWNER -fi # Derive names from URLs to avoid duplication SOURCE_REPO_NAME="${SOURCE_REPO_URL##*/}" @@ -77,6 +74,35 @@ else cd - > /dev/null fi +# 1.5 Extract CODEOWNERS from source repository as default +if [ -z "$CODEOWNER" ]; then + echo "Attempting to find default CODEOWNER from source repository..." + CODEOWNERS_FILE="" + if [ -f "$SOURCE_DIR/.github/CODEOWNERS" ]; then + CODEOWNERS_FILE="$SOURCE_DIR/.github/CODEOWNERS" + elif [ -f "$SOURCE_DIR/CODEOWNERS" ]; then + CODEOWNERS_FILE="$SOURCE_DIR/CODEOWNERS" + fi + + DEFAULT_CODEOWNER="" + if [ -n "$CODEOWNERS_FILE" ]; then + # Extract the line(s) starting with * (global owners) + # Use grep to find the line, then sed to remove the '*' and standard team handle + EXTRACTED_OWNERS=$(grep "^\*" "$CODEOWNERS_FILE" | sed 's/^\*[[:space:]]*//' | sed 's/@googleapis\/cloud-java-team-teamsync//g' | xargs) + if [ -n "$EXTRACTED_OWNERS" ]; then + DEFAULT_CODEOWNER="$EXTRACTED_OWNERS" + echo "Found default CODEOWNER: $DEFAULT_CODEOWNER" + fi + fi + + if [ -n "$DEFAULT_CODEOWNER" ]; then + read -p "Enter CODEOWNER [$DEFAULT_CODEOWNER]: " input_owner + CODEOWNER="${input_owner:-$DEFAULT_CODEOWNER}" + else + read -p "Enter CODEOWNER (e.g., @chingor13): " CODEOWNER + fi +fi + # 2. Clone the target monorepo (the "isolated clone") if [ ! -d "$TARGET_DIR" ]; then echo "Cloning target monorepo: $MONOREPO_URL into $TARGET_DIR" From ab868647fd4cebd1bdcbaed7069d3b83e14c2b4f Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 14 Jan 2026 23:51:38 +0000 Subject: [PATCH 30/44] migrate .OwlBot-hermetic.yaml --- monorepo-migration/migrate.sh | 24 +++++- monorepo-migration/update_owlbot_hermetic.py | 77 ++++++++++++++++++++ 2 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 monorepo-migration/update_owlbot_hermetic.py diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 682b9839b126..3c81fc860542 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -54,6 +54,7 @@ MODERNIZE_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/modernize_pom.py" UPDATE_ROOT_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_root_pom.py" FIX_COPYRIGHT_SCRIPT="$TRANSFORM_SCRIPT_DIR/fix_copyright_headers.py" UPDATE_GENERATION_CONFIG_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_generation_config.py" +UPDATE_OWLBOT_HERMETIC_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_owlbot_hermetic.py" echo "Starting migration using git read-tree with isolated clones..." @@ -258,7 +259,24 @@ if [ -f "$SOURCE_VERSIONS" ]; then git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): consolidate versions.txt into root" fi -# 7.8 Fix copyright headers in Java files +# 7.8 Migrate .OwlBot-hermetic.yaml +echo "Migrating .OwlBot-hermetic.yaml..." +if [ -f "$SOURCE_DIR/.github/.OwlBot-hermetic.yaml" ]; then + SOURCE_OWLBOT="$SOURCE_DIR/.github/.OwlBot-hermetic.yaml" +else + SOURCE_OWLBOT="" +fi + +if [ -n "$SOURCE_OWLBOT" ]; then + TARGET_OWLBOT="$SOURCE_REPO_NAME/.OwlBot-hermetic.yaml" + python3 "$UPDATE_OWLBOT_HERMETIC_SCRIPT" "$TARGET_OWLBOT" "$SOURCE_OWLBOT" "$SOURCE_REPO_NAME" + + echo "Committing .OwlBot-hermetic.yaml migration..." + git add "$TARGET_OWLBOT" + git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate .OwlBot-hermetic.yaml" +fi + +# 7.9 Fix copyright headers in Java files echo "Fixing copyright headers in Java files..." python3 "$FIX_COPYRIGHT_SCRIPT" "$SOURCE_REPO_NAME" @@ -266,7 +284,7 @@ echo "Committing copyright header fixes..." git add "$SOURCE_REPO_NAME" git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): update copyright headers to 2026 Google LLC" -# 7.9 Modernize root pom.xml +# 7.10 Modernize root pom.xml echo "Modernizing root pom.xml..." PARENT_VERSION=$(grep -m 1 ".*{x-version-update:google-cloud-java:current}" google-cloud-jar-parent/pom.xml | sed -E 's/.*(.*)<\/version>.*/\1/') python3 "$MODERNIZE_POM_SCRIPT" "$SOURCE_REPO_NAME/pom.xml" "$PARENT_VERSION" "$SOURCE_REPO_NAME" @@ -275,7 +293,7 @@ echo "Committing root pom.xml modernization..." git add "$SOURCE_REPO_NAME/pom.xml" git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): modernize root pom.xml" -# 7.10 Verify compilation +# 7.11 Verify compilation echo "Verifying compilation..." (cd "$SOURCE_REPO_NAME" && mvn compile -DskipTests -T 1C) diff --git a/monorepo-migration/update_owlbot_hermetic.py b/monorepo-migration/update_owlbot_hermetic.py new file mode 100644 index 000000000000..7f9f803822f5 --- /dev/null +++ b/monorepo-migration/update_owlbot_hermetic.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# Copyright 2026 Google LLC +# +# Licensed 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 sys +import yaml +import re + +def update_config(target_path, source_path, prefix): + """ + Reads source_path, prepends prefix to paths in deep-remove-regex and deep-preserve-regex, + and writes to target_path. + """ + with open(source_path, 'r') as f: + source_content = f.read() + + # Load source data + source_data = yaml.safe_load(source_content) or {} + + # Define fields to update + fields_to_update = ['deep-remove-regex', 'deep-preserve-regex'] + + for field in fields_to_update: + if field in source_data: + updated_list = [] + for item in source_data[field]: + # If item is a string, prepend prefix + # Regex might need handling if it starts with ^ + # But usually these are just paths. + # Assuming simple concatenation for now as per requirement. + # "When referencing paths in the deep-remove-regex and deep-preserve-regex, the new directory name should be prefixed" + + # If the regex starts with ^, insert the prefix after it. + if item.startswith('^'): + updated_list.append(f"^{prefix}/{item[1:]}") + else: + updated_list.append(f"/{prefix}{item}") + source_data[field] = updated_list + + if 'deep-copy-regex' in source_data: + for item in source_data['deep-copy-regex']: + if 'dest' in item and item['dest'].startswith('/owl-bot-staging/'): + item['dest'] = item['dest'].replace('/owl-bot-staging/', f'/owl-bot-staging/{prefix}/', 1) + + # Write to target_path + with open(target_path, 'w') as f: + # Check if there was a license header in the original file + # Match a block of lines starting with # at the beginning of the file + header_match = re.search(r'^((?:#[^\n]*\n)+)', source_content) + if header_match: + f.write(header_match.group(1)) + f.write("\n") # Add a newline after the header + + # Use yaml.dump to write the data. + yaml.dump(source_data, f, sort_keys=False, default_flow_style=False, indent=2) + +if __name__ == "__main__": + if len(sys.argv) != 4: + print("Usage: python3 update_owlbot_hermetic.py ") + sys.exit(1) + + target_path = sys.argv[1] + source_path = sys.argv[2] + prefix = sys.argv[3] + + update_config(target_path, source_path, prefix) From 4613a4efeedf9d4af15cc8889896695b992d3699 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 15 Jan 2026 18:07:30 +0000 Subject: [PATCH 31/44] migrate existing owlbot.py file and replace the template excludes --- monorepo-migration/migrate.sh | 25 +++++ monorepo-migration/update_owlbot.py | 159 ++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 monorepo-migration/update_owlbot.py diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 3c81fc860542..63a8b1914a1c 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -55,6 +55,7 @@ UPDATE_ROOT_POM_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_root_pom.py" FIX_COPYRIGHT_SCRIPT="$TRANSFORM_SCRIPT_DIR/fix_copyright_headers.py" UPDATE_GENERATION_CONFIG_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_generation_config.py" UPDATE_OWLBOT_HERMETIC_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_owlbot_hermetic.py" +TRANSFORM_OWLBOT_SCRIPT="$TRANSFORM_SCRIPT_DIR/update_owlbot.py" echo "Starting migration using git read-tree with isolated clones..." @@ -276,6 +277,30 @@ if [ -n "$SOURCE_OWLBOT" ]; then git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate .OwlBot-hermetic.yaml" fi + +# 7.8b Migrate owlbot.py +echo "Migrating owlbot.py..." +if [ -f "$SOURCE_DIR/owlbot.py" ]; then + TARGET_OWLBOT="$SOURCE_REPO_NAME/owlbot.py" + + # Check for a template owlbot.py to source common excludes from + TEMPLATE_OWLBOT="" + if [ -f "java-workstations/owlbot.py" ]; then + TEMPLATE_OWLBOT="java-workstations/owlbot.py" + echo "Using $TEMPLATE_OWLBOT as template for excludes." + fi + + if [ -n "$TEMPLATE_OWLBOT" ]; then + python3 "$TRANSFORM_OWLBOT_SCRIPT" "$TARGET_OWLBOT" "$SOURCE_DIR/owlbot.py" "$TEMPLATE_OWLBOT" + else + python3 "$TRANSFORM_OWLBOT_SCRIPT" "$TARGET_OWLBOT" "$SOURCE_DIR/owlbot.py" + fi + + echo "Committing owlbot.py migration..." + git add "$TARGET_OWLBOT" + git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): migrate owlbot.py" +fi + # 7.9 Fix copyright headers in Java files echo "Fixing copyright headers in Java files..." python3 "$FIX_COPYRIGHT_SCRIPT" "$SOURCE_REPO_NAME" diff --git a/monorepo-migration/update_owlbot.py b/monorepo-migration/update_owlbot.py new file mode 100644 index 000000000000..ec40992ca412 --- /dev/null +++ b/monorepo-migration/update_owlbot.py @@ -0,0 +1,159 @@ +# Copyright 2026 Google LLC +# +# Licensed 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 ast +import sys +import os + +def extract_info(source_code): + """Extracts excludes and replacement calls from the source owlbot.py.""" + excludes = [] + replacements = [] + + try: + tree = ast.parse(source_code) + except SyntaxError: + return excludes, replacements + + for node in ast.walk(tree): + # Look for s.replace(...) calls + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Attribute) and node.func.attr == 'replace': + # We found a replace call. We want to reconstruct the source code for this call. + # However, ast.unparse is only available in Python 3.9+. + # If we are on an older python, we might need a workaround or just grab the line range. + # Since we likely have 3.9+, let's try ast.unparse if available, otherwise strict extraction. + if sys.version_info >= (3, 9): + replacements.append(ast.unparse(node)) + else: + # Fallback for older python: just comment it needs manual migration or try simple extraction + # For now assume 3.9+ which is standard in this environment + pass + + # Look for java.common_templates or common.java_library calls to find excludes + if isinstance(node, ast.Call): + is_common_templates = False + if isinstance(node.func, ast.Attribute): + if node.func.attr == 'common_templates': + is_common_templates = True + elif node.func.attr == 'java_library': # older name + is_common_templates = True + + if is_common_templates: + for keyword in node.keywords: + if keyword.arg == 'excludes': + if isinstance(keyword.value, ast.List): + for elt in keyword.value.elts: + if isinstance(elt, ast.Constant): # Python 3.8+ + excludes.append(elt.value) + elif isinstance(elt, ast.Str): # Python < 3.8 + excludes.append(elt.s) + break + return excludes, replacements + + +def generate_target_content(excludes, replacements, standard_excludes=None): + # Default excludes for monorepo if no template is provided + if standard_excludes is None: + standard_excludes = { + ".github/*", + ".kokoro/*", + "samples/*", + "CODE_OF_CONDUCT.md", + "CONTRIBUTING.md", + "LICENSE", + "SECURITY.md", + "java.header", + "license-checks.xml", + "renovate.json", + ".gitignore" + } + + # Merge excludes + # User requested to ignore source excludes and strictly use standard excludes + final_excludes = sorted(list(set(standard_excludes))) + + # Format replacements with indentation + formatted_replacements = "" + for rep in replacements: + formatted_replacements += " " + rep + "\n" + + excludes_str = ",\n ".join([f'"{e}"' for e in final_excludes]) + + content = f"""# Copyright 2026 Google LLC +# +# Licensed 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 +# +# https://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 synthtool as s +from synthtool.languages import java + +for library in s.get_staging_dirs(): + # put any special-case replacements here + s.move(library) +{formatted_replacements} +s.remove_staging_dirs() +java.common_templates(monorepo=True, excludes=[ + {excludes_str} +]) +""" + return content + +def main(): + if len(sys.argv) < 3: + print("Usage: update_owlbot.py [template_file]") + sys.exit(1) + + target_file = sys.argv[1] + source_file = sys.argv[2] + template_file = sys.argv[3] if len(sys.argv) > 3 else None + + if not os.path.exists(source_file): + print(f"Source file {source_file} not found.") + sys.exit(1) + + with open(source_file, 'r') as f: + source_code = f.read() + + excludes, replacements = extract_info(source_code) + + standard_excludes = None + if template_file: + if os.path.exists(template_file): + with open(template_file, 'r') as f: + template_code = f.read() + template_excludes, _ = extract_info(template_code) + standard_excludes = template_excludes + else: + print(f"Template file {template_file} not found. using default excludes.") + + target_content = generate_target_content(excludes, replacements, standard_excludes) + + os.makedirs(os.path.dirname(target_file), exist_ok=True) + with open(target_file, 'w') as f: + f.write(target_content) + + print(f"Generated {target_file}") + +if __name__ == "__main__": + main() From 56d9bec078da707a2ef4479ebdbfca959cc7c918 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 15 Jan 2026 18:17:26 +0000 Subject: [PATCH 32/44] skip .kokoro folder --- monorepo-migration/migrate.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 63a8b1914a1c..b1837594c054 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -171,6 +171,7 @@ rm -f "$SOURCE_REPO_NAME/.gitignore" rm -f "$SOURCE_REPO_NAME/renovate.json" rm -f "$SOURCE_REPO_NAME/LICENSE" rm -f "$SOURCE_REPO_NAME/java.header" +rm -rf "$SOURCE_REPO_NAME/.kokoro" rm -f "$SOURCE_REPO_NAME/license-checks.xml" find "$SOURCE_REPO_NAME" -maxdepth 1 -name "*.md" ! -name "CHANGELOG.md" ! -name "README.md" -delete From b8a87e32d2b4340054240711eaf8b26249050648 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 15 Jan 2026 18:27:43 +0000 Subject: [PATCH 33/44] remove more unnecessary files --- monorepo-migration/migrate.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index b1837594c054..7b519761bece 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -172,6 +172,8 @@ rm -f "$SOURCE_REPO_NAME/renovate.json" rm -f "$SOURCE_REPO_NAME/LICENSE" rm -f "$SOURCE_REPO_NAME/java.header" rm -rf "$SOURCE_REPO_NAME/.kokoro" +rm -f "$SOURCE_REPO_NAME/codecov.yaml" +rm -f "$SOURCE_REPO_NAME/synth.metadata" rm -f "$SOURCE_REPO_NAME/license-checks.xml" find "$SOURCE_REPO_NAME" -maxdepth 1 -name "*.md" ! -name "CHANGELOG.md" ! -name "README.md" -delete From 84e03752368fba015e68671e82086c6a9e4b529b Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 15 Jan 2026 20:32:04 +0000 Subject: [PATCH 34/44] remove only kokoro configs and clean up generation config --- monorepo-migration/migrate.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 7b519761bece..95e6f58656b3 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -171,7 +171,7 @@ rm -f "$SOURCE_REPO_NAME/.gitignore" rm -f "$SOURCE_REPO_NAME/renovate.json" rm -f "$SOURCE_REPO_NAME/LICENSE" rm -f "$SOURCE_REPO_NAME/java.header" -rm -rf "$SOURCE_REPO_NAME/.kokoro" +rm -rf "$SOURCE_REPO_NAME/.kokoro/continuous" "$SOURCE_REPO_NAME/.kokoro/nightly" "$SOURCE_REPO_NAME/.kokoro/presubmit" rm -f "$SOURCE_REPO_NAME/codecov.yaml" rm -f "$SOURCE_REPO_NAME/synth.metadata" rm -f "$SOURCE_REPO_NAME/license-checks.xml" @@ -243,8 +243,11 @@ SOURCE_CONFIG="$SOURCE_REPO_NAME/generation_config.yaml" if [ -f "$SOURCE_CONFIG" ]; then python3 "$UPDATE_GENERATION_CONFIG_SCRIPT" "generation_config.yaml" "$SOURCE_CONFIG" + # Remove the source generation_config.yaml as it is now merged + rm "$SOURCE_CONFIG" + echo "Committing generation_config.yaml update..." - git add generation_config.yaml + git add generation_config.yaml "$SOURCE_CONFIG" git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): add library to generation_config.yaml" fi From 8c3f984063cfeb983a476337a01b8b6b08aff4f6 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Thu, 15 Jan 2026 21:22:22 +0000 Subject: [PATCH 35/44] disable java17 profile for split repo's java8 test --- monorepo-migration/transform_workflow.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monorepo-migration/transform_workflow.py b/monorepo-migration/transform_workflow.py index 3a719bb9710e..a333b1491fcf 100644 --- a/monorepo-migration/transform_workflow.py +++ b/monorepo-migration/transform_workflow.py @@ -73,6 +73,8 @@ def transform(content, lib_name): continue if not skip_current_job: + if 'run: echo "SUREFIRE_JVM_OPT=' in line and '!java17' not in line: + line = line.replace('" >> $GITHUB_ENV', ' -P !java17" >> $GITHUB_ENV') new_lines.append(line) return "\n".join(new_lines) From ece711f976b185bdc6136aa5142d1f017361a945 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Fri, 16 Jan 2026 01:12:24 +0000 Subject: [PATCH 36/44] add git config for longpaths on windows build --- monorepo-migration/transform_workflow.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/monorepo-migration/transform_workflow.py b/monorepo-migration/transform_workflow.py index a333b1491fcf..741a54f80fbb 100644 --- a/monorepo-migration/transform_workflow.py +++ b/monorepo-migration/transform_workflow.py @@ -35,6 +35,8 @@ def transform(content, lib_name): in_jobs = False skip_current_job = False + current_job_is_windows = False + for line in lines: if line.startswith('name:') and not in_jobs: name_match = re.match(r'^name:\s*(.*)', line) @@ -60,6 +62,7 @@ def transform(content, lib_name): job_match = re.match(r'^ ([\w-]+):', line) if job_match: job_name = job_match.group(1) + current_job_is_windows = False # Reset for new job if job_name == 'clirr': skip_current_job = True continue @@ -73,6 +76,15 @@ def transform(content, lib_name): continue if not skip_current_job: + if 'runs-on:' in line and 'windows' in line: + current_job_is_windows = True + + if line.strip() == 'steps:' and current_job_is_windows: + new_lines.append(line) + new_lines.append(" - name: Support longpaths") + new_lines.append(" run: git config --system core.longpaths true") + continue + if 'run: echo "SUREFIRE_JVM_OPT=' in line and '!java17' not in line: line = line.replace('" >> $GITHUB_ENV', ' -P !java17" >> $GITHUB_ENV') new_lines.append(line) From 2730b93cdae0586359b06a5c63d4c7c60f29d9c1 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Fri, 16 Jan 2026 21:16:38 +0000 Subject: [PATCH 37/44] fix working directory for longpaths --- monorepo-migration/transform_workflow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monorepo-migration/transform_workflow.py b/monorepo-migration/transform_workflow.py index 741a54f80fbb..f4175cf61c34 100644 --- a/monorepo-migration/transform_workflow.py +++ b/monorepo-migration/transform_workflow.py @@ -83,6 +83,7 @@ def transform(content, lib_name): new_lines.append(line) new_lines.append(" - name: Support longpaths") new_lines.append(" run: git config --system core.longpaths true") + new_lines.append(" working-directory: .") continue if 'run: echo "SUREFIRE_JVM_OPT=' in line and '!java17' not in line: From aeaa04f6aafd2998b94641e873aaf2081abd2a9e Mon Sep 17 00:00:00 2001 From: chingor13 Date: Fri, 16 Jan 2026 21:17:02 +0000 Subject: [PATCH 38/44] allow running root ci scripts in BUILD_SUBDIR --- .kokoro/build.sh | 15 ++++++++++++++- .kokoro/dependencies.sh | 12 ++++++++++++ generation/check_non_release_please_versions.sh | 4 ++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/.kokoro/build.sh b/.kokoro/build.sh index e604c8768b86..675fd276cbcd 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -33,10 +33,17 @@ if [ -f "${KOKORO_GFILE_DIR}/secret_manager/java-bigqueryconnection-samples-secr source "${KOKORO_GFILE_DIR}/secret_manager/java-bigqueryconnection-samples-secrets" fi +if [[ -n "${BUILD_SUBDIR}" ]] +then + echo "Running in subdir: ${BUILD_SUBDIR}" + pushd "${BUILD_SUBDIR}" +fi + RETURN_CODE=0 case ${JOB_TYPE} in test) + echo "SUREFIRE_JVM_OPT: ${SUREFIRE_JVM_OPT}" retry_with_backoff 3 10 \ mvn test \ -B -ntp \ @@ -48,7 +55,7 @@ case ${JOB_TYPE} in -Dflatten.skip=true \ -Danimal.sniffer.skip=true \ -Dmaven.wagon.http.retryHandler.count=5 \ - -T 1C + -T 1C ${SUREFIRE_JVM_OPT} RETURN_CODE=$? echo "Finished running unit tests" ;; @@ -125,6 +132,12 @@ case ${JOB_TYPE} in esac +if [[ -n "${BUILD_SUBDIR}" ]] +then + echo "restoring directory" + popd +fi + if [ "${REPORT_COVERAGE}" == "true" ]; then bash ${KOKORO_GFILE_DIR}/codecov.sh fi diff --git a/.kokoro/dependencies.sh b/.kokoro/dependencies.sh index bd8960246f66..1ea066f5bf77 100755 --- a/.kokoro/dependencies.sh +++ b/.kokoro/dependencies.sh @@ -49,6 +49,12 @@ function determineMavenOpts() { export MAVEN_OPTS=$(determineMavenOpts) +if [[ -n "${BUILD_SUBDIR}" ]] +then + echo "Running in subdir: ${BUILD_SUBDIR}" + pushd "${BUILD_SUBDIR}" +fi + # this should run maven enforcer retry_with_backoff 3 10 \ mvn install -B -V -ntp \ @@ -57,3 +63,9 @@ retry_with_backoff 3 10 \ -Dclirr.skip=true mvn -B dependency:analyze -DfailOnWarning=true + +if [[ -n "${BUILD_SUBDIR}" ]] +then + echo "Leaving subdir: ${BUILD_SUBDIR}" + popd +fi diff --git a/generation/check_non_release_please_versions.sh b/generation/check_non_release_please_versions.sh index bb7e2f0e88e0..0a0e32c22874 100755 --- a/generation/check_non_release_please_versions.sh +++ b/generation/check_non_release_please_versions.sh @@ -19,6 +19,10 @@ for pomFile in $(find . -mindepth 2 -name pom.xml | sort ); do echo "Skipping version check for java-samples directory" continue fi + if [[ "${pomFile}" =~ .*/samples/.* ]]; then + echo "Skipping version check for samples directory" + continue + fi if grep -n '.*' "$pomFile" | grep -v 'x-version-update'; then echo "Found version declaration(s) without x-version-update in: $pomFile" From 40f91a537dcae156e8a01459644fb24c1afd2b8d Mon Sep 17 00:00:00 2001 From: chingor13 Date: Fri, 16 Jan 2026 21:27:31 +0000 Subject: [PATCH 39/44] modernize the bom pom.xml --- monorepo-migration/migrate.sh | 15 ++++++++++++++- monorepo-migration/modernize_pom.py | 14 +++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 95e6f58656b3..b07b2aba5284 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -315,7 +315,7 @@ echo "Committing copyright header fixes..." git add "$SOURCE_REPO_NAME" git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): update copyright headers to 2026 Google LLC" -# 7.10 Modernize root pom.xml +# 7.11 Modernize root pom.xml echo "Modernizing root pom.xml..." PARENT_VERSION=$(grep -m 1 ".*{x-version-update:google-cloud-java:current}" google-cloud-jar-parent/pom.xml | sed -E 's/.*(.*)<\/version>.*/\1/') python3 "$MODERNIZE_POM_SCRIPT" "$SOURCE_REPO_NAME/pom.xml" "$PARENT_VERSION" "$SOURCE_REPO_NAME" @@ -324,6 +324,19 @@ echo "Committing root pom.xml modernization..." git add "$SOURCE_REPO_NAME/pom.xml" git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): modernize root pom.xml" +# 7.12 Modernize BOM pom.xml +echo "Modernizing BOM pom.xml..." +# Find potential BOM POMs (usually in a subdirectory ending with -bom) +find "$SOURCE_REPO_NAME" -name "pom.xml" | grep "\-bom/pom.xml" | while read -r bom_pom; do + echo "Modernizing BOM: $bom_pom" + # BOMs should inherit from google-cloud-pom-parent + python3 "$MODERNIZE_POM_SCRIPT" "$bom_pom" "$PARENT_VERSION" "$SOURCE_REPO_NAME" "google-cloud-pom-parent" "../../google-cloud-pom-parent/pom.xml" + + echo "Committing BOM pom.xml modernization for $bom_pom..." + git add "$bom_pom" + git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): modernize BOM pom.xml" +done + # 7.11 Verify compilation echo "Verifying compilation..." (cd "$SOURCE_REPO_NAME" && mvn compile -DskipTests -T 1C) diff --git a/monorepo-migration/modernize_pom.py b/monorepo-migration/modernize_pom.py index bdf7f57a6027..ea0c023a9b57 100644 --- a/monorepo-migration/modernize_pom.py +++ b/monorepo-migration/modernize_pom.py @@ -15,7 +15,8 @@ import sys import re -def modernize_pom(file_path, parent_version, source_repo_name=None): + +def modernize_pom(file_path, parent_version, source_repo_name=None, parent_artifactId='google-cloud-jar-parent', relative_path='../google-cloud-jar-parent/pom.xml'): with open(file_path, 'r') as f: lines = f.readlines() @@ -64,9 +65,9 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): indent = line[:line.find('<')] new_lines.append(f"{indent}\n") new_lines.append(f"{indent} com.google.cloud\n") - new_lines.append(f"{indent} google-cloud-jar-parent\n") + new_lines.append(f"{indent} {parent_artifactId}\n") new_lines.append(f"{indent} {parent_version}\n") - new_lines.append(f"{indent} ../google-cloud-jar-parent/pom.xml\n") + new_lines.append(f"{indent} {relative_path}\n") continue if '' in line and in_parent: in_parent = False @@ -161,7 +162,10 @@ def modernize_pom(file_path, parent_version, source_repo_name=None): if __name__ == "__main__": if len(sys.argv) > 2: source_repo = sys.argv[3] if len(sys.argv) > 3 else None - modernize_pom(sys.argv[1], sys.argv[2], source_repo) + parent_artifactId = sys.argv[4] if len(sys.argv) > 4 else 'google-cloud-jar-parent' + relative_path = sys.argv[5] if len(sys.argv) > 5 else '../google-cloud-jar-parent/pom.xml' + modernize_pom(sys.argv[1], sys.argv[2], source_repo, parent_artifactId, relative_path) else: - print("Usage: python3 modernize_pom.py [source_repo_name]") + print("Usage: python3 modernize_pom.py [source_repo_name] [parent_artifactId] [relative_path]") sys.exit(1) + From 19fb9b0d789d0b4ab080fe6ced6ff739efce6468 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 21 Jan 2026 21:16:19 +0000 Subject: [PATCH 40/44] allow specifying target branch --- monorepo-migration/migrate.sh | 16 +- monorepo-migration/update_owlbot.py | 237 ++++++++++++++++++---------- 2 files changed, 164 insertions(+), 89 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index b07b2aba5284..4b5d749cd5a5 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -62,6 +62,8 @@ echo "Starting migration using git read-tree with isolated clones..." # 0. Create working directory mkdir -p "$WORKING_DIR" +MIGRATION_HEAD_BRANCH="add-migration-script" + # 1. Clone the source repository if [ ! -d "$SOURCE_DIR" ]; then echo "Cloning source repo: $SOURCE_REPO_URL into $SOURCE_DIR" @@ -70,7 +72,7 @@ else echo "Source directory $SOURCE_DIR already exists. Ensuring it is clean and up-to-date..." cd "$SOURCE_DIR" git fetch origin - git checkout -f main + git checkout -f "main" git reset --hard origin/main git clean -fd cd - > /dev/null @@ -109,12 +111,14 @@ fi if [ ! -d "$TARGET_DIR" ]; then echo "Cloning target monorepo: $MONOREPO_URL into $TARGET_DIR" git clone "$MONOREPO_URL" "$TARGET_DIR" + git checkout -f "${MIGRATION_HEAD_BRANCH}" + git reset --hard origin/${MIGRATION_HEAD_BRANCH} else echo "Target directory $TARGET_DIR already exists. Ensuring it is clean and up-to-date..." cd "$TARGET_DIR" git fetch origin - git checkout -f main - git reset --hard origin/main + git checkout -f "${MIGRATION_HEAD_BRANCH}" + git reset --hard origin/${MIGRATION_HEAD_BRANCH} git clean -fd cd - > /dev/null fi @@ -126,8 +130,8 @@ echo "Ensuring clean state in target monorepo..." git fetch origin git reset --hard HEAD git clean -fd -git checkout -f main -git reset --hard origin/main +git checkout -f "${MIGRATION_HEAD_BRANCH}" +git reset --hard origin/${MIGRATION_HEAD_BRANCH} git clean -fdx # Check if the repository is already migrated @@ -327,7 +331,7 @@ git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): modernize root pom.xml # 7.12 Modernize BOM pom.xml echo "Modernizing BOM pom.xml..." # Find potential BOM POMs (usually in a subdirectory ending with -bom) -find "$SOURCE_REPO_NAME" -name "pom.xml" | grep "\-bom/pom.xml" | while read -r bom_pom; do +find "$SOURCE_REPO_NAME" -name "pom.xml" | grep "\-bom/pom.xml" | grep -v "samples" | while read -r bom_pom; do echo "Modernizing BOM: $bom_pom" # BOMs should inherit from google-cloud-pom-parent python3 "$MODERNIZE_POM_SCRIPT" "$bom_pom" "$PARENT_VERSION" "$SOURCE_REPO_NAME" "google-cloud-pom-parent" "../../google-cloud-pom-parent/pom.xml" diff --git a/monorepo-migration/update_owlbot.py b/monorepo-migration/update_owlbot.py index ec40992ca412..a89ff5182d3f 100644 --- a/monorepo-migration/update_owlbot.py +++ b/monorepo-migration/update_owlbot.py @@ -16,55 +16,122 @@ import sys import os +def is_call_to(node, name_parts): + """ + Checks if an AST node is a call to a specific function. + name_parts: list of strings, e.g. ['s', 'move'] for s.move() + or ['java', 'common_templates'] + """ + if not isinstance(node, ast.Call): + return False + + func = node.func + # Handle attribute access (e.g. s.move) + if isinstance(func, ast.Attribute): + if len(name_parts) == 2: + # Check object and attr + obj = func.value + if isinstance(obj, ast.Name) and obj.id == name_parts[0] and func.attr == name_parts[1]: + return True + elif len(name_parts) == 1: + if func.attr == name_parts[0]: + return True + # Handle direct name (e.g. if imported directly, though less common for these) + elif isinstance(func, ast.Name): + if len(name_parts) == 1 and func.id == name_parts[0]: + return True + + return False + +def extract_excludes_from_call(call_node): + excludes = [] + for keyword in call_node.keywords: + if keyword.arg == 'excludes': + if isinstance(keyword.value, ast.List): + for elt in keyword.value.elts: + if isinstance(elt, ast.Constant): # Python 3.8+ + excludes.append(elt.value) + elif isinstance(elt, ast.Str): # Python < 3.8 + excludes.append(elt.s) + break + return excludes + def extract_info(source_code): - """Extracts excludes and replacement calls from the source owlbot.py.""" excludes = [] - replacements = [] - + loop_body_lines = [] + top_level_lines = [] + try: tree = ast.parse(source_code) except SyntaxError: - return excludes, replacements - - for node in ast.walk(tree): - # Look for s.replace(...) calls - if isinstance(node, ast.Call): - if isinstance(node.func, ast.Attribute) and node.func.attr == 'replace': - # We found a replace call. We want to reconstruct the source code for this call. - # However, ast.unparse is only available in Python 3.9+. - # If we are on an older python, we might need a workaround or just grab the line range. - # Since we likely have 3.9+, let's try ast.unparse if available, otherwise strict extraction. - if sys.version_info >= (3, 9): - replacements.append(ast.unparse(node)) - else: - # Fallback for older python: just comment it needs manual migration or try simple extraction - # For now assume 3.9+ which is standard in this environment - pass - - # Look for java.common_templates or common.java_library calls to find excludes - if isinstance(node, ast.Call): - is_common_templates = False - if isinstance(node.func, ast.Attribute): - if node.func.attr == 'common_templates': - is_common_templates = True - elif node.func.attr == 'java_library': # older name - is_common_templates = True + return excludes, top_level_lines, loop_body_lines + + for node in tree.body: + # Some nodes are wrapped in Expr, e.g. s.remove_staging_dirs() + inner_node = node + if isinstance(node, ast.Expr): + inner_node = node.value + + # Ignore standard imports (we will inject them) + if isinstance(node, (ast.Import, ast.ImportFrom)): + # We assume we only care about synthtool/java imports which we regenerate. + # If there are other imports, we should probably preserve them. + # Heuristic: if it mentions 'synthtool', ignore it. + if isinstance(node, ast.Import): + if any('synthtool' in alias.name for alias in node.names): + continue + if isinstance(node, ast.ImportFrom): + if node.module and 'synthtool' in node.module: + continue + # Preserve other imports + if sys.version_info >= (3, 9): + top_level_lines.append(ast.unparse(node)) + continue + + # Check for java.common_templates (top level) + if is_call_to(inner_node, ['java', 'common_templates']) or is_call_to(inner_node, ['common', 'java_library']): + excludes.extend(extract_excludes_from_call(inner_node)) + continue + + # Check for s.remove_staging_dirs() + if is_call_to(inner_node, ['s', 'remove_staging_dirs']): + continue + + # Check for the main loop: for library in s.get_staging_dirs(): + if isinstance(node, ast.For): + is_staging_loop = False + if isinstance(node.iter, ast.Call): + # Check for s.get_staging_dirs() + if is_call_to(node.iter, ['s', 'get_staging_dirs']): + is_staging_loop = True - if is_common_templates: - for keyword in node.keywords: - if keyword.arg == 'excludes': - if isinstance(keyword.value, ast.List): - for elt in keyword.value.elts: - if isinstance(elt, ast.Constant): # Python 3.8+ - excludes.append(elt.value) - elif isinstance(elt, ast.Str): # Python < 3.8 - excludes.append(elt.s) - break - return excludes, replacements - - -def generate_target_content(excludes, replacements, standard_excludes=None): - # Default excludes for monorepo if no template is provided + if is_staging_loop: + # Extract body + for child in node.body: + child_inner = child + if isinstance(child, ast.Expr): + child_inner = child.value + + # Ignore s.move(library) + if is_call_to(child_inner, ['s', 'move']): + continue + # Check for nested common_templates (rare but possible) + if is_call_to(child_inner, ['java', 'common_templates']) or is_call_to(child_inner, ['common', 'java_library']): + excludes.extend(extract_excludes_from_call(child_inner)) + continue + + if sys.version_info >= (3, 9): + loop_body_lines.append(ast.unparse(child)) + continue + # else fall through to preserve other loops + + # Preserve everything else (constants, functions, other logic) + if sys.version_info >= (3, 9): + top_level_lines.append(ast.unparse(node)) + + return excludes, top_level_lines, loop_body_lines + +def generate_target_content(excludes, top_level_lines, loop_body_lines, standard_excludes=None): if standard_excludes is None: standard_excludes = { ".github/*", @@ -80,44 +147,47 @@ def generate_target_content(excludes, replacements, standard_excludes=None): ".gitignore" } - # Merge excludes - # User requested to ignore source excludes and strictly use standard excludes final_excludes = sorted(list(set(standard_excludes))) - - # Format replacements with indentation - formatted_replacements = "" - for rep in replacements: - formatted_replacements += " " + rep + "\n" - - excludes_str = ",\n ".join([f'"{e}"' for e in final_excludes]) - - content = f"""# Copyright 2026 Google LLC -# -# Licensed 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 -# -# https://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. + excludes_str = ",\n ".join([f'"{e}"' for e in final_excludes]) -import synthtool as s -from synthtool.languages import java + # Reconstruct content + lines = [] + lines.append("# Copyright 2026 Google LLC") + lines.append("#") + lines.append("# Licensed under the Apache License, Version 2.0 (the \"License\");") + lines.append("# you may not use this file except in compliance with the License.") + lines.append("# You may obtain a copy of the License at") + lines.append("#") + lines.append("# https://www.apache.org/licenses/LICENSE-2.0") + lines.append("#") + lines.append("# Unless required by applicable law or agreed to in writing, software") + lines.append("# distributed under the License is distributed on an \"AS IS\" BASIS,") + lines.append("# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.") + lines.append("# See the License for the specific language governing permissions and") + lines.append("# limitations under the License.") + lines.append("") + lines.append("import synthtool as s") + lines.append("from synthtool.languages import java") + lines.append("") + + if top_level_lines: + lines.extend(top_level_lines) + lines.append("") -for library in s.get_staging_dirs(): - # put any special-case replacements here - s.move(library) -{formatted_replacements} -s.remove_staging_dirs() -java.common_templates(monorepo=True, excludes=[ - {excludes_str} -]) -""" - return content + lines.append("for library in s.get_staging_dirs():") + lines.append(" # put any special-case replacements here") + lines.append(" s.move(library)") + for l in loop_body_lines: + # Indent loop body + for sl in l.split('\n'): + lines.append(" " + sl) + + lines.append("s.remove_staging_dirs()") + lines.append(f"java.common_templates(monorepo=True, excludes=[") + lines.append(f" {excludes_str}") + lines.append("])") + + return "\n".join(lines) + "\n" def main(): if len(sys.argv) < 3: @@ -135,21 +205,22 @@ def main(): with open(source_file, 'r') as f: source_code = f.read() - excludes, replacements = extract_info(source_code) + excludes, top_level_lines, loop_body_lines = extract_info(source_code) standard_excludes = None if template_file: if os.path.exists(template_file): with open(template_file, 'r') as f: template_code = f.read() - template_excludes, _ = extract_info(template_code) + template_excludes, _, _ = extract_info(template_code) standard_excludes = template_excludes else: - print(f"Template file {template_file} not found. using default excludes.") + print(f"Template file {template_file} not found using default excludes.") - target_content = generate_target_content(excludes, replacements, standard_excludes) + target_content = generate_target_content(excludes, top_level_lines, loop_body_lines, standard_excludes) - os.makedirs(os.path.dirname(target_file), exist_ok=True) + if os.path.dirname(target_file): + os.makedirs(os.path.dirname(target_file), exist_ok=True) with open(target_file, 'w') as f: f.write(target_content) From 0b1ec9df2ec94017118b51b84d731ce05974b4d3 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 21 Jan 2026 21:45:37 +0000 Subject: [PATCH 41/44] fix s.move --- monorepo-migration/update_owlbot.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/monorepo-migration/update_owlbot.py b/monorepo-migration/update_owlbot.py index a89ff5182d3f..be063ff685d7 100644 --- a/monorepo-migration/update_owlbot.py +++ b/monorepo-migration/update_owlbot.py @@ -111,10 +111,7 @@ def extract_info(source_code): child_inner = child if isinstance(child, ast.Expr): child_inner = child.value - - # Ignore s.move(library) - if is_call_to(child_inner, ['s', 'move']): - continue + # Check for nested common_templates (rare but possible) if is_call_to(child_inner, ['java', 'common_templates']) or is_call_to(child_inner, ['common', 'java_library']): excludes.extend(extract_excludes_from_call(child_inner)) @@ -176,7 +173,6 @@ def generate_target_content(excludes, top_level_lines, loop_body_lines, standard lines.append("for library in s.get_staging_dirs():") lines.append(" # put any special-case replacements here") - lines.append(" s.move(library)") for l in loop_body_lines: # Indent loop body for sl in l.split('\n'): From 1f03e0b7332b38d68c8dc84069fad783d7949866 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 21 Jan 2026 21:45:44 +0000 Subject: [PATCH 42/44] use BUILD_SUBDIR env variable --- monorepo-migration/transform_workflow.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/monorepo-migration/transform_workflow.py b/monorepo-migration/transform_workflow.py index f4175cf61c34..55b069950ad8 100644 --- a/monorepo-migration/transform_workflow.py +++ b/monorepo-migration/transform_workflow.py @@ -49,9 +49,8 @@ def transform(content, lib_name): if line.startswith('jobs:'): if not inserted_defaults: - new_lines.append("defaults:") - new_lines.append(" run:") - new_lines.append(f" working-directory: {lib_name}") + new_lines.append("env:") + new_lines.append(f" BUILD_SUBDIR: {lib_name}") inserted_defaults = True new_lines.append(line) new_lines.append(filter_job) From 50486c1d7740fa6530f8fa478a62b662997e0153 Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 21 Jan 2026 21:55:04 +0000 Subject: [PATCH 43/44] skip kokoro files --- monorepo-migration/migrate.sh | 16 +++------------- monorepo-migration/update_owlbot.py | 1 - 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/monorepo-migration/migrate.sh b/monorepo-migration/migrate.sh index 4b5d749cd5a5..bad39922663f 100755 --- a/monorepo-migration/migrate.sh +++ b/monorepo-migration/migrate.sh @@ -175,7 +175,8 @@ rm -f "$SOURCE_REPO_NAME/.gitignore" rm -f "$SOURCE_REPO_NAME/renovate.json" rm -f "$SOURCE_REPO_NAME/LICENSE" rm -f "$SOURCE_REPO_NAME/java.header" -rm -rf "$SOURCE_REPO_NAME/.kokoro/continuous" "$SOURCE_REPO_NAME/.kokoro/nightly" "$SOURCE_REPO_NAME/.kokoro/presubmit" +rm -rf "$SOURCE_REPO_NAME/.kokoro" +# rm -rf "$SOURCE_REPO_NAME/.kokoro/continuous" "$SOURCE_REPO_NAME/.kokoro/nightly" "$SOURCE_REPO_NAME/.kokoro/presubmit" rm -f "$SOURCE_REPO_NAME/codecov.yaml" rm -f "$SOURCE_REPO_NAME/synth.metadata" rm -f "$SOURCE_REPO_NAME/license-checks.xml" @@ -293,18 +294,7 @@ echo "Migrating owlbot.py..." if [ -f "$SOURCE_DIR/owlbot.py" ]; then TARGET_OWLBOT="$SOURCE_REPO_NAME/owlbot.py" - # Check for a template owlbot.py to source common excludes from - TEMPLATE_OWLBOT="" - if [ -f "java-workstations/owlbot.py" ]; then - TEMPLATE_OWLBOT="java-workstations/owlbot.py" - echo "Using $TEMPLATE_OWLBOT as template for excludes." - fi - - if [ -n "$TEMPLATE_OWLBOT" ]; then - python3 "$TRANSFORM_OWLBOT_SCRIPT" "$TARGET_OWLBOT" "$SOURCE_DIR/owlbot.py" "$TEMPLATE_OWLBOT" - else - python3 "$TRANSFORM_OWLBOT_SCRIPT" "$TARGET_OWLBOT" "$SOURCE_DIR/owlbot.py" - fi + python3 "$TRANSFORM_OWLBOT_SCRIPT" "$TARGET_OWLBOT" "$SOURCE_DIR/owlbot.py" echo "Committing owlbot.py migration..." git add "$TARGET_OWLBOT" diff --git a/monorepo-migration/update_owlbot.py b/monorepo-migration/update_owlbot.py index be063ff685d7..2b3d866d40f4 100644 --- a/monorepo-migration/update_owlbot.py +++ b/monorepo-migration/update_owlbot.py @@ -132,7 +132,6 @@ def generate_target_content(excludes, top_level_lines, loop_body_lines, standard if standard_excludes is None: standard_excludes = { ".github/*", - ".kokoro/*", "samples/*", "CODE_OF_CONDUCT.md", "CONTRIBUTING.md", From a7a73aa6652471dadbf0887900e63b1f84d6497e Mon Sep 17 00:00:00 2001 From: chingor13 Date: Wed, 21 Jan 2026 21:57:36 +0000 Subject: [PATCH 44/44] fix windows build file --- monorepo-migration/transform_workflow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monorepo-migration/transform_workflow.py b/monorepo-migration/transform_workflow.py index 55b069950ad8..fba791634ebe 100644 --- a/monorepo-migration/transform_workflow.py +++ b/monorepo-migration/transform_workflow.py @@ -82,11 +82,13 @@ def transform(content, lib_name): new_lines.append(line) new_lines.append(" - name: Support longpaths") new_lines.append(" run: git config --system core.longpaths true") - new_lines.append(" working-directory: .") continue if 'run: echo "SUREFIRE_JVM_OPT=' in line and '!java17' not in line: line = line.replace('" >> $GITHUB_ENV', ' -P !java17" >> $GITHUB_ENV') + if 'build.bat' in line: + line = line.replace('build.bat', 'build.sh') + new_lines.append(line) return "\n".join(new_lines)