Skip to content

Commit

Permalink
feat: support creating feature version files in update site
Browse files Browse the repository at this point in the history
Creation of these files is intended to be used in combination with
Renovate, where these files can serve as a custom datasource for feature
versions in a p2 repository created by bnd-platform.
  • Loading branch information
stempler committed Mar 19, 2024
1 parent 4e8e765 commit ffd72b9
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
if: always() # always run even if the previous step fails
with:
report_paths: 'build/test-results/**/*.xml'
require_tests: false # currently no tests present
require_tests: true

annotate_only: true
detailed_summary: true
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
# ORG_GRADLE_PROJECT_signing.password: ${{ secrets.SONATYE_PGP_PASSWORD }}
# ORG_GRADLE_PROJECT_signing.keyId: ${{ secrets.SONATYE_PGP_KEY_ID }}
# ORG_GRADLE_PROJECT_signing.secretKeyRingFile: /home/runner/.gnupg/secring.gpg

# in-memory key
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SONATYE_PGP_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SONATYE_PGP_PRIVATE_KEY }}
Expand All @@ -55,8 +55,8 @@ jobs:
if: always() # always run even if the previous step fails
with:
report_paths: 'build/test-results/**/*.xml'
require_tests: false # currently no tests present
require_tests: true # currently no tests present

annotate_only: true
detailed_summary: true
# fail_on_failure: true
# fail_on_failure: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ bin/
.project
.settings/
*~
/.idea/
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ apply plugin: 'org.standardout.bnd-platform'
The **platform** plugin comes with several Gradle tasks - the following are the main tasks and build upon each other:

* ***bundles*** - create bundles and write them to **build/plugins**
* ***potentialOptionalImports*** Creates a potentialOptionalImports.txt file of imported packages of all generated bundles with the optionalImport instruction (See "Optional Dependencies" section below)
* ***potentialOptionalImports*** Creates a potentialOptionalImports.txt file of imported packages of all generated bundles with the optionalImport instruction (See "Optional Dependencies" section below)
* ***updateSite*** - create a p2 repository from the bundles and write it to **build/updatesite** (default)
* ***updateSiteZip*** - create a ZIP archive from the p2 repository and write it to **build/updatesite.zip** (default)

Expand Down Expand Up @@ -462,6 +462,7 @@ Via the platform extension there are several settings you can provide:
* **updateSiteDir** - the directory the generated p2 repository is written to (default: `new File(buildDir, 'updatesite')`)
* **updateSiteZipFile** - the target file for the zipped p2 repository (default: `new File(buildDir, 'updatesite.zip')`)
* **appendUpdateSite** - if any the generated p2 repository should be appended to the one that already exists in **updateSiteDir** (default: `false`)
* **createFeatureVersionFiles** - if for the created update site, a version file should be created per feature, e.g. `<feature-id>_versions.json`, that includes information on the versions of the feature available in the p2 repository (default: `false`)
* **eclipseHome** - File object pointing to the directory of a local Eclipse installation to be used for generating the p2 repository (default: `null`)
* **eclipseMirror** - Eclipse download URLs to be used when no local installation is provided via *eclipseHome*. Since version 3 uses an Eclipse 2023-09 mirror by default.
* **downloadsDir** - the directory to store the downloaded Eclipse installation on local, this works if *eclipseHome* is not specified. (default: `new File(buildDir, 'eclipse-downloads')`)
Expand Down
19 changes: 16 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ repositories {
group = 'org.standardout'
version = '3.1.0-SNAPSHOT'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
}

