Skip to content

Commit 1f94567

Browse files
authored
Fix #203 : assemble-maven-repository should include PGP signatures in the P2 site (#228)
* Fix #203 : assemble-maven-repository should include PGP signatures in the P2 site Signed-off-by: Christoph Läubrich <laeubi@laeubi-soft.de>
1 parent 2dfb052 commit 1f94567

File tree

8 files changed

+491
-96
lines changed

8 files changed

+491
-96
lines changed

RELEASE_NOTES.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This page describes the noteworthy improvements provided by each release of Ecli
44

55
## Next release...
66

7+
### [Support for PGP Signatures in maven-p2 sites](https://github.com/eclipse/tycho/issues/203)
8+
9+
The `assemble-maven-repository` mojo now supports embedding the PGP signature of maven artifacts to allow additional verifications and trust decisions.
10+
711
### Support for new m2e-pde features
812

913
Tycho supports the new m2e-pde features regarding [multiple dependencies per target](https://github.com/eclipse-m2e/m2e-core/blob/master/RELEASE_NOTES.md#the-m2e-pde-editor-now-supports-adding-more-than-one-dependency-per-target-location) and specifying [extra repositories in the target](https://github.com/eclipse-m2e/m2e-core/blob/master/RELEASE_NOTES.md#the-m2e-pde-editor-now-supports-adding-additional-maven-repoistories-for-a-target-location).
@@ -46,18 +50,18 @@ From now on this restriction is no longer true and one is able to execute unit-t
4650
Tycho also includes a new tycho-failsafe mojo, that is similar to the maven one:
4751
- it executes at the integration-test phase but do not fail the build if a test fails, instead a summary file is written
4852
- the outcome of the tests are checked in the verify phase (and fail the build there if neccesary)
49-
- this allows to hook some setup/teardown mojos (e.g. start webservers, ...) in the pre-integration-test phase and to safly tear them down in the post-integration test phase (thus the name 'failsafe' see [tycho-failsafe-faq](https://maven.apache.org/surefire/maven-failsafe-plugin/faq.html) for some more details
53+
- this allows to hook some setup/teardown mojos (e.g. start webservers, ...) in the pre-integration-test phase and to safely tear them down in the post-integration test phase (thus the name 'failsafe' see [tycho-failsafe-faq](https://maven.apache.org/surefire/maven-failsafe-plugin/faq.html) for some more details
5054

5155
Given you have the above setup you create an integration-test (executed in an OSGi runtime like traditional tycho-surefire mojo) as following:
5256

5357
- create a new test that mathes the pattern `*IT.java` (or configure a different pattern that do not intersects with the surefire test pattern)
5458
- run your it with `mvn verify`
5559

56-
:warning: If you where previously using `-Dtest=....` on the root level of your build tree it might now be neccesary to also include `-Dsurefire.failIfNoSpecifiedTests=false` as maven-surefire might otherwhise complain about
60+
:warning: If you where previously using `-Dtest=....` on the root level of your build tree it might now be necessary to also include `-Dsurefire.failIfNoSpecifiedTests=false` as maven-surefire might otherwise complain about
5761

5862
> No tests were executed! (Set -DfailIfNoTests=false to ignore this error.)
5963
60-
for your eclipse-plugin packaged project if they do not match anything (the error message is a bit missleading, thsi is tracked in [SUREFIRE-1910](https://issues.apache.org/jira/browse/SUREFIRE-1910)).
64+
for your eclipse-plugin packaged project if they do not match anything (the error message is a bit misleading, this is tracked in [SUREFIRE-1910](https://issues.apache.org/jira/browse/SUREFIRE-1910)).
6165

6266

6367
### [Enhanced support for debug output in surefire-tests](https://github.com/eclipse/tycho/issues/52)
@@ -88,7 +92,7 @@ Tycho now understands the `additional.bundles` directive in the `build.propertie
8892

8993
### Create p2 repository referencing Maven artifacts
9094

91-
A new mojo [tycho-p2-repository-plugin:assemble-maven-repository](https://www.eclipse.org/tycho/sitedocs/tycho-p2-repository-plugin/assemble-maven-repository.html) was added to enable creation of p2 repositories directly from Maven artifact references. This removes the usual need to create a target definition and a category.xml for this task.
95+
A new mojo [tycho-p2-repository-plugin:assemble-maven-repository](https://www.eclipse.org/tycho/sitedocs/tycho-p2/tycho-p2-repository-plugin/assemble-maven-repository-mojo.html) was added to enable creation of p2 repositories directly from Maven artifact references. This removes the usual need to create a target definition and a category.xml for this task.
9296

9397
### [Skip Tycho dependency-resolution for clean-only builds by default](https://github.com/eclipse/tycho/issues/166)
9498

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2021 Christoph Läubrich and others.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Christoph Läubrich - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.tycho.p2.tools.publisher;
14+
15+
import java.util.Map;
16+
17+
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
18+
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
19+
import org.eclipse.equinox.p2.metadata.Version;
20+
import org.eclipse.equinox.p2.publisher.AbstractAdvice;
21+
import org.eclipse.equinox.p2.publisher.actions.IPropertyAdvice;
22+
import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;
23+
import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor;
24+
25+
@SuppressWarnings("restriction")
26+
public class PGPSignatureAdvice extends AbstractAdvice implements IPropertyAdvice {
27+
private final String id;
28+
private final Version version;
29+
private final String signature;
30+
31+
public PGPSignatureAdvice(String id, Version version, String signature) {
32+
this.id = id;
33+
this.version = version;
34+
this.signature = signature;
35+
}
36+
37+
@Override
38+
protected String getId() {
39+
return id;
40+
}
41+
42+
@Override
43+
protected Version getVersion() {
44+
return version;
45+
}
46+
47+
@Override
48+
public Map<String, String> getInstallableUnitProperties(InstallableUnitDescription iu) {
49+
return null;
50+
}
51+
52+
@Override
53+
public boolean isApplicable(String configSpec, boolean includeDefault, String candidateId,
54+
Version candidateVersion) {
55+
return id.equals(candidateId) && version.equals(candidateVersion);
56+
}
57+
58+
@Override
59+
public Map<String, String> getArtifactProperties(IInstallableUnit iu, IArtifactDescriptor descriptor) {
60+
// workaround Bug 539672
61+
if (descriptor instanceof ArtifactDescriptor) {
62+
ArtifactDescriptor artifactDescriptor = (ArtifactDescriptor) descriptor;
63+
artifactDescriptor.setProperty("pgp.signatures", signature);
64+
}
65+
return null;
66+
}
67+
68+
}

tycho-bundles/org.eclipse.tycho.p2.tools.impl/src/main/java/org/eclipse/tycho/p2/tools/publisher/TychoFeaturesAndBundlesPublisherApplication.java

+69-16
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ public class TychoFeaturesAndBundlesPublisherApplication extends AbstractPublish
5858
private static final String MAVEN_PREFIX = "maven-";
5959
private BundleDescription[] bundles;
6060
private File[] advices;
61+
private String[] signatures;
6162
private URI categoryDefinition;
6263
private String[] rules;
64+
private String publicKeys;
6365

6466
@Override
6567
public Object run(PublisherInfo publisherInfo) throws Exception {
@@ -75,6 +77,9 @@ public Object run(PublisherInfo publisherInfo) throws Exception {
7577
SimpleArtifactRepository repo = (SimpleArtifactRepository) repoFactory.load(artifactLocation,
7678
IRepositoryManager.REPOSITORY_HINT_MODIFIABLE, null);
7779
repo.setRules(newRules);
80+
if (publicKeys != null) {
81+
repo.setProperty("pgp.publicKeys", publicKeys);
82+
}
7883
repo.save();
7984
}
8085
}
@@ -85,41 +90,49 @@ public Object run(PublisherInfo publisherInfo) throws Exception {
8590
protected void processParameter(String arg, String parameter, PublisherInfo publisherInfo)
8691
throws URISyntaxException {
8792
super.processParameter(arg, parameter, publisherInfo);
93+
8894
if (arg.equalsIgnoreCase("-bundles")) {
89-
bundles = Arrays.stream(AbstractPublisherAction.getArrayFromString(parameter, ",")).map(File::new)
90-
.map(t -> {
91-
try {
92-
return BundlesAction.createBundleDescription(t);
93-
} catch (IOException | BundleException e) {
94-
//ignoring files that are "not bundles" they will be skipped on the later steps
95-
return null;
96-
}
97-
}).toArray(BundleDescription[]::new);
98-
}
99-
if (arg.equalsIgnoreCase("-bundlesFile")) {
10095
bundles = Arrays.stream(getArrayFromFile(parameter)).map(File::new).map(t -> {
10196
try {
10297
return BundlesAction.createBundleDescription(t);
10398
} catch (IOException | BundleException e) {
10499
//ignoring files that are "not bundles" they will be skipped on the later steps
100+
System.out.println("Ignore " + t.getName() + " as it is not a bundle!");
105101
return null;
106102
}
107103
}).toArray(BundleDescription[]::new);
108104
}
109105
if (arg.equalsIgnoreCase("-advices")) {
110-
advices = Arrays.stream(AbstractPublisherAction.getArrayFromString(parameter, ","))
111-
.map(str -> str.isBlank() ? null : new File(str)).toArray(File[]::new);
112-
}
113-
if (arg.equalsIgnoreCase("-advicesFile")) {
114106
advices = Arrays.stream(getArrayFromFile(parameter)).map(str -> str.isBlank() ? null : new File(str))
115107
.toArray(File[]::new);
116108
}
109+
if (arg.equalsIgnoreCase("-signatures")) {
110+
signatures = Arrays.stream(getArrayFromFile(parameter)).map(str -> str.isBlank() ? null : new File(str))
111+
.map(file -> {
112+
if (file == null) {
113+
return null;
114+
}
115+
try {
116+
return FileUtils.readFileToString(file, StandardCharsets.US_ASCII);
117+
} catch (IOException e) {
118+
// skip unreadable files...
119+
return null;
120+
}
121+
}).toArray(String[]::new);
122+
}
117123
if (arg.equalsIgnoreCase("-categoryDefinition")) {
118124
categoryDefinition = URIUtil.fromString(parameter);
119125
}
120126
if (arg.equalsIgnoreCase("-rules")) {
121127
rules = AbstractPublisherAction.getArrayFromString(parameter, ",");
122128
}
129+
if (arg.equalsIgnoreCase("-publicKeys")) {
130+
try {
131+
publicKeys = FileUtils.readFileToString(new File(parameter), StandardCharsets.US_ASCII);
132+
} catch (IOException e) {
133+
throw new URISyntaxException(parameter, "can't read public key file: " + e);
134+
}
135+
}
123136
}
124137

125138
private String[] getArrayFromFile(String parameter) {
@@ -139,7 +152,7 @@ protected IPublisherAction[] createActions() {
139152
for (int i = 0; i < advices.length; i++) {
140153
File adviceFile = advices[i];
141154
BundleDescription bundleDescription;
142-
if (i >= bundles.length - 1 || (bundleDescription = bundles[i]) == null) {
155+
if (i >= bundles.length || (bundleDescription = bundles[i]) == null) {
143156
continue;
144157
}
145158
String symbolicName = bundleDescription.getSymbolicName();
@@ -194,6 +207,28 @@ public Map<String, String> getInstallableUnitProperties(InstallableUnitDescripti
194207
result.add(new AdviceFilePublisherAction(advicesList));
195208
}
196209
}
210+
if (signatures != null) {
211+
List<PGPSignatureAdvice> signaturesList = new ArrayList<>();
212+
for (int i = 0; i < signatures.length; i++) {
213+
String signature = signatures[i];
214+
if (signature == null) {
215+
//no signature...
216+
continue;
217+
}
218+
BundleDescription bundleDescription;
219+
if (i >= bundles.length || (bundleDescription = bundles[i]) == null) {
220+
continue;
221+
}
222+
String symbolicName = bundleDescription.getSymbolicName();
223+
if (symbolicName == null) {
224+
//not a bundle... no signature...
225+
continue;
226+
}
227+
signaturesList.add(new PGPSignatureAdvice(symbolicName,
228+
PublisherHelper.fromOSGiVersion(bundleDescription.getVersion()), signature));
229+
}
230+
result.add(new SignaturePublisherAction(signaturesList));
231+
}
197232
if (bundles != null) {
198233
result.add(new BundlesAction(bundles));
199234
}
@@ -203,6 +238,24 @@ public Map<String, String> getInstallableUnitProperties(InstallableUnitDescripti
203238
return result.toArray(IPublisherAction[]::new);
204239
}
205240

241+
private static final class SignaturePublisherAction extends AbstractPublisherAction {
242+
243+
private List<PGPSignatureAdvice> signaturesList;
244+
245+
public SignaturePublisherAction(List<PGPSignatureAdvice> signaturesList) {
246+
this.signaturesList = signaturesList;
247+
}
248+
249+
@Override
250+
public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
251+
for (PGPSignatureAdvice signature : signaturesList) {
252+
publisherInfo.addAdvice(signature);
253+
}
254+
return Status.OK_STATUS;
255+
}
256+
257+
}
258+
206259
private static final class AdviceFilePublisherAction extends AbstractPublisherAction {
207260

208261
private List<AdviceFileAdvice> advicesList;
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project>
3+
<modelVersion>4.0.0</modelVersion>
4+
<groupId>org.eclipse.tycho.it</groupId>
5+
<artifactId>issue-203-parent</artifactId>
6+
<version>1.0.0</version>
7+
<packaging>pom</packaging>
8+
<properties>
9+
<tycho-version>2.5.0-SNAPSHOT</tycho-version>
10+
</properties>
11+
<modules>
12+
<module>site</module>
13+
</modules>
14+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project>
3+
4+
<parent>
5+
<groupId>org.eclipse.tycho.it</groupId>
6+
<artifactId>issue-203-parent</artifactId>
7+
<version>1.0.0</version>
8+
</parent>
9+
<modelVersion>4.0.0</modelVersion>
10+
<artifactId>pgp-site</artifactId>
11+
<packaging>pom</packaging>
12+
<build>
13+
<plugins>
14+
<plugin>
15+
<groupId>org.eclipse.tycho</groupId>
16+
<artifactId>tycho-p2-repository-plugin</artifactId>
17+
<version>${tycho-version}</version>
18+
<executions>
19+
<execution>
20+
<configuration>
21+
<categoryName>PGP Site</categoryName>
22+
<includeDependencies>true</includeDependencies>
23+
<includePGPSignature>true</includePGPSignature>
24+
</configuration>
25+
<id>maven-p2-site</id>
26+
<phase>package</phase>
27+
<goals>
28+
<goal>assemble-maven-repository</goal>
29+
</goals>
30+
</execution>
31+
<execution>
32+
<id></id>
33+
<phase>verify</phase>
34+
<goals>
35+
<goal>verify-repository</goal>
36+
</goals>
37+
<inherited>false</inherited>
38+
<configuration>
39+
</configuration>
40+
</execution>
41+
</executions>
42+
</plugin>
43+
</plugins>
44+
</build>
45+
46+
<dependencies>
47+
<dependency>
48+
<groupId>org.apache.commons</groupId>
49+
<artifactId>commons-numbers-core</artifactId>
50+
<version>1.0-beta1</version>
51+
</dependency>
52+
</dependencies>
53+
54+
</project>

tycho-its/src/test/java/org/eclipse/tycho/test/p2Repository/MavenP2SiteTest.java

+21-4
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ public void testProduceConsume() throws Exception {
3333
Verifier verifier = getVerifier("p2mavensite/producer", false);
3434
verifier.executeGoals(asList("clean", "install"));
3535
verifier.verifyErrorFreeLog();
36-
assertTrue(new File(verifier.getBasedir(), "target/repository/artifacts.xml").exists());
37-
assertTrue(new File(verifier.getBasedir(), "target/repository/content.xml").exists());
38-
assertTrue(new File(verifier.getBasedir(), "target/p2-site.zip").exists());
36+
verifyRepositoryExits(verifier, "");
3937
}
4038
{ // consumer
4139
Verifier verifier = getVerifier("p2mavensite/consumer", false);
@@ -49,10 +47,29 @@ public void testDeployIgnore() throws Exception {
4947
Verifier verifier = getVerifier("p2mavensite.reactor", false);
5048
verifier.executeGoals(asList("install"));
5149
verifier.verifyErrorFreeLog();
50+
verifyRepositoryExits(verifier, "site/");
5251
String artifacts = FileUtils
5352
.readFileToString(new File(verifier.getBasedir(), "site/target/repository/artifacts.xml"), "UTF-8");
5453
assertTrue("artifact to deploy is missing", artifacts.contains("id='org.eclipse.tycho.it.deployme'"));
5554
assertFalse("artifact is deployed but should't", artifacts.contains("id='org.eclipse.tycho.it.ignoreme'"));
56-
assertFalse("artifact is deployed but should't", artifacts.contains("id='org.eclipse.tycho.it.ignoreme-property'"));
55+
assertFalse("artifact is deployed but should't",
56+
artifacts.contains("id='org.eclipse.tycho.it.ignoreme-property'"));
57+
}
58+
59+
@Test
60+
public void testPGP() throws Exception {
61+
Verifier verifier = getVerifier("p2mavensite.pgp", false);
62+
verifier.executeGoals(asList("clean", "install"));
63+
verifier.verifyErrorFreeLog();
64+
verifyRepositoryExits(verifier, "site/");
65+
}
66+
67+
protected void verifyRepositoryExits(Verifier verifier, String subdir) {
68+
File artifacts = new File(verifier.getBasedir(), subdir + "target/repository/artifacts.xml");
69+
assertTrue(artifacts.getAbsolutePath() + " is missing", artifacts.exists());
70+
File content = new File(verifier.getBasedir(), subdir + "target/repository/content.xml");
71+
assertTrue(content.getAbsolutePath() + " is missing", content.exists());
72+
File site = new File(verifier.getBasedir(), subdir + "target/p2-site.zip");
73+
assertTrue(site.getAbsolutePath() + " is missing", site.exists());
5774
}
5875
}

tycho-p2/tycho-p2-repository-plugin/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
<artifactId>sisu-equinox-launching</artifactId>
4646
<version>${project.version}</version>
4747
</dependency>
48+
<dependency>
49+
<groupId>org.bouncycastle</groupId>
50+
<artifactId>bcpg-jdk16</artifactId>
51+
<version>1.46</version>
52+
</dependency>
4853
</dependencies>
4954

5055
</project>

0 commit comments

Comments
 (0)