Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/cibuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ env:
P2_SIGN: true
P2_SIGN_KEY: ${{ secrets.GPG_KEY_ID }}
P2_SIGN_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
P2_PUB_KEY: ${{ secrets.GPG_PUBLIC_KEY }}
SONATYPE_BEARER: ${{ secrets.SONATYPE_BEARER }}
P2_PUB_KEY: ${{ secrets.GPG_PUBLIC_KEY }}
SONATYPE_BEARER: ${{ secrets.SONATYPE_BEARER }}

defaults:
run:
Expand Down Expand Up @@ -123,6 +123,9 @@ jobs:
./.github/scripts/ci-publish.sh
env:
CANONICAL: ${{ matrix.canonical }}
GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
SONATYPE_BEARER: ${{ secrets.SONATYPE_BEARER }}
JFROG_USERNAME: ${{ secrets.JFROG_USERNAME }}
JFROG_PASSWORD: ${{ secrets.JFROG_PASSWORD }}
- name: Upload Test Reports
Expand Down
4 changes: 0 additions & 4 deletions biz.aQute.bnd.transform/bnd.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ Bundle-Description: Class file and Manifest header Transformation Support

-sources: false

-maven-release: pom;path=JAR,\
sources;-sourcepath="${project.allsourcepath}",\
javadoc;-classpath="${project.buildpath}"

-maven-scope: provided

-buildpath: \
Expand Down
216 changes: 159 additions & 57 deletions biz.aQute.repository/src/aQute/maven/provider/MavenFileRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

Expand All @@ -19,7 +23,7 @@

public class MavenFileRepository extends MavenBackingRepository {

private final File remote;
private final File remote;
private HttpClient client = null;

public MavenFileRepository(File localRepo, File remote, Reporter reporter) throws Exception {
Expand Down Expand Up @@ -89,62 +93,160 @@ public boolean isRemote() {
return false;
}

/**
* Creates a ZIP archive containing all files from the remote repository directory.
*
* @param outputFile the destination file for the ZIP archive
* @return the created ZIP file
* @throws IOException if an error occurs during ZIP creation
*/
public File createZipArchive(File outputFile) throws IOException {
if (!remote.exists() || !remote.isDirectory()) {
throw new IllegalStateException("Remote directory does not exist or is not a directory: " + remote);
}

IO.mkdirs(outputFile.getParentFile());

try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputFile))) {
Path remotePath = remote.toPath();

Files.walk(remotePath)
.filter(path -> !Files.isDirectory(path))
.forEach(path -> {
try {
Path relativePath = remotePath.relativize(path);
ZipEntry zipEntry = new ZipEntry(relativePath.toString().replace("\\", "/"));

zos.putNextEntry(zipEntry);

try (FileInputStream fis = new FileInputStream(path.toFile())) {
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
}

zos.closeEntry();
} catch (IOException e) {
reporter.error("Error adding file %s to ZIP archive: %s", path, e.getMessage());
}
});
}

return outputFile;
}

/**
* Creates a ZIP archive containing all files from the remote repository directory.
* The ZIP file will be created in the system temp directory with a generated name.
*
* @return the created ZIP file
* @throws IOException if an error occurs during ZIP creation
*/
public File createZipArchive() throws IOException {
File tempFile = File.createTempFile("sonatype-bundle-", ".zip");
tempFile.deleteOnExit();
return createZipArchive(tempFile);
}
/**
* Discovers all groupIds in the Maven repository by analyzing the directory
* structure.
*
* @return map of groupId to its directory path
* @throws IOException if an error occurs reading the directory
*/
private Map<String, File> discoverGroupIds() throws IOException {
Map<String, File> groupIds = new LinkedHashMap<>();
if (!remote.exists() || !remote.isDirectory()) {
return groupIds;
}

// Walk the directory tree to find all groupIds
Files.walk(remote.toPath())
.filter(Files::isDirectory)
.forEach(dir -> {
File dirFile = dir.toFile();
File[] children = dirFile.listFiles();
if (children != null && children.length > 0) {
// Look for artifact directories (contain version
// subdirectories with artifacts)
for (File child : children) {
if (child.isDirectory()) {
File[] versionDirs = child.listFiles();
if (versionDirs != null) {
for (File versionDir : versionDirs) {
if (versionDir.isDirectory() && containsArtifacts(versionDir)) {
// Found a groupId directory
Path relativePath = remote.toPath()
.relativize(dir);
String groupId = relativePath.toString()
.replace(File.separatorChar, '.');
if (!groupId.isEmpty() && !groupIds.containsKey(groupId)) {
groupIds.put(groupId, dirFile);
}
return;
}
}
}
}
}
}
});

return groupIds;
}

