Use Gradle to upload archives to GitLab Maven repository - java

I use Gradle and Gradle Maven plugin to upload archives to Maven Central. This works fine. I want to upload the same archives also to the GitLab Maven repository. Upload to this repo requires authentication with special HTTP header values. But I don't know how to do that with the Gradle Maven plugin. The GitLab documentation describes a Maven based build process using pom.xml and settings.xml. What I currently have is this:
Files created with Gradle:
build/libs
├── wms-1.2.0.jar
├── wms-1.2.0.jar.asc
├── wms-1.2.0-javadoc.jar
├── wms-1.2.0-javadoc.jar.asc
├── wms-1.2.0-sources.jar
└── wms-1.2.0-sources.jar.asc
build/poms
└── pom-default.xml
Files according to the GitLab documentation:
pom.xml
settings.xml
When I execute
mvn deploy -s settings.xml
then pom-default.xml must be "mixed into" pom.xml and build/libs/* must be used as archive location. How can I do this?

Please note that the maven-publish plugin “is now the preferred option for publishing [Maven] artifacts” with Gradle (see also the note at the top of the page you linked). Doing what you need with the maven plugin would be more cumbersome as it’s both more intricate and not as well documented, IMHO. Hence, I hope you won’t mind when I answer your question with the maven-publish plugin in mind.
As for the authentication with special HTTP header values, you should be able to solve this as follows (via):
publishing {
repositories {
maven {
url "http://repo.mycompany.com/maven2"
credentials(HttpHeaderCredentials) {
name = "Private-Token"
value = "REPLACE_WITH_YOUR_PERSONAL_ACCESS_TOKEN"
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
}
I you really want to use the old maven plugin then I’d suggest to start researching from this section of the documentation on how to solve the authentication issue.
BTW: I have mainly answered your question on how to authenticate with GitLab here. If you should have trouble with configuring the publication, then I’d recommend to post this as a separate question with more details on your setup and on maybe what you’ve tried already.
Edited to add: if you need to use different header credentials depending on certain command line options, you could change the credentials configuration above to something like the following:
credentials(HttpHeaderCredentials) {
if (project.hasProperty('jobToken')) {
name = "Job-Token"
value = project.property('jobToken')
} else {
name = "Private-Token"
value = "REPLACE_WITH_YOUR_PERSONAL_ACCESS_TOKEN"
}
}
In this example, you could run ./gradlew -PjobToken=REPLACE_WITH_YOUR_JOB_TOKEN … in GitLab CI jobs while leaving out the -PjobToken=… part when working locally.

Related

How to publish Android Library to Maven Central Gradle 7.2.0

I am trying to upload my first android library to Maven Central, but there is no proper documentation for Gradle 7.2.0 after some research I found some links but they are outdated too.
Things I have done till now:
registered on jira
approved my project on it and got access to nexus repository manager
generated gpg key
now comes configuring the gradle file:
so i have followed this library = https://github.com/vanniktech/gradle-maven-publish-plugin
in project root build.gradle file i have mentioned
buildscript {
dependencies {
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.20.0' // NEW
classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.10.2' // NEW
}
}
in library build.gradle i have added this plugin
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'com.vanniktech.maven.publish' // NEW
}
in project root gradle.properties i have written this
SONATYPE_HOST=S01
RELEASE_SIGNING_ENABLED=true
GROUP=io.github.Darkprnce
POM_ARTIFACT_ID=InnerDrawer
VERSION_NAME=1.0.0
POM_NAME=InnerDrawer
POM_PACKAGING=aar
POM_DESCRIPTION=Inner Drawer is a highly customizable navigation drawer.
POM_INCEPTION_YEAR=2022
POM_URL=https://github.com/Darkprnce/InnerDrawer
POM_SCM_URL=https://github.com/Darkprnce/InnerDrawer
POM_SCM_CONNECTION=scm:git#github.com:Darkprnce/InnerDrawer.git
POM_SCM_DEV_CONNECTION=scm:git#github.com:Darkprnce/InnerDrawer.git
POM_LICENCE_NAME=MIT License
POM_LICENCE_URL=http://www.opensource.org/licenses/mit-license.php
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=Darkprnce
POM_DEVELOPER_NAME=Tarun Yadvendu
POM_DEVELOPER_URL=https://github.com/Darkprnce
signing.keyId=FCF8EDCA
signing.password=[my signing password]
signing.secretKeyRingFile=secret-keys.gpg
ossrhUsername=[my username]
ossrhPassword=[my password]
then again in library build.gradle i have mentioned publishing code
publishing {
repositories {
maven {
url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2"
credentials {
username = ossrhUsername
password = ossrhPassword
}
}
}
}
now comes the issue when i run this command in terminal it gives wrong credentials error
* What went wrong:
Credentials required for this build could not be resolved.
> The following Gradle properties are missing for 'mavenCentral' credentials:
- mavenCentralUsername
- mavenCentralPassword
so if anyone has used this library then help me in this, library is well maintained so no issues in that i only want to know the right way to upload.
After akarnokd help i was able to upload my library to Maven Central.
Step i have taken to resolve this issue:
First i have renamed the properties in project root => gradle.properties
ossrhUsername=[my username] ==> mavenCentralUsername
ossrhPassword=[my password] ==> mavenCentralPassword
Now the above information is available to everyone, as we have just mention our username and password in a gradle file which will be uploaded with the code. so to prevent that, akarnokd suggested me to create a new gradle.properties file in C:/users/darkprnce/.gradle/gradle.properties and place the below information in that file :
mavenCentralUsername=[my username]
mavenCentralPassword=[my password]
signing.keyId=[last 8 digit of your key]
signing.password=[signing password]
signing.secretKeyRingFile=secring.gpg
we can remove publishing from library build.gradle file as it is already included in library.
run command
./gradlew publish --no-daemon --no-parallel
It will upload the library to Maven Central, then you can close the repository and release it.

Maven Plugin: Manipulate resources during packaging

in my maven project, I've got a xml file in resources. Depending on some input parameter I want the file to be adapted before packaged into a jar or war. Of course, the original file shall not be touched.
It is not an option to create multiple xml-files and select a suitable one, for example, with spring profiles as there can be numerous combinations of contents in the xml file.
So, I thought of creating a maven plugin, that manipulates the file before packaging. Probably, I need to manipulate the file, when maven has copied the file to the target folder but before maven packages the file into the jar/war.
#Mojo(name = "manipulate-xml", defaultPhase = LifecyclePhase.PREPARE_PACKAGE)
public class MyMojo extends AbstractMojo {
#Parameter(defaultValue = "${project}", required = true, readonly = true)
MavenProject project;
#Parameter(property = "option")
String option;
public void execute() throws MojoExecutionException {
if (option.equals("optionA")) {
// get file from target and manipulate
} else if (option.equals("optionB")) {
// get file from target and manipulate
}
}
}
Then, I could embedded the maven plugin into my project and build the project with
mvn clean package -Doption=optionA
However, now I am stuck. I do not know, how to get the file from target and even if this is the right approach.
Besides, is it possible during the packaging to prevent some dependencies from being packaged into the jar/war?
I appreciate any help.
Depending on what manipulating means, you can use the possibilities of the maven resources plugin (https://maven.apache.org/plugins/maven-resources-plugin/index.html).
If you need to modify some simple values inside the xml, use properties in the xml and let the resources plugin replace them during build. The values for the build can be either in the pom.xml or given to maven via -Dproperty=value.
If you want to select a different files, define multiple maven profiles, in each you can configure the resources plugin to copy only the wanted files and then select the correct profile in the build.
If the built-in possibilities are not enough, you might even program your own filter for the resources plugin, that might be easier than writing a custom full fledged maven plugin.

Is there a way to create maven dependency in pom file from a local jar

There is a java project which has too many jar files and it has become difficult to manage all of them. I know there is a way to convert this project to a maven project but I don't want to manually add the dependency for each jar in my project to the pom file.
Is there any way to create the pom file using the local jar files such that the pom will contain the dependency tag for each jar.
Edit : I want to clarify that I do not want to add the jars from/to the local repository.
The local repository approach will not work for me as the project will be used by multiple users across different systems.
I wanted to create regular pom dependency entries along with groupId, artifactId and version for the jar files which I already have so that I can copy-paste it into the pom file when I convert my project to a Maven project. As I have a large number of JARs in my project, I would have to do it all manually.
Solution provided by #Milen Dyankov worked for me. I was able to get the information I needed from most of the JAR files.
This code
public static void main(String[] args) throws IOException {
Set<String> missingMavenData = new HashSet<String>();
String FOLDER = "/path/to/your/folder/with/jars";
Files
.walk(Paths.get(FOLDER), FileVisitOption.FOLLOW_LINKS)
.map(Path::toFile)
.filter(f -> f.isFile())
.filter(f -> f.getName().endsWith(".jar"))
.map(f -> {
try {
return new JarFile(f);
} catch (IOException e) {
e.printStackTrace();
return null;
}
})
.filter(Objects::nonNull)
.map(jar -> {
Properties properties = null;
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
if (jarEntry.getName().matches("^META-INF/maven/.*/pom\\.properties$")) {
try {
properties = new Properties();
properties.load(jar.getInputStream(jarEntry));
break;
} catch (IOException e) {
e.printStackTrace();
}
};
}
if (properties == null) {
missingMavenData.add(jar.getName());
}
return properties;
})
.filter(Objects::nonNull)
.forEach(properties -> {
System.out.println("< dependency>");
System.out.println(" <groupId>" + properties.getProperty("groupId")+ "</groupId>");
System.out.println(" <artifactId>" + properties.getProperty("artifactId")+ "</artifactId>");
System.out.println(" <version>" + properties.getProperty("version")+ "</version>");
System.out.println("</dependency>");
});
System.out.println("Those JAR files do not contain Maven metadata:");
missingMavenData.forEach(System.out::println);
}
will iterate over your jar files and try to find the Maven metadata in them. It will generate the POM entries for those who have it and will list those that don't have it so you can add it manually. I hope this helps.
UPDATE:
I added bom-helper:fromJars goal to BOM Helper Maven Plugin which does more or less the same thing as the above code. One can now simply add the plugin
<plugin>
<groupId>com.commsen.maven</groupId>
<artifactId>bom-helper-maven-plugin</artifactId>
<version>0.2.0</version>
</plugin>
and configure it to call fromJars goal. It can also be called form command line. For example:
mvn bom-helper:fromJars \
-Dbom-helper.jarsFolder=/path/to/folder/with/jars \
-Dbom-helper.inplace=true \
-Dbom-helper.recursive
will update current pom with entries for all jars that have Maven metadata in them.
You can Install manually the JAR into your local Maven repository
First Way
Follow the steps here
mvn install:install-file -Dfile=<path-to-file>
And in here you can be able to add these information in command line
mvn install:install-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version>
<path-to-file> mentioned path to the JAR to install
<group-id> mentioned group id of the JAR to install
<artifact-id> mentioned artifact id of the JAR to install
<version> mentioned version of the JAR
Second Way
You can create a different local Maven repository
Follow the Steps
we can consider the new local Maven repository is named maven-repository and is located in ${basedir} (the directory containing pom.xml).
Deploying the local JARs in the new local maven repository as below i am following
mvn deploy:deploy-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version> -Dpackaging=jar -Durl=file:./maven-repository/ -DrepositoryId=maven-repository -DupdateReleaseInfo=true
you can see which the maven deploy:deploy-file installs the artifact in a remote repository but in here repository is located in the local machine
After installing the JARs your need to add the repository in your pom.xml file
<repositories>
<repository>
<id>maven-repository</id>
<url>file:///${project.basedir}/maven-repository</url>
</repository>
</repositories>
AFAIK there is not Maven way to do this.
You could write a script that computes the SHA1 value of each jar and searches for the jar in your Maven repository. It could, alternatively, open the jars and search for the pom.properties to extract the Maven coordinates.
In any way, this is probably too much programming effort unless you need to do it for many projects.
What you need is your local maven nexus that is only available in your server. Considering you have jars that are only available to you, I guess that you have sources for them. As a result, you should be able to convert them into maven projects and deploy them into your local nexus. After fixing the structure, maven will download your dependencies and the dependencies' dependencies when resolving a project.
Or you could use <scope>system</scope>, but that requires absolute paths and is generally not suggested.

