如何将 JCenter 工件迁移到 Sonatype Maven 存储库?
How can I migrate JCenter artifact to the Sonatype Maven repository?
JCenter Maven 存储库将在一个月后关闭。
如何在删除所有工件之前将其迁移到 Sonatype。
我已经通过多个步骤解决了这个问题:
- 首先,我编写了一个 Java 程序来从我的 JCenter 帐户下载所有工件。
- 然后我用 Maven 插件写了一个 Gradle 脚本。此脚本使用 archiveBaseName、版本和工件目录作为参数。脚本和 maven 插件添加了所有缺失的东西,比如 PGP 签名。
- 然后我编写了一个 Java 程序来遍历下载的工件(或其中的一部分)并使用参数 archiveBaseName、版本和包含工件的目录调用 Gradle 脚本一个循环。
- 在 Sonatype GUI 上,我部署了上传的文件。一次多个版本。
我使用的以下代码片段无法开箱即用,因为它使用了内部私有 API。但它可以作为起点。当然你需要一个sonatype账号和一个gpg ring文件。秘密属性保存在 gradle.properties 中。您还需要自定义公司在所有 3 个文件中使用的组名称。
下载器
package tool;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import com.inet.lib.util.IOFunctions;
public class DownloadJCenter {
// this path will be cut on save
private static String basePath;
private static File root;
public static void main( String[] args ) throws IOException {
basePath = "/";
URL startURL = new URL( "https://jcenter.bintray.com/com/company/" );
root = new File( "jcenter" );
load( startURL );
}
/**
* Load the URL iterative
*
* @param parent the URL to load
* @throws IOException if any error occur
*/
static void load( @Nonnull URL parent ) throws IOException {
System.err.println( parent );
InputStream input = IOFunctions.openStreamSupportingRedirect( parent, 5000 );
String content = IOFunctions.readString( input, StandardCharsets.UTF_8 );
List<URL> urls = extractUrls( parent, content );
for( URL url : urls ) {
String path = url.getPath();
if( path.endsWith( "/" ) ) {
load( url );
} else {
input = IOFunctions.openStreamSupportingRedirect( url, 5000 );
byte[] bytes = IOFunctions.readBytes( input );
File file = new File( root, path.substring( basePath.length() ) );
file.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream( file )) {
fos.write( bytes );
}
}
}
}
/**
* Extract the URLs from the content of page
*
* @param parent the parent URL for relative URLs
* @param content the page content
* @return list of found URLs
* @throws IOException if any error occur
*/
@Nonnull
static List<URL> extractUrls( URL parent, @Nonnull String content ) throws IOException {
ArrayList<URL> result = new ArrayList<>();
int idx = 0;
while( true ) {
int idx1 = content.indexOf( "href=\"", idx );
if( idx1 < 0 ) {
break;
}
idx1 += 6;
int idx2 = content.indexOf( "\"", idx1 );
String urlStr = content.substring( idx1, idx2 );
if( !urlStr.startsWith( ".." ) ) {
result.add( new URL( parent, urlStr ) );
}
idx = idx2;
}
return result;
}
}
Gradle 启动器
package tool;
import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.util.ArrayList;
import java.util.HashMap;
import com.inet.error.ErrorCode;
import com.inet.lib.util.IOFunctions;
import com.inet.shared.utils.Version;
/**
* Deploy to the Sonatype server.
*/
public class DeploySonatype {
private static final String gradle = "C:/Users/...../gradle-6.7.1/bin/gradle.bat";
public static void main( String[] args ) throws IOException {
File root = new File( "jcenter/com/company" );
for( File file : root.listFiles() ) {
if( file.isDirectory() ) {
String archivesBaseName = file.getName();
deployLibrary( archivesBaseName, file );
}
}
}
static void deployLibrary( String archivesBaseName, File libraryDir ) throws IOException {
HashMap<Version,File> versionDirs = new HashMap<>();
for( File file : libraryDir.listFiles() ) {
if( file.isDirectory() ) {
versionDirs.put( new Version( file.getName()), file );
}
}
ArrayList<Version> versions = new ArrayList( versionDirs.keySet() );
versions.sort( null );// old versions first
for( Version version : versions ) {
File dir = versionDirs.get( version );
deployVersion( archivesBaseName, version, dir );
}
}
static void deployVersion( String archivesBaseName, Version version, File dir ) throws IOException {
System.err.println( archivesBaseName + " " + version + " " + dir );
File script = IOFunctions.getFile( DeploySonatype.class.getResource( "deploy.gradle" ) );
IOFunctions.deleteDir( new File( script.getParent(), "build" ) ); // clean from previous run
ArrayList<String> command = new ArrayList<>();
command.add( gradle );
command.add( "-b" );
command.add( "\"" + script.getPath() + "\"" );
command.add( "--stacktrace" );
command.add( "-ParchivesBaseName=" + archivesBaseName );
command.add( "-Pversion=" + version );
command.add( "-PartifactDir=" + dir.getAbsolutePath() );
command.add( "uploadArchives" );
System.err.println( command );
ProcessBuilder processBuilder = new ProcessBuilder( command );
processBuilder.redirectOutput( Redirect.INHERIT );
processBuilder.redirectError( Redirect.INHERIT );
processBuilder.environment().put( "JAVA_HOME", System.getProperty( "java.home" ) );
Process start = processBuilder.start();
try {
int exitValue = start.waitFor();
if( exitValue != 0 ) {
throw new IOException( "Exit Value: " + exitValue );
}
} catch( InterruptedException ex ) {
ErrorCode.throwAny( ex );
}
}
}
deploy.gradle
/****************************************
* Deploy to Maven
****************************************/
apply plugin: 'maven'
apply plugin: 'signing'
group = 'com.company'
println archivesBaseName + "/" + version + " -> " + artifactDir // come from Java as parameter
task copyArtifact(type: Copy) {
from file( artifactDir )
into file("$buildDir/artifacts" )
}
task setupArchives {
dependsOn copyArtifact
doLast {
artifacts {
fileTree( dir: file( "$buildDir/artifacts" ) ).each {
archives file: it
println "\t" + it
}
}
signing {
if (project.hasProperty("signing.keyId") ){
sign configurations.archives
}
}
}
}
uploadArchives {
dependsOn setupArchives
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment ->
signing {
fileTree( dir: file( "$buildDir/artifacts" ) ).each {
sign it
println "sign: " + it
}
}
}
if (project.hasProperty("ossrhUsername") ){
repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: project["ossrhUsername"], password: project["ossrhPassword"])
}
}
}
}
}
JCenter Maven 存储库将在一个月后关闭。
如何在删除所有工件之前将其迁移到 Sonatype。
我已经通过多个步骤解决了这个问题:
- 首先,我编写了一个 Java 程序来从我的 JCenter 帐户下载所有工件。
- 然后我用 Maven 插件写了一个 Gradle 脚本。此脚本使用 archiveBaseName、版本和工件目录作为参数。脚本和 maven 插件添加了所有缺失的东西,比如 PGP 签名。
- 然后我编写了一个 Java 程序来遍历下载的工件(或其中的一部分)并使用参数 archiveBaseName、版本和包含工件的目录调用 Gradle 脚本一个循环。
- 在 Sonatype GUI 上,我部署了上传的文件。一次多个版本。
我使用的以下代码片段无法开箱即用,因为它使用了内部私有 API。但它可以作为起点。当然你需要一个sonatype账号和一个gpg ring文件。秘密属性保存在 gradle.properties 中。您还需要自定义公司在所有 3 个文件中使用的组名称。
下载器
package tool;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import com.inet.lib.util.IOFunctions;
public class DownloadJCenter {
// this path will be cut on save
private static String basePath;
private static File root;
public static void main( String[] args ) throws IOException {
basePath = "/";
URL startURL = new URL( "https://jcenter.bintray.com/com/company/" );
root = new File( "jcenter" );
load( startURL );
}
/**
* Load the URL iterative
*
* @param parent the URL to load
* @throws IOException if any error occur
*/
static void load( @Nonnull URL parent ) throws IOException {
System.err.println( parent );
InputStream input = IOFunctions.openStreamSupportingRedirect( parent, 5000 );
String content = IOFunctions.readString( input, StandardCharsets.UTF_8 );
List<URL> urls = extractUrls( parent, content );
for( URL url : urls ) {
String path = url.getPath();
if( path.endsWith( "/" ) ) {
load( url );
} else {
input = IOFunctions.openStreamSupportingRedirect( url, 5000 );
byte[] bytes = IOFunctions.readBytes( input );
File file = new File( root, path.substring( basePath.length() ) );
file.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream( file )) {
fos.write( bytes );
}
}
}
}
/**
* Extract the URLs from the content of page
*
* @param parent the parent URL for relative URLs
* @param content the page content
* @return list of found URLs
* @throws IOException if any error occur
*/
@Nonnull
static List<URL> extractUrls( URL parent, @Nonnull String content ) throws IOException {
ArrayList<URL> result = new ArrayList<>();
int idx = 0;
while( true ) {
int idx1 = content.indexOf( "href=\"", idx );
if( idx1 < 0 ) {
break;
}
idx1 += 6;
int idx2 = content.indexOf( "\"", idx1 );
String urlStr = content.substring( idx1, idx2 );
if( !urlStr.startsWith( ".." ) ) {
result.add( new URL( parent, urlStr ) );
}
idx = idx2;
}
return result;
}
}
Gradle 启动器
package tool;
import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.util.ArrayList;
import java.util.HashMap;
import com.inet.error.ErrorCode;
import com.inet.lib.util.IOFunctions;
import com.inet.shared.utils.Version;
/**
* Deploy to the Sonatype server.
*/
public class DeploySonatype {
private static final String gradle = "C:/Users/...../gradle-6.7.1/bin/gradle.bat";
public static void main( String[] args ) throws IOException {
File root = new File( "jcenter/com/company" );
for( File file : root.listFiles() ) {
if( file.isDirectory() ) {
String archivesBaseName = file.getName();
deployLibrary( archivesBaseName, file );
}
}
}
static void deployLibrary( String archivesBaseName, File libraryDir ) throws IOException {
HashMap<Version,File> versionDirs = new HashMap<>();
for( File file : libraryDir.listFiles() ) {
if( file.isDirectory() ) {
versionDirs.put( new Version( file.getName()), file );
}
}
ArrayList<Version> versions = new ArrayList( versionDirs.keySet() );
versions.sort( null );// old versions first
for( Version version : versions ) {
File dir = versionDirs.get( version );
deployVersion( archivesBaseName, version, dir );
}
}
static void deployVersion( String archivesBaseName, Version version, File dir ) throws IOException {
System.err.println( archivesBaseName + " " + version + " " + dir );
File script = IOFunctions.getFile( DeploySonatype.class.getResource( "deploy.gradle" ) );
IOFunctions.deleteDir( new File( script.getParent(), "build" ) ); // clean from previous run
ArrayList<String> command = new ArrayList<>();
command.add( gradle );
command.add( "-b" );
command.add( "\"" + script.getPath() + "\"" );
command.add( "--stacktrace" );
command.add( "-ParchivesBaseName=" + archivesBaseName );
command.add( "-Pversion=" + version );
command.add( "-PartifactDir=" + dir.getAbsolutePath() );
command.add( "uploadArchives" );
System.err.println( command );
ProcessBuilder processBuilder = new ProcessBuilder( command );
processBuilder.redirectOutput( Redirect.INHERIT );
processBuilder.redirectError( Redirect.INHERIT );
processBuilder.environment().put( "JAVA_HOME", System.getProperty( "java.home" ) );
Process start = processBuilder.start();
try {
int exitValue = start.waitFor();
if( exitValue != 0 ) {
throw new IOException( "Exit Value: " + exitValue );
}
} catch( InterruptedException ex ) {
ErrorCode.throwAny( ex );
}
}
}
deploy.gradle
/****************************************
* Deploy to Maven
****************************************/
apply plugin: 'maven'
apply plugin: 'signing'
group = 'com.company'
println archivesBaseName + "/" + version + " -> " + artifactDir // come from Java as parameter
task copyArtifact(type: Copy) {
from file( artifactDir )
into file("$buildDir/artifacts" )
}
task setupArchives {
dependsOn copyArtifact
doLast {
artifacts {
fileTree( dir: file( "$buildDir/artifacts" ) ).each {
archives file: it
println "\t" + it
}
}
signing {
if (project.hasProperty("signing.keyId") ){
sign configurations.archives
}
}
}
}
uploadArchives {
dependsOn setupArchives
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment ->
signing {
fileTree( dir: file( "$buildDir/artifacts" ) ).each {
sign it
println "sign: " + it
}
}
}
if (project.hasProperty("ossrhUsername") ){
repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: project["ossrhUsername"], password: project["ossrhPassword"])
}
}
}
}
}