Publish Snapshot to oss.jfrog.org with multiple modules fails with 403 - gradle

I'm trying to publish a project with multiple modules to artifactory (oss.jfrog.org). When I run artifactoryPublish I get a 403 error but I know it's not a permissions issue because it works with a single module. It only fails trying publish multiple modules.
Some modules are aars and others are jars and all include sources. I can publish them all to Bintray, but can't publish to artifactory (for snapshots).
So the question is, how do I configure a multi-module project to publish snapshots to oss.jfrog.org.
I've figured out that if I change it to publish a single module and make the artifact name the same as the last part of the group, it works, but a different name doesn't work (gives 403 error).
So if group is com.example.foo I can publish foo.jar (com/example/foo/foo/1.0.0-SNAPSHOT/foo-1.0.0.jar). But I can't publish bar.jar (com/example/foo/bar/1.0.0-SNAPSHOT/bar.jar).
This gradle is included in every project's build.gradle
afterEvaluate {
publishing {
publications {
mavenPublication(MavenPublication) {
artifact sourcesJar
if (project.plugins.hasPlugin("com.android.library")) {
artifact("$buildDir/outputs/aar/${project.name}-debug.aar")
} else {
artifact("$buildDir/libs/${project.name}-${version}.jar")
}
groupId "com.example.foo"
artifactId project.name // changing this to "foo" works for a single project
version version
pom {
name.set(project.name)
url.set(POM_URL)
packaging POM_PACKAGING
version version
licenses {
license {
name.set(POM_LICENSE_NAME)
url.set(POM_LICENSE_URL)
}
}
developers {
developer {
name.set(POM_DEVELOPER)
}
}
scm {
url.set(POM_SCM_URL)
connection.set(POM_SCM_CONNECTION)
developerConnection.set(POM_SCM_DEV_CONNECTION)
}
}
}
}
}
bintray {
user = project.findProperty('bintrayUser') ?: System.getenv('BINTRAY_USER')
key = project.findProperty('bintrayApiKey') ?: System.getenv('BINTRAY_API_KEY')
configurations = ['archives']
publish = true
dryRun = true
pkg {
name = project.name
repo = BINTRAY_REPO
userOrg = BINTRAY_ORG
licenses = [POM_LICENSE_NAME]
vcsUrl = POM_SCM_URL
version {
name = project.name
released = new Date()
}
}
}
artifactory {
contextUrl = 'http://oss.jfrog.org'
publish {
repository {
repoKey = 'oss-snapshot-local'
username = project.findProperty('bintrayUser') ?: System.getenv('BINTRAY_USER')
password = project.findProperty('bintrayApiKey') ?: System.getenv('BINTRAY_API_KEY')
}
defaults {
publications('mavenPublication')
publishArtifacts = true
publishPom = true
}
}
resolve {
repoKey = 'jcenter'
}
}
}

Artifactory returns a 403 whenever you're trying to publish an artefact that already exists. In your case, if you've previously published snapshot artefacts from your multi module build, whenever you will try doing that again, you will get a 403. I know you can configure the user access to provide delete permissions to the account you are using to deploy, as indicated here. However, overwriting history is not considered a good practice.
Regarding renaming your groups and artefacts, I don't think that will provide a solution, as it's not your GAV coordinates that are the issue, but rather the fact that artefacts with matching GAV already exist.
If I may ask, why do you want to use SNAPSHOT artefacts? Can you not achieve the same behaviour with dynamic dependencies and dependency locking?

Related

Unable to publish jar to Gitlab package registry with gradle