Zeppelin does not see dependencies from custom repository

I want to add company artifactory to Zeppelin spark interpreter and try to use this document.
So, the URL of our artifactory looks like
http://artifactory.thecompany.com:8081/artifactory/
The access is not restricted to specific user and artifacts are downloadable both from my machine and from machine where Zepplin is running (I tried this with curl).
I've copied the artifact ID from by build.gradle, so I am pretty sure it is correct. However when I try to add the artifact that should be found in my company's artifactory I get error
Error setting properties for interpreter 'spark.spark': Could not find
artifact
com.feedvisor.dataplatform:data-platform-schema-scala:jar:3.0.19-SNAPSHOT
in central (http://repo1.maven.org/maven2/)
This error message sounds like Zeppelin did not try to look for my dependency in custom repository.
I tried to play with artifactory URL using:
http://artifactory.thecompany.com:8081/artifactory/
http://artifactory.thecompany.com:8081/
as well as with "snapshot" property of "Add New Repository" form (using true and false) but nothing helped. The error message does not disappear and classes from the referenced artifact are not found.
Thanks in advance.
For Zeppelin to use your company's repo by default you can set ZEPPELIN_INTERPRETER_DEP_MVNREPO in your ${Z_HOME}/conf/zeppelin-env.sh:
export ZEPPELIN_INTERPRETER_DEP_MVNREPO=http://artifactory.thecompany.com:8081/artifactory/
Alternatively, you can use Dynamic Dependency Loading feature of the notebook:
%dep
z.reset()
z.addRepo("Artifactory").url("http://artifactory.thecompany.com:8081/artifactory/").snapshot()
z.load("com.feedvisor.dataplatform:data-platform-schema-scala:3.0.19-SNAPSHOT")

How to get the next build number in Gradle

Is there any way to get the next version when publishing to a repository in gradle?
For e.g. if I have the version 3.0.1 in my repository I want the published version to be 3.0.2.
ivy has a task for ant named buildnumber which does exactly that:
<project xmlns:ivy="antlib:org.apache.ivy.ant"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<target name="ivyBuildNumber" description="Use ivy get the next build number">
<ivy:buildnumber
resolver="url-chain"
organisation="${ivy.organisation}"
module="${ivy.module}"
revision="${version.base}"/>
<echoproperties prefix="ivy.new."/>
</target>
Is there a way to do so in gradle? if not how can I access ivy tasks from gradle's ant?
In my build.gradle I calling to the ant
ant.importBuild 'build.xml'
I don't think there is support in Gradle, but you can try to use the Ant task.
https://docs.gradle.org/current/userguide/ant.html#sec:import_ant_build
Another way to do this is to use some sort of plugin, or customized task for managing the version.
Plugin: https://github.com/researchgate/gradle-release
Custom task: https://www.tikalk.com/devops/increment-version-numbers-in-gradle/
Yes, you can access ivy tasks from the ant script by importing ant's build.xml file to gradle's build.gradle file. Following is the syntax to do so.
ant.importBuild 'build.xml'
Please refer : https://docs.gradle.org/current/userguide/ant.html#sec:import_ant_build
I recommend you to use ResearchGate release plugin
https://github.com/researchgate/gradle-release
It has a pretty documentation. Easy to read.
Also, check out how I used it in my personal project.
https://github.com/vatolinrp/bitcoin-esb/blob/master/build.gradle
It would be a nice example for you.
After a long work, I managed to do that.
In my build.gradle I added this following code
ant.importBuild 'build.xml'
task getNextBuild(dependsOn : ivyBuildNumber) {
doLast{
def nextVersion = ant.properties['ivy.new.revision']
println nextVersion
}
}
I imported my ant build file, and created a task that calls the ivy buildnumber task.
There is my build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant">
<target name="ivyBuildNumber">
<path id="ivy.classpath" path="lib/ivy.jar" />
<typedef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant" classpathref="ivy.classpath" />
<ivy:buildnumber
organisation="daniel"
module="hello"/>
<echoproperties prefix="ivy.new."/>
</target>
</project>
Because my IDE (Intellij), didn't have ivy.jar in the content,
I imported the ivy.jar from my root dir (lib/ivy.jar)
For this exact behavior, Ivy buildnumber task can be invoked using pure Gradle without importing the Ant build:
configurations {
antTasks // define a new configuration
}
repositories {
mavenCentral()
}
dependencies {
antTasks("org.apache.ivy:ivy:2.4.0") // add Ivy library to it
}
ext {
// define the Ivy task, using the extra configuration as classpath extension
ant.taskdef(name: "ivyBuildNumber",
classname: "org.apache.ivy.ant.IvyBuildNumber",
classpath: configurations.antTasks.asPath)
ant.ivyBuildNumber(organisation: "daniel", module: "hello")
nextVersion = ant.properties["ivy.new.revision"]
}
task demo {
doLast {
println nextVersion
}
}
In general, Gradle doesn't have any bundled equivalent to Maven Release Plugin, so one has to rely on plugins. One solid plugin is gradle-release by ResearchGate, the other is axion by Allegro Tech. The former is classic Maven-style versioning, the latter takes SCM itself as the only source of truth, eliminating the versioning in the build files. But neither of these plugins does provide the exact requested behavior.
My personal take on the versioning problem was initially to use some plugins. Since I use Bamboo as CI server at work, literally everything I did with release plugins using Gradle crashed on CI server sooner or later. It might have worked for some weeks, but every server update brought some problems. I ended up using SCM-less approach with a simple convention: use branch name as base version, concatenate it with build number (both values are provided by the CI server):
ext {
branch = System.getProperty("branch", "develop")
buildNumber = System.getProperty("buildNumber", "latest")
isRelease = System.getProperty("isRelease", "false").toBoolean()
artifactVersion = "${branch}${(isRelease ? ".$buildNumber" : "-SNAPSHOT")}"
}
CI server then can be set up for executing the following command
./gradlew -DisRelease=true -Dbranch=${git.branch} -DbuildNumber=${build.number} mavenPublish
when 'Release' button is pushed. For example, build 12 of the 3.0 branch will produce version 3.0.12 in the binary repository.
The advantages are:
+ the version comes for free, assuming the branches are named accordingly
+ the auto-incremented build number also comes for free
+ one can easily publish custom revisions
+ no plugins means no problems with Gradle version updates
+ this approach is dead simple and always works
The downsides are:
- additional script tasks are required for tags
- some build numbers will be skipped, obviously (e.g. next version after 3.5.76 can be 3.5.84)

Categories