← Back to team overview

launchpad-reviewers team mailing list archive

Re: [Merge] ~ruinedyourlife/launchpad-buildd:maven-virtual-remote into launchpad-buildd:master

 

Left a question below ^^

Diff comments:

> diff --git a/lpbuildd/target/build_craft.py b/lpbuildd/target/build_craft.py
> index 69106a8..524d60f 100644
> --- a/lpbuildd/target/build_craft.py
> +++ b/lpbuildd/target/build_craft.py
> @@ -196,157 +196,144 @@ class BuildCraft(
>                  name = key[len("CARGO_"):-len("_READ_AUTH")]
>                  # Extract token from user:token format
>                  token = value.split(":", 1)[1]
> -                cargo_vars[f"CARGO_REGISTRIES_{name}_TOKEN"] = f"Bearer {token}"
> +                cargo_vars[f"CARGO_REGISTRIES_{name}_TOKEN"] = (
> +                    f"Bearer {token}"
> +                )
>  
>          if cargo_vars:
>              # Tell Cargo to use token-based authentication from environment
> -            cargo_vars["CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS"] = "cargo:token"
> -        
> +            cargo_vars["CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS"] = (
> +                "cargo:token"
> +            )
> +
>          return cargo_vars
>  
>      def setup_maven_credentials(self):
> -        """Set up Maven credential files if needed.
> -        
> +        """Set up Maven credential files for Artifactory virtual repository.
> +
>          Creates a Maven settings.xml file with:
> -        1. Server configurations for authentication
> -           - One for release repository
> -           - One for snapshot repository
> -           - Uses credentials from MAVEN_*_READ_AUTH variables
> -        
> -        2. Profile configurations for each repository
> -           - Defines both release and snapshot repositories
> -           - Configures plugin repositories
> -           - Controls snapshot behavior
> -        
> -        3. Mirror configurations
> -           - Redirects all repository requests through Artifactory
> -           - Ensures dependencies are fetched through our proxy
> -        
> -        The resulting settings.xml allows Maven to:
> -        - Authenticate with private repositories
> -        - Use the correct URLs for artifacts
> -        - Handle both release and snapshot dependencies
> -        - Use plugin repositories with authentication
> -
> -        It is up to the user to activate the profile they want to use
> -        in the command line (this is done inside sourcecraft.yaml).
> +        1. Server configuration for authentication
> +        2. Profile configuration for the virtual repository
> +        3. Mirror configuration to redirect all requests through Artifactory
> +
> +        Note: Multi-tenant Maven setups are complex due to how mirrors work.
> +        Instead, we use a single Artifactory virtual repository that shadows
> +        Maven Central and other remote repositories as needed.
>          """
>          env_vars = dict(
> -            pair.split("=", maxsplit=1) 
> +            pair.split("=", maxsplit=1)
>              for pair in self.args.environment_variables
>          )
> -        
> +
>          # Extract Maven-specific variables
> -        maven_vars = {k: v for k, v in env_vars.items() if k.startswith("MAVEN_")}
> +        maven_vars = {
> +            k: v for k, v in env_vars.items() if k.startswith("MAVEN_")
> +        }
>          if not maven_vars:
> +            logger.info("No Maven environment variables found")
>              return
>  
>          # Create .m2 directory for Maven configuration
>          m2_dir = os.path.join(self.buildd_path, ".m2")
>          self.backend.run(["mkdir", "-p", m2_dir])
>  
> -        # Parse repository URLs and credentials from environment variables
> -        repositories = {}
> +        # Parse single repository URL and credentials from env vars
> +        repository = {}
>          for key, value in maven_vars.items():
>              if key.endswith("_URL"):
> -                repo_name = key[6:-4].lower()  # Remove MAVEN_ and _URL
> -                repositories.setdefault(repo_name, {})["url"] = value
> +                repository["url"] = value
>              elif key.endswith("_READ_AUTH"):
> -                repo_name = key[6:-10].lower()  # Remove MAVEN_ and _READ_AUTH
>                  user, token = value.split(":")
> -                repositories.setdefault(repo_name, {})["username"] = user
> -                repositories.setdefault(repo_name, {})["password"] = token
> -
> -        # Create settings.xml with server configurations
> -        settings_xml = """<?xml version="1.0" encoding="UTF-8"?>
> -<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0";
> -          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
> -          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd";>
> -    <servers>
> -"""
> -        # Add authentication for both release and snapshot repositories
> -        for name, repo in repositories.items():
> -            if "username" in repo and "password" in repo:
> -                settings_xml += f"""        <server>
> -            <id>{name}</id>
> -            <username>{repo['username']}</username>
> -            <password>{repo['password']}</password>
> -        </server>
> -        <server>
> -            <id>{name}-snapshots</id>
> -            <username>{repo['username']}</username>
> -            <password>{repo['password']}</password>
> -        </server>
> -"""
> -
> -        # Add profile configurations for repository definitions
> -        settings_xml += """    </servers>
> -    <profiles>
> -"""
> -        for name, repo in repositories.items():
> -            if "url" in repo:
> -                # Each profile contains both release and snapshot repositories
> -                settings_xml += f"""        <profile>
> -            <id>{name}</id>
> -            <repositories>
> -                <repository>
> -                    <id>{name}</id>
> -                    <name>{name}</name>
> -                    <url>{repo['url']}</url>
> -                    <snapshots>
> -                        <enabled>false</enabled>
> -                    </snapshots>
> -                </repository>
> -                <repository>
> -                    <id>{name}-snapshots</id>
> -                    <name>{name}</name>
> -                    <url>{repo['url']}</url>
> -                    <snapshots>
> -                        <enabled>true</enabled>
> -                    </snapshots>
> -                </repository>
> -            </repositories>
> -            <pluginRepositories>
> -                <pluginRepository>
> -                    <id>{name}</id>
> -                    <name>{name}</name>
> -                    <url>{repo['url']}</url>
> -                    <snapshots>
> -                        <enabled>false</enabled>
> -                    </snapshots>
> -                </pluginRepository>
> -                <pluginRepository>
> -                    <id>{name}-snapshots</id>
> -                    <name>{name}</name>
> -                    <url>{repo['url']}</url>
> -                    <snapshots>
> -                        <enabled>true</enabled>
> -                    </snapshots>
> -                </pluginRepository>
> -            </pluginRepositories>
> -        </profile>
> -"""
> -
> -        # Add mirror configurations to ensure all requests go through Artifactory
> -        settings_xml += """    </profiles>
> -    <mirrors>
> -"""
> -        for name, repo in repositories.items():
> -            if "url" in repo:
> -                settings_xml += f"""        <mirror>
> -            <id>{name}</id>
> -            <name>Maven Repository Manager running on {name}</name>
> -            <url>{repo['url']}</url>
> -            <mirrorOf>*</mirrorOf>
> -        </mirror>
> -"""
> -
> -        settings_xml += """    </mirrors>
> -</settings>
> -"""
> +                repository["username"] = user
> +                repository["password"] = token
> +
> +        # Get repository name from environment variable
> +        # (e.g., MAVEN_ARTIFACTORY_URL -> artifactory)
> +        repo_name = None
> +        for key in maven_vars.keys():

Do we need another loop over maven_vars.keys()? There is already another loop up above with items() that is also parsing some variables, can't we connect them? Or is it done due to a reason like readability etc?

> +            if key.endswith("_URL"):
> +                repo_name = key[6:-4].lower()  # Remove MAVEN_ and _URL
> +                break
> +
> +        if not repo_name or not repository.get("url"):
> +            return
> +
> +        # Generate Maven settings.xml
> +        settings_xml = self._generate_maven_settings_xml(repo_name, repository)
> +
>          with self.backend.open(os.path.join(m2_dir, "settings.xml"), "w") as f:
>              f.write(settings_xml)
>  
> +    def _generate_maven_settings_xml(self, repo_name, repository):
> +        """Generate Maven settings.xml for single virtual repository.
> +
> +        :param repo_name: Name/ID of the repository
> +        :param repository: Dict with repository configuration
> +        :return: XML content as string
> +        """
> +        # XML header
> +        header = (
> +            '<?xml version="1.0" encoding="UTF-8"?>\n'
> +            '<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"\n'
> +            '        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n'
> +        )
> +
> +        schema = (
> +            '        xsi:schemaLocation="'
> +            "http://maven.apache.org/SETTINGS/1.0.0 "
> +            'http://maven.apache.org/xsd/settings-1.0.0.xsd";>\n'
> +        )
> +
> +        # Servers section for authentication
> +        servers = "    <servers>\n"
> +        if "username" in repository and "password" in repository:
> +            servers += (
> +                "        <server>\n"
> +                f"            <id>{repo_name}</id>\n"
> +                f"            <username>{repository['username']}</username>\n"
> +                f"            <password>{repository['password']}</password>\n"
> +                "        </server>\n"
> +            )
> +        servers += "    </servers>\n"
> +
> +        # Profiles section with virtual repository
> +        profiles = "    <profiles>\n"
> +        profiles += (
> +            "        <profile>\n"
> +            f"            <id>{repo_name}</id>\n"
> +            "            <repositories>\n"
> +            "                <repository>\n"
> +            f"                    <id>{repo_name}</id>\n"
> +            f"                    <name>Artifactory Virtual Repo</name>\n"
> +            f"                    <url>{repository['url']}</url>\n"
> +            "                </repository>\n"
> +            "            </repositories>\n"
> +            "            <pluginRepositories>\n"
> +            "                <pluginRepository>\n"
> +            f"                    <id>{repo_name}</id>\n"
> +            f"                    <name>Artifactory Virtual Repo</name>\n"
> +            f"                    <url>{repository['url']}</url>\n"
> +            "                </pluginRepository>\n"
> +            "            </pluginRepositories>\n"
> +            "        </profile>\n"
> +        )
> +        profiles += "    </profiles>\n"
> +
> +        # Mirrors section - redirect all requests through Artifactory
> +        mirrors = (
> +            "    <mirrors>\n"
> +            "        <mirror>\n"
> +            f"            <id>{repo_name}</id>\n"
> +            f"            <name>Artifactory Virtual Repo</name>\n"
> +            f"            <url>{repository['url']}</url>\n"
> +            "            <mirrorOf>*</mirrorOf>\n"
> +            "        </mirror>\n"
> +            "    </mirrors>\n"
> +        )
> +
> +        # Combine all parts
> +        return header + schema + servers + profiles + mirrors + "</settings>"
> +
>      def build(self):
>          """Running build phase..."""
>          # Set up credential files before building


-- 
https://code.launchpad.net/~ruinedyourlife/launchpad-buildd/+git/launchpad-buildd/+merge/490002
Your team Launchpad code reviewers is requested to review the proposed merge of ~ruinedyourlife/launchpad-buildd:maven-virtual-remote into launchpad-buildd:master.



References