jar {
// include license into jar
Expand All @@ -35,6 +38,16 @@ dependencies {
implementation 'commons-io:commons-io:2.15.1'
implementation 'de.undercouch:gradle-download-task:5.6.0'
implementation localGroovy()

// Testing
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.2'

testImplementation("org.assertj:assertj-core:3.25.1")
}

test {
useJUnitPlatform()
}

tasks.wrapper {
Expand Down Expand Up @@ -100,7 +113,7 @@ publishing {
pom {
name = 'bnd-platform'
packaging = 'jar'

configurePom(pom)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.standardout.gradle.plugin.platform

import org.standardout.gradle.plugin.platform.internal.util.VersionFile

import java.util.jar.*

import org.gradle.api.GradleException
Expand Down Expand Up @@ -275,6 +277,11 @@ public class PlatformPlugin implements Plugin<Project> {
}

project.logger.info 'Built p2 repository.'

def createFeatureVersionFiles = project.platform.createFeatureVersionFiles
if (createFeatureVersionFiles) {
VersionFile.createFeatureVersionFiles(project.platform.updateSiteDir)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,16 @@ class PlatformPluginExtension {
* If the update site should be appended instead of over-written.
*/
boolean appendUpdateSite = false

/**
* If after creating the update site, additional files should be created for
* each feature contained in the update site, that includes the information
* on which versions are contained in the update site.
*
* This can be used with tools like Renovate to automated updates of feature
* versions where the update site is used.
*/
boolean createFeatureVersionFiles = false

/**
* The directory of a local Eclipse installation. If none is specified the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package org.standardout.gradle.plugin.platform.internal.util

import groovy.json.JsonOutput
import groovy.xml.XmlSlurper
import groovy.xml.slurpersupport.GPathResult

import java.util.zip.ZipFile

class VersionFile {

/**
* Read an XML file from a Jar/Zip file using XMLSlurper.
*
* @param jarFile the Jar file the XML is contained in
* @param xmlFileName the name of the XML file in the Jar file
* @return the parsed XML file or null if the file did not exist
*/
private static GPathResult readXmlFromJar(File jarFile, String xmlFileName) {
def zipFile = new ZipFile(jarFile)
def entry = zipFile.getEntry(xmlFileName)

if (entry) {
zipFile.getInputStream(entry).withStream {
new XmlSlurper().parse(it)
}
} else {
null
}
}

/**
* Create version files for features in a p2 repository.
*
* @param updateSiteDir the location of the p2 repository
*/
static def createFeatureVersionFiles(File updateSiteDir) {
//TODO delete any previously existing version files?

GPathResult artifactsXml

def artifactsJar = new File(updateSiteDir, 'artifacts.jar')
if (artifactsJar.exists()) {
artifactsXml = readXmlFromJar(artifactsJar, 'artifacts.xml')
}

if (artifactsXml == null) {
def artifactsXmlFile = new File(updateSiteDir, 'artifacts.xml')
artifactsXml = new XmlSlurper().parse(artifactsXmlFile)
}

if (artifactsXml != null) {
createFeatureVersionFiles(updateSiteDir, artifactsXml)
}
}

static def createFeatureVersionFiles(File updateSiteDir, GPathResult artifactsXml) {
def featureVersions = collectFeatureVersions(artifactsXml)

featureVersions.forEach { featureId, versions ->
def versionFile = new File(updateSiteDir, "${featureId}_version.json")
writeVersionFile(versionFile, versions)
}
}

static Map<String, List<String>> collectFeatureVersions(GPathResult artifactsXml) {
def features = artifactsXml.artifacts.artifact.findAll{ a -> a.@classifier == 'org.eclipse.update.feature' }

features
.findResults { feature ->
def id = feature.@id as String
def version = feature.@version as String
if (id && version) {
[id: id, version: version]
}
else {
null
}
}
.groupBy { it.id }
.collectEntries { id, objects ->
[id, objects.collect { obj -> obj.version }]
}
}

/**
* Write a version file with the given versions.
* The format used is a Json file that is compatible with custom datasources in Renovate.
* See https://docs.renovatebot.com/modules/datasource/custom/#usage
*
* @param versionFile the file to write
* @param versions the versions
*/
static void writeVersionFile(File versionFile, List<String> versions) {
versionFile.text = JsonOutput.toJson([releases: versions.collect { [version: it] }])
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.standardout.gradle.plugin.platform.util

import groovy.xml.XmlSlurper
import org.junit.jupiter.api.Test
import org.standardout.gradle.plugin.platform.internal.util.VersionFile

import static org.assertj.core.api.Assertions.*

class VersionFileTest {

@Test
void testCollectFeatureVersions() {
def xml = getClass().getClassLoader().getResourceAsStream('artifacts-xml/example.xml').withStream {
new XmlSlurper().parse(it)
}

def featureName = 'to.wetransform.offlineresources.feature'

def versions = VersionFile.collectFeatureVersions(xml)

assertThat(versions)
.as('should have exactly one feature')
.hasSize(1)
.containsOnlyKeys(featureName)
.extractingByKey(featureName)
.asList()
.hasSize(2) // 2 versions expected
.containsExactly('2024.3.15.bnd-Ia6g4Q', '2024.3.18.bnd-tNmhEg')
}

@Test
void testCollectFeatureVersionsEmpty() {
def xml = new XmlSlurper().parseText('<repository />')

def versions = VersionFile.collectFeatureVersions(xml)

assertThat(versions)
.isEmpty()
}

}
89 changes: 89 additions & 0 deletions src/test/resources/artifacts-xml/example.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version='1.0' encoding='UTF-8'?>
<?artifactRepository version='1.1.0'?>
<repository name='file:/home/simon/repos/offline-resources/updatesite/build/updatesite/ - artifacts' type='org.eclipse.equinox.p2.artifact.repository.simpleRepository' version='1.0.0'>
<properties size='2'>
<property name='p2.timestamp' value='1710770425928'/>
<property name='p2.compressed' value='true'/>
</properties>
<mappings size='3'>
<rule filter='(&amp; (classifier=osgi.bundle))' output='${repoUrl}/plugins/${id}_${version}.jar'/>
<rule filter='(&amp; (classifier=binary))' output='${repoUrl}/binary/${id}_${version}'/>
<rule filter='(&amp; (classifier=org.eclipse.update.feature))' output='${repoUrl}/features/${id}_${version}.jar'/>
</mappings>
<artifacts size='9'>
<artifact classifier='org.eclipse.update.feature' id='to.wetransform.offlineresources.feature' version='2024.3.15.bnd-Ia6g4Q'>
<properties size='5'>
<property name='artifact.size' value='441'/>
<property name='download.size' value='441'/>
<property name='download.checksum.sha-512' value='5f1e413265b4d7bcffc75514104764c66aad18f825d97d71daca07eb874ab0c41e1f762074d91ff2db49450522effb320aee5fad6afd4ff702db48aed032df1a'/>
<property name='download.checksum.sha-256' value='9ab6b0ef619a436d01ddb1d09d69f70445882a569311d62d267d052f9066d1d5'/>
<property name='download.contentType' value='application/zip'/>
</properties>
</artifact>
<artifact classifier='osgi.bundle' id='to.wetransform.offline-resources.www.w3.org' version='2024.1.25.bnd-8IYMsg'>
<properties size='4'>
<property name='artifact.size' value='18532'/>
<property name='download.size' value='18532'/>
<property name='download.checksum.sha-512' value='eeece45a645768df0797a0e990ec8c34b6c3ea9caf10def4474288aead3c9e44a3e1b035be25f9757b52f9b0cd4feea02c8afeca9f9264b357f146ae9ca7ae6e'/>
<property name='download.checksum.sha-256' value='769c333211055e1e184510d2e95b4bcf9b1dd1e88303b023374880bcb1b96fe2'/>
</properties>
</artifact>
<artifact classifier='osgi.bundle' id='to.wetransform.offline-resources.schemas.opengis.net' version='2024.3.18.bnd-8IYMsg'>
<properties size='4'>
<property name='artifact.size' value='14571884'/>
<property name='download.size' value='14571884'/>
<property name='download.checksum.sha-512' value='bb9e1af2658ce727d7e6bd107e28547514fbfe7ac8f23e11a79f25ca1a42a5b935aaaf8720eec7d19dbdf3b9416e20ff09cf7738151ec8b6d6581aea1c1cf4e6'/>
<property name='download.checksum.sha-256' value='33340359c5c68ee62977104005290aeed5cc689d4ab13dcf329dc5d859003f14'/>
</properties>
</artifact>
<artifact classifier='org.eclipse.update.feature' id='to.wetransform.offlineresources.feature' version='2024.3.18.bnd-tNmhEg'>
<properties size='5'>
<property name='artifact.size' value='443'/>
<property name='download.size' value='443'/>
<property name='download.checksum.sha-512' value='36e12880f05a1c4c0d10170b8a84366463fc96710eea29ce9128f0c70154624ac8c38f51a11a40898e31c274da14baea0ccde06360c308444c1c1461d05a05df'/>
<property name='download.checksum.sha-256' value='de43b6f342cf3710a26d4a1d741c9bb7385677e8e74e95ba0b4c067b1573fe1d'/>
<property name='download.contentType' value='application/zip'/>
</properties>
</artifact>
<artifact classifier='osgi.bundle' id='to.wetransform.offline-resources.inspire.ec.europa.eu' version='2024.3.15.bnd-8IYMsg'>
<properties size='4'>
<property name='artifact.size' value='5839548'/>
<property name='download.size' value='5839548'/>
<property name='download.checksum.sha-512' value='4d8f0972873dcc2242eb5b174d01a7ba70a6df732fa0bf6aa0636caaae3a4be9a126484c751c2fd5300bf25a1d30cc438aedd1f188ec7fb88d2dcb394539cfde'/>
<property name='download.checksum.sha-256' value='afffa6b7e58de085d0d6f1eb795ad5db0ed6b5b99c221888eebc5d2a36c2319e'/>
</properties>
</artifact>
<artifact classifier='osgi.bundle' id='to.wetransform.offline-resources.schemas.opengis.net' version='2024.3.8.bnd-8IYMsg'>
<properties size='4'>
<property name='artifact.size' value='14571887'/>
<property name='download.size' value='14571887'/>
<property name='download.checksum.sha-512' value='233eebc16468cc3d23dc5783459bee5593de206dff936cc1afeb45ecc806c50b8df922543ab265e22fcffb715ce3a1f0e2e937e61dace5ea3ef553caba8d9378'/>
<property name='download.checksum.sha-256' value='4471f1e354c629fe6f828374a05240fe45fb5fd4b61ac157bd94be5f4c7cc12d'/>
</properties>
</artifact>
<artifact classifier='osgi.bundle' id='to.wetransform.offline-resources.repository.gdi-de.org' version='2024.2.20.bnd-8IYMsg'>
<properties size='4'>
<property name='artifact.size' value='4760565'/>
<property name='download.size' value='4760565'/>
<property name='download.checksum.sha-512' value='c36f96ebac58d758ab3ed1e409c71c84f9dee7c7410c435de2110a895d32a9df7b038696b60b6b4132e6f0ac3a044745c1fb983ddaf469228acdd4a42866d9a0'/>
<property name='download.checksum.sha-256' value='8a0c058d6ef95a9737f68f57bb7bb152fe4b3a8f5253deec7749332c14da87ef'/>
</properties>
</artifact>
<artifact classifier='osgi.bundle' id='to.wetransform.offline-resources.schemas.geosciml.org' version='2024.1.25.bnd-8IYMsg'>
<properties size='4'>
<property name='artifact.size' value='4029'/>
<property name='download.size' value='4029'/>
<property name='download.checksum.sha-512' value='3f9e1a4f76ad2a7c9ab6de03c6b50629ee61b59fbdee1021b98bc01732838b48dd82b2c49a41144ef7abfac573732adeb0f8372f54cfd067db0395161383647e'/>
<property name='download.checksum.sha-256' value='1ad370890bc7e71621a5f9f7773d7cce401f30db2dcd3c8a66d3fe9b199a1e6c'/>
</properties>
</artifact>
<artifact classifier='osgi.bundle' id='to.wetransform.offline-resources.portele.de' version='2024.1.25.bnd-8IYMsg'>
<properties size='4'>
<property name='artifact.size' value='1939'/>
<property name='download.size' value='1939'/>
<property name='download.checksum.sha-512' value='46b083abca27a1273245422148cb1c6708b365d706633ab9170116c50b42599fcd6a4953bec3ef4fa7dd90b472b41f8e25d4e366b805c020e44d424e1704a606'/>
<property name='download.checksum.sha-256' value='67707d04caaad5ed713c96e9f2182e856d4bf3a4f7b69ff2ad714a3e0170212b'/>
</properties>
</artifact>
</artifacts>
</repository>

0 comments on commit ffd72b9

Please # to comment.