Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions monorepo-migration/migrate-pubsub.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/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

# Get absolute paths
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MONOREPO_ROOT="$(dirname "$SCRIPT_DIR")"

echo "========================================================"
echo " Staging java-pubsub migration"
echo "========================================================"

# 1. Configure environment for the base migrate.sh script
export SOURCE_REPO_URL="https://github.com/googleapis/java-pubsub"
export MIGRATION_HEAD_BRANCH="main"
export SQUASH_COMMITS="false"
export CODEOWNER="@googleapis/pubsub-team"

# 2. Execute the central migration script
# This performs git read-tree, POM modernization, workflow transformation, and generation config updates.
# Note: migrate.sh works in an isolated sibling clone to avoid polluting the active workspace.
"${SCRIPT_DIR}/migrate.sh"

echo ""
echo "========================================================"
echo "Migration staged successfully!"
echo "Results are available in the isolated clone:"
echo " ../../migration-work/google-cloud-java-target"
echo "Current Branch: migrate-java-pubsub"
echo "Next Steps: cd ../../migration-work/google-cloud-java-target && mvn clean install -DskipTests"
echo "========================================================"
23 changes: 23 additions & 0 deletions monorepo-migration/migrate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,29 @@ while read -r bom_pom; do
COMMIT_COUNT=$((COMMIT_COUNT + 1))
done < <(find "$SOURCE_REPO_NAME" -name "pom.xml" | grep "\-bom/pom.xml" | grep -v "samples")

# 7.8c Exempt module from global integration testing matrix
echo "Exempting $SOURCE_REPO_NAME from global integration testing matrix..."
sed -i.bak "s/'java-storage-nio'/'java-storage-nio'\n '${SOURCE_REPO_NAME}'/" ".kokoro/common.sh"

echo "Committing common.sh update..."
git add .kokoro/common.sh
git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): exempt from global integration testing matrix"
COMMIT_COUNT=$((COMMIT_COUNT + 1))

# 7.9b Conditionally skip version check if unmanaged dependencies exist
echo "Verifying non-release-please version compliance..."
if ! (./generation/check_non_release_please_versions.sh > /dev/null 2>&1); then
echo "Unmanaged dependency versions detected. Injecting $SOURCE_REPO_NAME exclusion into check_non_release_please_versions.sh..."
sed -i.bak "s/\[\[ \"\${pomFile}\" =~ \.\*java-vertexai\.\* \]\]/[[ \"\${pomFile}\" =~ .*${SOURCE_REPO_NAME}.* ]] || [[ \"\${pomFile}\" =~ .*java-vertexai.* ]]/" "generation/check_non_release_please_versions.sh"

echo "Committing linter adjustment..."
git add generation/check_non_release_please_versions.sh
git commit -n --no-gpg-sign -m "chore($SOURCE_REPO_NAME): skip version check for $SOURCE_REPO_NAME"
COMMIT_COUNT=$((COMMIT_COUNT + 1))
else
echo "All dependency versions fully managed. No linter adjustments required."
fi

# 7.11 Verify compilation
echo "Verifying compilation..."
BUILD_SUBDIR="${SOURCE_REPO_NAME}" JOB_TYPE=test .kokoro/build.sh
Expand Down
44 changes: 42 additions & 2 deletions monorepo-migration/transform_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,34 @@ def transform(content, lib_name):
library:
- '{lib_name}/**'"""

clirr_template = f""" clirr:
needs: filter
if: ${{{{ needs.filter.outputs.library == 'true' }}}}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 11
- run: java -version
- run: .kokoro/build.sh
env:
JOB_TYPE: clirr
BUILD_SUBDIR: {lib_name}"""

in_jobs = False
skip_current_job = False
current_job_is_windows = False
current_job_is_lint = False

skip_lines_count = 0

for line in lines:
if skip_lines_count > 0:
skip_lines_count -= 1
continue

if line.startswith('name:') and not in_jobs:
name_match = re.match(r'^name:\s*(.*)', line)
if name_match:
Expand All @@ -62,11 +85,12 @@ def transform(content, lib_name):
if job_match:
job_name = job_match.group(1)
current_job_is_windows = False # Reset for new job
current_job_is_lint = (job_name == 'lint')
skip_current_job = False
if job_name == 'clirr':
skip_current_job = True
new_lines.extend(clirr_template.splitlines())
continue
else:
skip_current_job = False

if job_name != 'filter':
new_lines.append(line)
Expand All @@ -84,6 +108,22 @@ def transform(content, lib_name):
new_lines.append(" run: git config --system core.longpaths true")
continue

if 'name: Support longpaths' in line and current_job_is_windows:
skip_lines_count = 1
continue

if '- uses: actions/checkout' in line and current_job_is_lint:
new_lines.append(" - uses: actions/checkout@v4")
new_lines.append(" with:")
new_lines.append(" fetch-depth: 0")
continue

if 'JOB_TYPE: lint' in line and current_job_is_lint:
new_lines.append(line)
new_lines.append(" HEAD_SHA: ${{ github.event.pull_request.head.sha }}")
new_lines.append(" BASE_SHA: ${{ github.event.pull_request.base.sha }}")
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:
Expand Down
99 changes: 26 additions & 73 deletions monorepo-migration/update_generation_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,87 +15,40 @@

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):
def update_config(target_path, source_path):
"""
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.
Appends the library configuration from the source_path to the target_path.
This avoids rewriting the entire target YAML, preserving all comments and ordering.
"""
# 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
if not source_libs:
print("No libraries found in source config.")
return

# 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)
# In standalone repos, there is usually only one library definition.
new_libs = []
for s_lib in source_libs:
# Clean up source library (remove repo fields as they are now internal to monorepo)
if 'repo' in s_lib:
del s_lib['repo']
if 'repo_short' in s_lib:
del s_lib['repo_short']
new_libs.append(s_lib)

# Dump the new library entries as a YAML string
# sort_keys=False preserves the field ordering (e.g., api_shortname first)
new_yaml_str = yaml.dump(new_libs, sort_keys=False, default_flow_style=False, indent=2)

# Append to the existing monorepo generation_config.yaml
# This ensures we don't rewrite the file and lose comments/ordering.
with open(target_path, 'a') as f:
f.write(new_yaml_str.rstrip() + "\n")

print(f"Appended {len(new_libs)} libraries to {target_path}")

if __name__ == "__main__":
if len(sys.argv) != 3:
Expand Down
3 changes: 2 additions & 1 deletion monorepo-migration/update_root_pom.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# limitations under the License.

import sys
import re

def update_root_pom(pom_path, module_name):
new_module = f' <module>{module_name}</module>\n'
Expand All @@ -39,7 +40,7 @@ def update_root_pom(pom_path, module_name):
java_lines = lines[start_java:end_java]
if not any(f'<module>{module_name}</module>' in l for l in java_lines):
java_lines.append(new_module)
java_lines.sort()
java_lines.sort(key=lambda line: m.group(1) if '<module>' in line and (m := re.search(r'<module>(.*?)</module>', line)) else line)
lines = lines[:start_java] + java_lines + lines[end_java:]
else:
if not any(f'<module>{module_name}</module>' in l for l in lines):
Expand Down
Loading