/**
* Checks if a directory contains Maven artifacts (JAR, POM, WAR, AAR
* files).
*/
private boolean containsArtifacts(File dir) {
File[] files = dir.listFiles();
if (files == null) {
return false;
}
for (File file : files) {
if (file.isFile()) {
String name = file.getName();
if (name.endsWith(".jar") || name.endsWith(".pom") || name.endsWith(".war") || name.endsWith(".aar")) {
return true;
}
}
}
return false;
}

/**
* Creates ZIP archives, one for each Maven namespace/groupId in the
* repository. Each archive contains all files for that specific groupId.
*
* @return list of created ZIP files with groupId information
* @throws IOException if an error occurs during ZIP creation
*/
public List<GroupIdArchive> createZipArchive() throws IOException {
if (!remote.exists() || !remote.isDirectory()) {
throw new IllegalStateException("Remote directory does not exist or is not a directory: " + remote);
}

List<GroupIdArchive> archives = new ArrayList<>();
Map<String, File> groupIds = discoverGroupIds();

if (groupIds.isEmpty()) {
reporter.warning("No groupIds found in repository: %s", remote);
return archives;
}

reporter.trace("Found %d groupId(s) in repository: %s", groupIds.size(), groupIds.keySet());

// Create a separate ZIP archive for each groupId
for (Map.Entry<String, File> entry : groupIds.entrySet()) {
String groupId = entry.getKey();
File groupDir = entry.getValue();

String sanitizedGroupId = groupId.replace('.', '_');
File tempFile = File.createTempFile("sonatype-bundle-" + sanitizedGroupId + "-", ".zip");
tempFile.deleteOnExit();

try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tempFile))) {
Path groupDirPath = groupDir.toPath();
Path remotePath = remote.toPath();

Files.walk(groupDirPath)
.filter(path -> !Files.isDirectory(path))
.forEach(path -> {
try {
// Use path relative to remote root (includes
// groupId path)
Path relativePath = remotePath.relativize(path);
ZipEntry zipEntry = new ZipEntry(relativePath.toString()
.replace("\\", "/"));

zos.putNextEntry(zipEntry);

try (FileInputStream fis = new FileInputStream(path.toFile())) {
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
}

zos.closeEntry();
} catch (IOException e) {
reporter.error("Error adding file %s to ZIP archive for groupId %s: %s", path, groupId,
e.getMessage());
}
});
}

archives.add(new GroupIdArchive(groupId, tempFile));
reporter.trace("Created ZIP archive for groupId %s: %s", groupId, tempFile);
}

return archives;
}

/**
* Container class for a groupId and its corresponding archive file.
*/
public static class GroupIdArchive {
public final String groupId;
public final File archiveFile;

public GroupIdArchive(String groupId, File archiveFile) {
this.groupId = groupId;
this.archiveFile = archiveFile;
}

public String getSanitizedGroupId() {
return groupId.replace('.', '_');
}
}

protected HttpClient getClient() {
return client;
Expand Down
34 changes: 26 additions & 8 deletions biz.aQute.repository/src/aQute/maven/provider/Releaser.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,34 @@ private void prepareSonatypeUpload() throws IOException, Exception {
throw new IllegalStateException("No release repository configured for Sonatype upload");
}
}
logger.info("Creating and uploading deployment bundle for Sonatype Central Portal");
logger.info("Creating and uploading deployment bundles for Sonatype Central Portal");
MavenFileRepository mfr = (MavenFileRepository) mbr;
client = mfr.getClient();
File deploymentBundle = mfr.createZipArchive();
uploadToPortal(deploymentBundle);
File deploymentIdFile = Files.createTempFile("deploymentid", ".txt")
.toFile();
Files.writeString(deploymentIdFile.toPath(), deploymentId, StandardOpenOption.CREATE);
deploymentIdFile.deleteOnExit();
mbr.store(deploymentIdFile, MavenBndRepository.SONATYPE_DEPLOYMENTID_FILE);

// Create archives for each groupId
List<MavenFileRepository.GroupIdArchive> archives = mfr.createZipArchive();
if (archives.isEmpty()) {
throw new IllegalStateException("No groupIds found in staging repository");
}

logger.info("Found {} groupId(s) to upload", archives.size());

