From 5e3050696e78d84ee44fe1f83e964a2f6ddc15fb Mon Sep 17 00:00:00 2001 From: Mike Eltsufin Date: Mon, 22 Dec 2025 15:52:48 -0500 Subject: [PATCH 01/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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/27] 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