diff --git a/scripts/setup-new-module b/scripts/setup-new-module
new file mode 100755
index 000000000000..fff112a75ce9
--- /dev/null
+++ b/scripts/setup-new-module
@@ -0,0 +1,406 @@
+#!/usr/bin/env python
+
+"""
+setup-new-module - Script to set up a new module in the AWS SDK for Java v2
+Usage: ./scripts/setup-new-module -n module-name [-t] [-p parent-dir]
+Options:
+ -n module-name: Name of the new module (required)
+ -t: Set up as a test module (optional, default: false)
+ -p parent-dir: Parent directory for the module (optional, default: root project directory for regular modules, test directory for test modules)
+ -h: Show help
+"""
+
+import os
+import sys
+import argparse
+import re
+import shutil
+from pathlib import Path
+
+
+def parse_arguments():
+ """Parse command line arguments."""
+ parser = argparse.ArgumentParser(description='Set up a new module in the AWS SDK for Java v2')
+ parser.add_argument('-n', '--name', required=True, help='Name of the new module (required)')
+ parser.add_argument('-t', '--test', action='store_true', help='Set up as a test module (optional, default: false)')
+ parser.add_argument('-p', '--parent-dir', help='Parent directory for the module (optional)')
+ return parser.parse_args()
+
+
+def get_sdk_version(root_dir):
+ """Get the current SDK version from the root pom.xml."""
+ pom_path = os.path.join(root_dir, 'pom.xml')
+ with open(pom_path, 'r') as f:
+ content = f.read()
+
+ # Find the first version tag
+ match = re.search(r'([^<]+)', content)
+ if match:
+ return match.group(1)
+ else:
+ print("Warning: Could not find SDK version in pom.xml")
+ return "UNKNOWN"
+
+
+def create_module_directory(module_dir):
+ """Create the module directory structure."""
+ print(f"Creating module directory: {module_dir}")
+ os.makedirs(module_dir, exist_ok=True)
+ os.makedirs(os.path.join(module_dir, 'src/main/java'), exist_ok=True)
+ os.makedirs(os.path.join(module_dir, 'src/main/resources'), exist_ok=True)
+ os.makedirs(os.path.join(module_dir, 'src/test/java'), exist_ok=True)
+ os.makedirs(os.path.join(module_dir, 'src/test/resources'), exist_ok=True)
+
+
+def create_pom_xml(module_dir, module_name, sdk_version, is_test_module):
+ """Create a basic pom.xml file for the module."""
+ pom_path = os.path.join(module_dir, 'pom.xml')
+
+ print(f"Using SDK version: {sdk_version}")
+
+ with open(pom_path, 'w') as f:
+ f.write(f'''
+
+ 4.0.0
+
+ software.amazon.awssdk
+ aws-sdk-java-pom
+ {sdk_version}
+
+
+ {module_name}
+ AWS Java SDK :: {module_name}
+ AWS SDK for Java - {module_name}
+ https://aws.amazon.com/sdkforjava
+''')
+
+ # Add Automatic-Module-Name for non-test modules
+ if not is_test_module:
+ f.write('''
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+
+ software.amazon.awssdk.TODO
+
+
+
+
+
+
+''')
+
+ # Close pom.xml
+ f.write('''
+
+
+
+
+
+''')
+
+ print(f"Created pom.xml at {pom_path}")
+def add_dependency_to_pom(pom_file, module_name):
+ """Add dependency to a pom.xml file."""
+ # Check if the file exists
+ if not os.path.isfile(pom_file):
+ print(f"Warning: {pom_file} does not exist. Skipping.")
+ return
+
+ # Read the file content
+ with open(pom_file, 'r') as f:
+ content = f.read()
+
+ # Check if dependency already exists
+ dependency_pattern = f"{module_name}"
+ if dependency_pattern in content:
+ print(f"Dependency already exists in {pom_file}. Skipping.")
+ return
+
+ # Find the dependencies section and add the new dependency
+ dependencies_pattern = r""
+ new_dependency = f'''
+ software.amazon.awssdk
+ {module_name}
+ ${{awsjavasdk.version}}
+
+'''
+
+ # Insert the new dependency after the dependencies tag
+ modified_content = re.sub(
+ dependencies_pattern,
+ f"{dependencies_pattern}\n{new_dependency}",
+ content
+ )
+
+ # Write the modified content back to the file
+ with open(pom_file, 'w') as f:
+ f.write(modified_content)
+
+ print(f"Added dependency to {pom_file}")
+
+
+def update_root_pom(root_dir, module_name, is_test_module):
+ """Update the root pom.xml to include the new module."""
+ root_pom = os.path.join(root_dir, 'pom.xml')
+
+ # Check if the file exists
+ if not os.path.isfile(root_pom):
+ print(f"Warning: {root_pom} does not exist. Skipping.")
+ return
+
+ # Determine the module path based on whether it's a test module
+ module_path = module_name
+ if is_test_module:
+ module_path = f"test/{module_name}"
+
+ # Read the file content
+ with open(root_pom, 'r') as f:
+ content = f.read()
+
+ # Check if module already exists
+ module_pattern = f"{module_path}"
+ if module_pattern in content:
+ print("Module already exists in root pom.xml. Skipping.")
+ return
+
+ # Find the modules section and add the new module
+ modules_pattern = r""
+ new_module = f" {module_path}"
+
+ # Insert the new module after the modules tag
+ modified_content = re.sub(
+ modules_pattern,
+ f"{modules_pattern}\n{new_module}",
+ content
+ )
+
+ # Write the modified content back to the file
+ with open(root_pom, 'w') as f:
+ f.write(modified_content)
+
+ print("Added module to root pom.xml")
+def update_japicmp_config(root_dir, module_name):
+ """Update japicmp plugin config in root pom.xml."""
+ root_pom = os.path.join(root_dir, 'pom.xml')
+
+ # Check if the file exists
+ if not os.path.isfile(root_pom):
+ print(f"Warning: {root_pom} does not exist. Skipping japicmp update.")
+ return
+
+ # Read the file content
+ with open(root_pom, 'r') as f:
+ content = f.read()
+
+ # Check if module already exists in japicmp config
+ include_module_pattern = f" {module_name}"
+ if include_module_pattern in content:
+ print("Module already exists in japicmp config. Skipping.")
+ return
+
+ # Find the includeModules section and add the new module
+ include_modules_end_pattern = r""
+ new_include_module = f"{module_name}\n"
+
+ # Insert the new module before the includeModules end tag
+ modified_content = re.sub(
+ include_modules_end_pattern,
+ f"{new_include_module} {include_modules_end_pattern}",
+ content
+ )
+
+ # Write the modified content back to the file
+ with open(root_pom, 'w') as f:
+ f.write(modified_content)
+
+ print(f"Added {module_name} to japicmp plugin configuration in {root_pom}")
+
+
+def update_brazil_json(root_dir, module_name, is_test):
+ """Update .brazil.json file."""
+ brazil_json = os.path.join(root_dir, '.brazil.json')
+
+ # Check if the file exists
+ if not os.path.isfile(brazil_json):
+ print(f"Warning: {brazil_json} does not exist. Skipping.")
+ return
+
+ # Read the file content
+ with open(brazil_json, 'r') as f:
+ content = f.read()
+
+ # Check if module already exists in .brazil.json
+ module_pattern = f'"{module_name}":'
+ if module_pattern in content:
+ print("Module already exists in .brazil.json. Skipping.")
+ return
+
+ if is_test:
+ # Find a specific test module entry to anchor our insertion
+ anchor_pattern = r'"s3-tests": {"skipImport": true}'
+ new_module_entry = f'"{module_name}": {{ "skipImport": true }},\n'
+
+ # Insert the new module before the anchor
+ modified_content = re.sub(
+ anchor_pattern,
+ f"{new_module_entry} {anchor_pattern}",
+ content
+ )
+
+ print(f"Added {module_name} to .brazil.json with skipImport: true")
+ else:
+ # Find a specific non-test module entry to anchor our insertion
+ anchor_pattern = r'"annotations": { "packageName": '
+ new_module_entry = f'"{module_name}": {{ "packageName": "TODO" }},\n'
+
+ # Insert the new module before the anchor
+ modified_content = re.sub(
+ anchor_pattern,
+ f"{new_module_entry} {anchor_pattern}",
+ content
+ )
+
+ print(f"Added {module_name} to .brazil.json with packageName: TODO")
+
+ # Write the modified content back to the file
+ with open(brazil_json, 'w') as f:
+ f.write(modified_content)
+def update_buildspecs(root_dir, module_name):
+ """Update buildspec files for test modules."""
+ release_maven = os.path.join(root_dir, 'buildspecs/release-to-maven.yml')
+ release_javadoc = os.path.join(root_dir, 'buildspecs/release-javadoc.yml')
+
+ # Update release-to-maven.yml
+ if os.path.isfile(release_maven):
+ with open(release_maven, 'r') as f:
+ content = f.read()
+
+ # Look for MODULES_TO_SKIP variable
+ modules_pattern = r'MODULES_TO_SKIP="([^"]*)"'
+ match = re.search(modules_pattern, content)
+
+ if match:
+ current_modules = match.group(1)
+ new_modules = f"{current_modules},{module_name}" if current_modules else module_name
+
+ # Update the file
+ modified_content = re.sub(
+ modules_pattern,
+ f'MODULES_TO_SKIP="{new_modules}"',
+ content
+ )
+
+ with open(release_maven, 'w') as f:
+ f.write(modified_content)
+
+ print(f"Updated MODULES_TO_SKIP in {release_maven} to include {module_name}")
+ else:
+ print(f"MODULES_TO_SKIP variable not found in {release_maven}. Please manually update.")
+ else:
+ print(f"Warning: {release_maven} does not exist. Skipping.")
+
+ # Update release-javadoc.yml
+ if os.path.isfile(release_javadoc):
+ with open(release_javadoc, 'r') as f:
+ content = f.read()
+
+ # Look for MODULES_TO_SKIP variable
+ modules_pattern = r'MODULES_TO_SKIP="([^"]*)"'
+ match = re.search(modules_pattern, content)
+
+ if match:
+ current_modules = match.group(1)
+ new_modules = f"{current_modules},{module_name}" if current_modules else module_name
+
+ # Update the file
+ modified_content = re.sub(
+ modules_pattern,
+ f'MODULES_TO_SKIP="{new_modules}"',
+ content
+ )
+
+ with open(release_javadoc, 'w') as f:
+ f.write(modified_content)
+
+ print(f"Updated MODULES_TO_SKIP in {release_javadoc} to include {module_name}")
+ else:
+ print(f"MODULES_TO_SKIP variable not found in {release_javadoc}. Please manually update.")
+ else:
+ print(f"Warning: {release_javadoc} does not exist. Skipping.")
+def main():
+ """Main function to set up a new module."""
+ args = parse_arguments()
+
+ # Get the root project directory
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ root_dir = os.path.dirname(script_dir)
+
+ # Set default parent directory based on module type
+ parent_dir = args.parent_dir
+ if not parent_dir:
+ if args.test:
+ parent_dir = os.path.join(root_dir, 'test')
+ print(f"Setting default parent directory for test module to: {parent_dir}")
+ else:
+ parent_dir = root_dir
+
+ # Create module directory
+ module_dir = os.path.join(parent_dir, args.name)
+ create_module_directory(module_dir)
+
+ # Get SDK version
+ sdk_version = get_sdk_version(root_dir)
+
+ # Create pom.xml
+ create_pom_xml(module_dir, args.name, sdk_version, args.test)
+
+ # Perform updates based on module type
+ if not args.test:
+ print("Performing non-test module updates...")
+
+ # Add to tests-coverage-reporting pom.xml
+ add_dependency_to_pom(os.path.join(root_dir, 'test/tests-coverage-reporting/pom.xml'), args.name)
+
+ # Add to aws-sdk-java pom.xml
+ add_dependency_to_pom(os.path.join(root_dir, 'aws-sdk-java/pom.xml'), args.name)
+
+ # Add to architecture-tests pom.xml
+ add_dependency_to_pom(os.path.join(root_dir, 'test/architecture-tests/pom.xml'), args.name)
+
+ # Add to bom pom.xml
+ add_dependency_to_pom(os.path.join(root_dir, 'bom/pom.xml'), args.name)
+
+ # Update japicmp plugin config
+ update_japicmp_config(root_dir, args.name)
+
+ # Update .brazil.json
+ update_brazil_json(root_dir, args.name, False)
+
+ # Update root pom.xml
+ update_root_pom(root_dir, args.name, False)
+ else:
+ print("Performing test module updates...")
+
+ # Update buildspecs
+ update_buildspecs(root_dir, args.name)
+
+ # Update .brazil.json
+ update_brazil_json(root_dir, args.name, True)
+
+ # Update root pom.xml
+ update_root_pom(root_dir, args.name, True)
+
+ print("")
+ print("Module setup complete! Please review the changes.")
+
+
+if __name__ == "__main__":
+ main()