// Upload each archive separately
for (MavenFileRepository.GroupIdArchive archive : archives) {
logger.info("Processing groupId: {}", archive.groupId);
uploadToPortal(archive.archiveFile);

// Store deployment ID file with groupId in filename
String sanitizedGroupId = archive.getSanitizedGroupId();
File deploymentIdFile = Files.createTempFile(sanitizedGroupId + "_deploymentid", ".txt")
.toFile();
Files.writeString(deploymentIdFile.toPath(), deploymentId, StandardOpenOption.CREATE);
deploymentIdFile.deleteOnExit();

String deploymentIdPath = sanitizedGroupId + "_" + MavenBndRepository.SONATYPE_DEPLOYMENTID_FILE;
mbr.store(deploymentIdFile, deploymentIdPath);
logger.info("Completed upload for groupId: {} with deployment ID: {}", archive.groupId, deploymentId);
}
}

private String normalize(String sonatypePublisherUrl) {
Expand Down
2 changes: 1 addition & 1 deletion bndtools.m2e.debug.fragment/bnd.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Bundle-SymbolicName: ${p};singleton:=true
Fragment-Host: org.eclipse.m2e.maven.runtime

-buildpath: org.eclipse.m2e.maven.runtime
-buildpath: org.eclipse.m2e.maven.runtime;maven-optional=true

Import-Package: \
javax.crypto,\
Expand Down
4 changes: 3 additions & 1 deletion bndtools.m2e/bnd.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
bndtools.api;version=latest,\
bndtools.core;version=latest,\
slf4j.api;version=latest,\
org.eclipse.m2e.maven.runtime,\
org.eclipse.m2e.maven.runtime;maven-optional=true,\
org.eclipse.m2e.core;maven-optional=true,\
org.eclipse.m2e.jdt;maven-optional=true,\
org.apache.maven:maven-artifact,\
org.apache.maven:maven-core,\
org.apache.maven:maven-model,\
Expand Down
27 changes: 25 additions & 2 deletions cnf/build.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,17 @@ Bundle-Version: ${base.version}.${tstamp}-SNAPSHOT
"org.osgi.service.*.annotations";~version=latest

# Maven info. The maven artifactId defaults to Bundle-SymbolicName
# signing is only active if GPG_KEY_ID and GPG_PASSPHRASE are set
-groupid: biz.aQute.bnd
-pom: version=${if;${def;-snapshot};${versionmask;===;${@version}}-${def;-snapshot};${versionmask;===s;${@version}}}
-maven-release: pom;path=JAR,javadoc;-classpath="${project.buildpath}"

-maven-release: \
pom;path=JAR,\
javadoc;-classpath="${project.buildpath}",\
sign;keyname=${env;GPG_KEY_ID};passphrase=${env;GPG_PASSPHRASE}

# supress the warning if keyname/passphrase is empty. needed to avoid failing the build
-fixupmessages.releasesign: "No value after '=' sign for attribute (passphrase|keyname)"

gpg: gpg --homedir ${def;USERHOME;~}/.gnupg --pinentry-mode loopback

Expand Down Expand Up @@ -89,7 +97,22 @@ Bundle-Developers: \
organization="Liferay Inc."; \
organizationUrl="https://www.liferay.com"; \
roles="developer"; \
timezone="America/New_York"
timezone="America/New_York",\
chrisrueger; \
name="Christoph Rueger"; \
email="chrisrueger@gmail.com"; \
organization="Synesty GmbH"; \
organizationUrl="https://synesty.com/"; \
roles="developer"; \
timezone="Europe/Berlin",\
peterkir; \
name="Peter Kirschner"; \
email="peter@klib.io"; \
url="https://peterkir.github.io"; \
organization="Kirschners GmbH"; \
organizationUrl="https://peterkir.github.io/"; \
roles="developer"; \
timezone="Europe/Berlin"

-make: (*).(jar);type=bnd; recipe="bnd/$1.bnd"
-reproducible: true
Expand Down
4 changes: 2 additions & 2 deletions cnf/ext/repositories.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ sonatype_bearer: '${env;SONATYPE_BEARER}'
snapshotUrl = ${sonatype_snapshots}; \
index = ${.}/release.maven; \
name = "Sonatype"; \
sonatypeMode = manual

sonatypeMode = manual;\
noupdateOnRelease=true

# Eclipse Release repository
#-plugin.1.Eclipse:\
Expand Down
3 changes: 1 addition & 2 deletions cnf/includes/bndtools.bnd
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ eclipse.importpackage: \

# Add a fixup for the Unsued import on the refactoring package from above.
# This package is only used by a small number of bundles (only bndtools.core)
-fixupmessages.eclipserefactor: "Unused Import-Package instructions: \\[org.eclipse.jdt.internal.corext.refactoring.*\\]

-fixupmessages.eclipserefactor: "Unused Import-Package instructions: \\[org.eclipse.jdt.internal.corext.refactoring.*\\]"