I am trying to publish some jar artefacts on gitlab package registry but I get this error from the server :
Received status code 415 from server: Unsupported Media Type
Here is the publishing section of my build.gradle.kts :
publishing {
publications {
create<MavenPublication>("maven"){
artifact(tasks["bootJar"])
}
}
repositories {
maven {
url = uri("https://gitlab.com/api/v4/groups/my-group/-/packages/maven")
name = "Gitlab"
credentials(HttpHeaderCredentials::class) {
name = "Token"
value = System.getenv("CI_JOB_TOKEN")
}
authentication {
create<HttpHeaderAuthentication>("header")
}
}
}
}
In my gitlab-ci, I added a task for publish the artefacts :
deploy:
stage: deploy
script: gradle publish
only:
- master
Any help would be appreciated
Quick answer
Replace your publishing url pointing to the group-scope with the one pointing to the specific-package-repository, e.g. on gitlab.com:
https://gitlab.com/api/v4/projects/<your-project-id>/packages/maven
You need to replace <your-project-id> with your specific project-id of course.
Related to this a quote from docs.gitlab:
Note: In all cases, you need a project specific URL for uploading a package in the distributionManagement section.
Or in other words: Only the general repositories section can use your groups-url for searching other already published artifacts! (I also had to understand that). So:
you cannot publish to the group-package-store on gitlab, you can just search there.
Publication goes always to the project-specific package-store, which will then be visible at group-scope too.
Example gradle config (kotlin-dsl)
repositories {
mavenCenter()
jcenter()
// Here you USE the group api/v4 url for SEARCHING packages
maven {
name = "GitLab"
url = uri("https://gitlab.com/api/v4/groups/my-group/-/packages/maven")
credentials(HttpHeaderCredentials::class) {
name = "Job-Token"
value = System.getenv("CI_JOB_TOKEN")
}
authentication {
create<HttpHeaderAuthentication>("header")
}
}
}
publishing {
publications {
create<MavenPublication>("maven"){
artifact(tasks["bootJar"])
}
}
repositories {
maven {
// here your PROVIDE the PROJECT-URI for publishing your package
// in the project-specific package-space which is also visible at
// the group scope above
url = uri("https://gitlab.com/api/v4/projects/<your-project-id>/packages/maven")
name = "Gitlab"
credentials(HttpHeaderCredentials::class) {
name = "Job-Token"
value = System.getenv("CI_JOB_TOKEN")
}
authentication {
create<HttpHeaderAuthentication>("header")
}
}
}
}
More Info
There are multiple scenarios on how you may interact with the maven-repository-space on GitLab. The three switches are:
The place where you want to look for existing published packages
project-scope (https://.../api/v4/projects/<project-id>/packages/maven)
group-scope (https://.../api/v4/groups/<group-id>/-/packages/maven)
instance-scope (https://.../api/v4/packages/maven)
The authorization-method you want to use
PERSONAL_ACCESS_TOKEN
DEPLOY_TOKEN
CI_JOB_TOKEN
The place where your want to publish your package
this must always be a specific project-url (https://.../api/v4/projects/<project-id>/packages/maven)
I think the most important thing is to make sure you've enabled archives in your project:
Go to Project Settings
Expand Permissions
Switch on "Packages"
Apparently, there are also other reasons for the response status 415 Unsupported Media Type:
I ran into the same error message while trying to publish to the project repository. The error that I made was to use the URL-encoded path of the project instead of the project ID in the repository URL.
From the Gitlab documentation (emphasis added):
For retrieving artifacts, use either the URL-encoded path of the project
(like group%2Fproject) or the project's ID (like 42). However, only the
project's ID can be used for publishing.
This is the build.gradle.kts configuration that worked for me on my self-hosted Gitlab instance for publishing a Spring Boot fat JAR:
plugins {
/* ... other stuff ... */
`java-library`
`maven-publish`
}
publishing {
publications {
create<MavenPublication>("bootJava") {
artifact(tasks.getByName("bootJar"))
}
}
repositories {
maven {
val projectId = System.getenv("CI_PROJECT_ID")
name = "Project Name"
url = uri("https://gitlab.example.com/api/v4/projects/${projectId}/packages/maven")
credentials(HttpHeaderCredentials::class) {
name = "Job-Token"
value = System.getenv("CI_JOB_TOKEN")
}
authentication {
create<HttpHeaderAuthentication>("header")
}
}
}
}

Publishing Maven artifact to artifactory with different artifact ID

New to Maven Publishing. Our team has started using Artifactory and I'm trying to figure out how to publish to it, but using a custom artifact ID. Here's the relevant part of my build.gradle file
def applicationName = 'example-api'
def applicationVersion = '1.0.0.1'
def group = 'com.example.api'
def archiveName = "${applicationName}##${applicationVersion}"
bootWar {
archiveFileName = "${archiveName}.war"
}
publishing {
publications {
mavenJava(MavenPublication) {
artifact(file("build/libs/${archiveName}.war"))
afterEvaluate {
artifactId archiveName
groupId group
version applicationVersion
}
}
}
}
artifactory {
contextUrl = "http://172.17.0.2:32447/artifactory"
publish {
repository {
repoKey = 'libs-release-local'
username = 'admin'
password = 'password1'
maven = true
}
defaults {
publications('mavenJava')
publishBuildInfo = true
publishArtifacts = true
publishPom = false
}
}
clientConfig.info.setBuildName(applicationName)
clientConfig.info.setBuildNumber(applicationVersion)
}
But it seems it's never keeps the same name as the WAR file that's produced in the build. This is the closest I've gotten:
Ideally, I'd like to have the artifact be called just 'example-api##1.0.0.1.war' since it will be deployed to Tomcat.
When I try to remove groupId, Artfactory seems unable to create a repo URL, and if I omit the the version, the artifact name is then example-api##1.0.0.1-unspecified.war
Any thoughts?
What you see is the expected behaviour.
Maven artifact names are strictly <artifactId>-<version>.<extension>.
So when you set the publication configuration as follows:
artifactId = example-api##1.0.0.1
version = 1.0.0.1
extension = war
It results in an artifact named example-api##1.0.0.1-1.0.0.1.war
I would strongly recommend dropping the version from the artifactId part, to be better aligned with Maven conventions.
An alternative would be to use an Ivy repository, which has more flexibility around specifying the artifact name.

Confused about process to publish snapshots to BinTray

I want to investigate publishing Hibernate ORM jars to Bintray. However one requirement we have is to be able to publish snapshots, which I see Bintray now supports through this OJO repository. However, I am quite confused about how this is supposed to work after reading the documentation.
First, the documentation mentions that I should be able to request publishing to JCenter and at the same time be able to request to be able to publish snapshots. However, I see no such options: https://bintray.com/hibernate/artifacts/hibernate-orm
Secondly, after I get the account on OJO set up, what, if anything, do I need to do special with the Bintray/Gradle plugin?
After many trial and errors I've ended up with the following setup.
We use 2 different plugins for publishing:
snapshots and release publishing (using com.jfrog.artifactory) and
bintray-related activities (using com.jfrog.bintray) in gradle (in the project p6spy).
Relevant parts from the build.gradle file follow, please note the specifics of the project :
plugins {
...
// to publish !SNAPSHOTs to bintray and sync it to maven-central
// ./gradlew bintrayUpload
id 'com.jfrog.bintray' version '1.7.3'
// to publish SNAPSHOTs and !SNAPSHOTs to oss.jfrog.org
// ./gradlew artifactoryPublish
id 'com.jfrog.artifactory' version '4.5.2'
}
publishing {
publications {
maven(MavenPublication) {
from components.java
groupId project.group
artifactId project.archivesBaseName
version project.version
...
pom {
packaging 'jar'
withXml {
asNode().children().last() + {
def builder = delegate
// maven central publishing mandatories
builder.name project.name
builder.description description
builder.url 'https://github.com/p6spy/p6spy'
builder.licenses {
builder.license {
builder.name 'The Apache Software License, Version 2.0'
builder.url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
builder.distribution 'repo'
}
}
builder.scm {
builder.url 'http://github.com/p6spy/p6spy'
builder.connection 'scm:git:git://github.com/p6spy/p6spy.git'
builder.developerConnection 'scm:git:ssh://github.com:p6spy/p6spy.git'
}
builder.developers {
builder.developer {
builder.name 'Quinton McCombs'
builder.email 'quinton.mccombs#gmail.com'
}
builder.developer {
builder.name 'Peter Butkovic'
builder.email 'butkovic#gmail.com'
}
builder.developer {
builder.name 'Felix Barnsteiner'
builder.email 'felix.barnsteiner#isys-software.de'
}
}
// maven central publishing optionals
builder.issueManagement {
builder.system 'github'
builder.url 'https://github.com/p6spy/p6spy/issues'
}
builder.ciManagement {
builder.system 'Travis CI'
builder.url 'https://travis-ci.org/p6spy/p6spy'
}
}
}
}
}
}
}
// to publish SNAPSHOTs to http://oss.jfrog.org/oss-snapshot-local/
// and !SNAPSHOTs to http://oss.jfrog.org/oss-release-local/
artifactory {
contextUrl = 'https://oss.jfrog.org'
resolve {
repository {
repoKey = 'libs-release'
}
}
publish {
repository {
// The Artifactory repository key to publish to
// when using oss.jfrog.org the credentials are from Bintray.
if (project.version.endsWith("-SNAPSHOT")) {
repoKey = 'oss-snapshot-local'
} else {
repoKey = 'oss-release-local'
}
username = System.getenv('BINTRAY_USER')
password = System.getenv('BINTRAY_API_KEY')
}
defaults {
publications 'maven'
properties = [ 'bintray.repo': 'p6spy/maven', 'bintray.package': 'p6spy:p6spy', 'bintray.version': project.version.toString() ]
}
}
}
// to publish to bintray and later sync to maven-central
bintray {
user = System.getenv('BINTRAY_USER')
key = System.getenv('BINTRAY_API_KEY')
publications = ['maven']
// dryRun = true
// publish = true
pkg {
repo = 'maven'
name = 'p6spy:p6spy'
userOrg = group
desc = description
websiteUrl = 'https://github.com/p6spy/p6spy'
issueTrackerUrl = 'https://github.com/p6spy/p6spy/issues'
vcsUrl = 'https://github.com/p6spy/p6spy.git'
licenses = ['Apache-2.0']
publicDownloadNumbers = true
githubRepo = 'p6spy/p6spy'
githubReleaseNotesFile = 'docs/releasenotes.md'
version {
released = new Date()
name = project.version
vcsTag = "p6spy-${project.version}"
// Optional configuration for Maven Central sync of the version
mavenCentralSync {
sync = true //[Default: true] Determines whether to sync the version to Maven Central.
close = '1' //Optional property. By default the staging repository is closed and artifacts are released to Maven Central. You can optionally turn this behaviour off (by puting 0 as value) and release the version manually.
user = System.getenv('SONATYPE_USERNAME') //OSS user token: mandatory
password = System.getenv('SONATYPE_PASSWORD') //OSS user password: mandatory
}
}
}
}
UPDATE
Published:
snapshots are in: http://oss.jfrog.org/oss-snapshot-local/p6spy/p6spy/ (I just followed official docs: https://www.jfrog.com/confluence/display/RTF/Deploying+Snapshots+to+oss.jfrog.org)
releases end up in: http://oss.jfrog.org/oss-release-local/p6spy/p6spy/ and are later auto-synced to maven central: http://repo1.maven.org/maven2/p6spy/p6spy/

Publish wrapped OSGi bundles to artifactory with Gradle

As not all jars are automatically usable OSGi bundles I use wrapping to generate them. After having being wrapped I'd like to publish them to my Artifactory repository. However, my lack of understanding of Gradle inhibits success, and after reading several suggested Stackoverflow answers I am still stuck.
This is my build.gradle file:
buildscript {
repositories {
maven { url 'http://localhost:8081/artifactory/gradle-dev' }
}
dependencies {
classpath 'org.standardout:bnd-platform:1.2.0'
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+"
}
}
apply plugin: 'org.standardout.bnd-platform'
apply plugin: "com.jfrog.artifactory"
apply plugin: 'maven-publish'
group = 'com.google.code.gson'
version = '2.8.0'
publishing {
publications {
osgiBundles(MavenPublication) {
artifacts {
files("build/plugins")
}
}
}
}
artifactory {
contextUrl = "${artifactory_contextUrl}" //The base Artifactory URL if not overridden by the publisher/resolver
publish {
repository {
repoKey = 'gradle-dev-local'
username = "${artifactory_user}"
password = "${artifactory_password}"
maven = true
}
defaults {
publications ('osgiBundles')
}
}
resolve {
repository {
repoKey = 'gradle-dev'
username = "${artifactory_user}"
password = "${artifactory_password}"
maven = true
}
}
}
platform {
useBndHashQualifiers = false
defaultQualifier = ''
bundle(group: 'com.google.code.gson', name:'gson', version:'2.8.0') {
bnd {
instruction 'Export-Package', 'com.google.gson,com.google.gson.stream,com.google.gson.annotations,com.google.gson.reflect'
}
}
}
The output of the script is as follows:
gradle artifactoryPublish
:generatePomFileForOsgiBundlesPublication
:artifactoryPublish
Deploying artifact: http://localhost:8081/artifactory/gradle-dev-local/com/google/code/gson/bundle-jars/2.8.0/bundle-jars-2.8.0.pom
Deploying build descriptor to: http://localhost:8081/artifactory/api/build
Build successfully deployed. Browse it in Artifactory under http://localhost:8081/artifactory/webapp/builds/bundle-jars/1489323863518
When I look in the artifactory repository the structure is not what I expected:
+- com
+--- google/code/gson/bundle-jars
|+-- 2.8.0
| +- bundle-jars.pom
+--- maven-metadata.xml
The wrong directory structure (google/code/gson/bundle-jars), where I expected several sub directorties (google, code, gson) with a 2.8.0 and a jar file.
I think I have to change the publications block, but I don't know what it should be.
I use unpuzzle (or rather this fork) to create Maven artifacts from OSGi bundles (and publish them to Artifactory).
This is probably not the most efficient solution for your use case, but at least something that works and I can come up with fast.
Here is an example of where I use unpuzzle for this purpose. Maybe that can serve as a starting point (together with the unpuzzle docs). There is a lot of bloat in my example because there I try to actually determine the original Maven artifacts for OSGi bundles created from them - as you always want the OSGi bundle, that's probably not relevant for you.
Note that by default the published artifacts will have different names (based on the bundle symbolic name) and a different group (which is configurable). But I think that is to be preferred over having the original group and name, otherwise it may get confused w/ the original. Adapting the group and name individually is possible as you can see in the example.
The publication should be as follows:
publishing {
publications {
osgiBundles(MavenPublication) {
groupId 'my.group'
artifactId 'com.google.code.gson'
version '2.8.0'
artifact file("build/plugins/com.google.code.gson_2.8.0.jar")
}
}
}

Context URL cannot be empty - Artifactory Gradle Plugin

I'm trying to get to the Artifactory Gradle plugin working to publish to my local Artifactory instance.
I have the latest version (default install) running at localhost:8081/artifactory. I can verify this with access via a webbrowser.
However, with my bare minimum example .. I am getting a "Context URL cannot be found error
Note that I have specified all the mandatory required Artifactory configurations settings - (as indicated on the Artifactory Gradle WebPage) .. including the Context URL.
buildscript {
repositories{ maven { url 'http://repo.jfrog.org/artifactory/gradle-plugins' } }
dependencies{ classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:2.0.12'}
}
apply plugin: 'artifactory'
artifactory {
contextUrl = 'http://localhost:8081/artifactory' //The base Artifactory URL if not overridden by the publisher/resolver
publish {
repository {
repoKey = 'integration-libs' //The Artifactory repository key to publish to
username = 'admin' //The publisher user name
password = 'password'
}
}
resolve {
repository {
repoKey = 'libs-releases' //The Artifactory (preferably virtual) repository key to resolve from
}
}
}
This looks like a weird bug and I'm not sure what causes it. I get it in some of my gradle build files but others seem to work fine.
I fixed it by defining the contextUrl again inside the publish element, so your script will now look like:
artifactory {
contextUrl = 'http://localhost:8081/artifactory' //The base Artifactory URL if not overridden by the publisher/resolver
publish {
contextUrl = 'http://localhost:8081/artifactory' // <- this is the fix
repository {
repoKey = 'integration-libs' //The Artifactory repository key to publish to
username = 'admin' //The publisher user name
password = 'password'
}
}
resolve {
repository {
repoKey = 'libs-releases' //The Artifactory (preferably virtual) repository key to resolve from
}
}
}
You might also have to define it again inside the resolve element.

Resources