GoogleCloudPlatform / artifact-registry-maven-tools

Apache License 2.0
24 stars 22 forks source link

Gradle plugin not working with dependencyResolutionManagement #71

Closed elizabeth-dev closed 2 months ago

elizabeth-dev commented 2 years ago

When using the Artifact Registry Gradle plugin on a project generated by Android Studio (restricting repository declarations to the settings.gradle via dependencyResolutionManagement), syncing fails with the error

Not a supported repository protocol 'artifactregistry': valid protocols are [file, http, https, gcs, s3, sftp]

Example settings.gradle

buildscript {
    repositories {
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.1.5"

        // https://github.com/GoogleCloudPlatform/artifact-registry-maven-tools/issues/63
        classpath('com.google.guava:guava'){
            version {
                strictly '30.1.1-jre'
            }
        }
    }
}

apply plugin: "com.google.cloud.artifactregistry.gradle-plugin"

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()

        maven {
            url "artifactregistry://europe-maven.pkg.dev/foo/bar"
        }
    }
}
rootProject.name = "Foo"
include ':app'
dasdranagon commented 2 years ago

Facing the same issue. In fact in order to be able to use "artifactregistry://" URI you need to apply the Gradle plugin, but looks like it will be applied later (so not in dependencyResolutionManagement section). As you can see in the sources plugin changes scheme to https and sets credential. As a workaround, you can do it manually. An ugly solution, but it works. Something like this should work:

// this is kotlin code
artifactURL = uri("artifactregistry://europe-maven.pkg.dev/foo/bar")
url = java.net.URI("https", artifactURL.host, artifactURL.path, artifactURL.fragment)
authentication {
    create<BasicAuthentication>("basic")
}
credentials {
    username = "_json_key_base64"
    password = <Securely saved artifact maven secret>
}
keith-miller commented 2 years ago

Same here, would be nice if this worked so we could start using Gradle's version catalogs.

gavenkoa commented 2 years ago

There is no reason to use special namespace artifactregistry:// and extra dependency to Gradle plugin when you have a small number of dependencies from GCP:

    apply plugin: 'java-library'
    apply plugin: 'maven-publish'

    String GCP_REPO_SNAPSHOT = "https://${GCP_REGION}-maven.pkg.dev/${GCP_PROJECT}/${GCP_REPO_SNAPSHOT}"
    String GCP_REPO_RELEASE = "https://${GCP_REGION}-maven.pkg.dev/${GCP_PROJECT}/${GCP_REPO_RELEASE}"
    String gcpRepoUrl = version.endsWith('SNAPSHOT') ? GCP_REPO_SNAPSHOT : GCP_REPO_RELEASE
    String GCP_REPO_SECRET = new File(project.rootDir, "maven-key.json").text

    repositories {
        maven {
            name "gcpSnapshot"
            url GCP_REPO_SNAPSHOT
            credentials {
                username '_json_key'
                password GCP_REPO_SECRET
            }
        }
        maven {
            name "gcpRelease"
            url GCP_REPO_RELEASE
            credentials {
                username '_json_key'
                password GCP_REPO_SECRET
            }
        }
    }

    publishing {
        publications {
            jar(MavenPublication) {
                from components.java
            }
        }
        repositories {
            maven {
                url gcpRepoUrl
                credentials {
                    username '_json_key'
                    password GCP_REPO_SECRET
                }
            }
        }
    }
gavenkoa commented 2 years ago

Seems there are 2 magical users:

The difference is only in the necessity to encode a json service secret in base64.

Such users are documented in Docker GCP registry docs: such tools don't need non-standard plugins, long live Basic Authentication!

lindsayismith commented 2 years ago

Thank you for adding that, gavenkoa.

The plugin is there only to attach authorization headers to each request, with an up-to-date token retrieved from the environment (e.g. gcloud auth, or the GCE service account).

If you want to use basic auth, then you can do:

username: oauth2accesstoken password: {A valid gcp oauth token}

You can get a token by doing gcloud auth print-access-token. But tokens only last an hour, so it's inconvenient to constantly update it.

If you want to use a service account, then as gavenkoa states, you can use:

username: _json_key password: {the sa json key}

or, more conveniently:

username: _json_key_base64 password: {the sa json key in base64}

If you use basic auth you don't need the wagon/plugin for authenticating to AR.

j4velin commented 1 year ago

So basically the workaround is to not use the plugin at all? Are there any plans to fix the plugin in the near future? How does this work when using workload identity provider?

fgsalomon commented 1 year ago

So basically the workaround is to not use the plugin at all? Are there any plans to fix the plugin in the near future?

I have a further question, does this mean that when working with a dependency that it's stored in a public repository we just need to change the protocol from artifactregistry to https and it should work?

kikoso commented 1 year ago

Hi folks. I opened https://github.com/GoogleCloudPlatform/artifact-registry-maven-tools/issues/90 before realizing this already existed. It would be great to have a fix for this.

kashan-pt commented 1 year ago

Any plans for fixing this issue? This is causing us issues on Android side

c3-hcflores commented 1 year ago

Please fix this issue, it is completely useless, HELP!

thiago-scherrer commented 1 year ago

I managed to work around the problem like this: https://github.com/GoogleCloudPlatform/artifact-registry-maven-tools/issues/66#issuecomment-1735802153

EdsonSchleiAcc commented 6 months ago

This is how I could use the catalog from Google maven repo inside the settings.gradle file:

buildscript {
    repositories {
        maven {
            url "https://plugins.gradle.org/m2/"              
        }
    }

    dependencies {
        classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1"
    }
}

apply plugin: "com.google.cloud.artifactregistry.gradle-plugin"

// get Gcloud user credentials from current  gcloud auth login
def userCred = com.google.cloud.artifactregistry.auth.DefaultCredentialProvider.getInstance().getCredential()

dependencyResolutionManagement {
    repositories {
        maven {
            url "https://europe-west1-maven.pkg.dev/my-gcp-prj/my-maven-repo"
            credentials {
                username 'oauth2accesstoken'
                password userCred.accessToken.tokenValue
            }
        }
    }

    versionCatalogs {
        libs {
            from("com.test:my-catalog:1.0.0")
        }
    }
}
xloger commented 4 months ago

I tried the solution suggested by EdsonSchleiAcc in the above comment, and it worked very well for me. However, in another project, I am using settings.gradle.kts and encountered a specific issue. Therefore, I made some modifications, and here is the updated code for everyone's reference:

val userCred = com.google.cloud.artifactregistry.auth.DefaultCredentialProvider.getInstance().makeGoogleCredentials()
EdsonSchleiAcc commented 4 months ago

I changed the code a few days after, so it can also work with a Service Account on github actions.

      // get Gcloud user credentials
      val userCred = com.google.cloud.artifactregistry.auth.DefaultCredentialProvider.getInstance().getCredential()
      val accessToken = (userCred as com.google.auth.oauth2.OAuth2Credentials).getAccessToken().getTokenValue()

      // and update the line credentials line with this:

        credentials {
            username = "oauth2accesstoken"
            password = accessToken.toString()
        }