diff --git a/pom.xml b/pom.xml index 8aae6a8c8..13feb76bc 100644 --- a/pom.xml +++ b/pom.xml @@ -188,6 +188,11 @@ 4.11.0 test + + org.codehaus.plexus + plexus-utils + 4.0.2 + diff --git a/src/it/property-expansion/pom.xml b/src/it/property-expansion/pom.xml new file mode 100644 index 000000000..b6c488fd9 --- /dev/null +++ b/src/it/property-expansion/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + org.vafer + jdeb-it + 1.0 + description from pom + + 2.0.0-SNAPSHOT + 1 + + ${release.version}-${property.package.revision} + 1.8 + 1.8 + + + + + org.vafer + jdeb + @project.version@ + + + package + + jdeb + + + true + ${basedir}/src/deb/control + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + regex-property + + regex-property + + + release.version + ${property.upstream.version} + -SNAPSHOT + + false + + + + + + + + diff --git a/src/it/property-expansion/src/deb/control/control b/src/it/property-expansion/src/deb/control/control new file mode 100644 index 000000000..c3dff5c53 --- /dev/null +++ b/src/it/property-expansion/src/deb/control/control @@ -0,0 +1,7 @@ +Package: [[name]] +Version: [[property.project.version]] +Section: misc +Priority: low +Architecture: all +Description: [[description]] +Maintainer: tcurdt@vafer.org diff --git a/src/it/property-expansion/src/main/java/org/vafer/jdeb/examples/Main.java b/src/it/property-expansion/src/main/java/org/vafer/jdeb/examples/Main.java new file mode 100644 index 000000000..62a24347d --- /dev/null +++ b/src/it/property-expansion/src/main/java/org/vafer/jdeb/examples/Main.java @@ -0,0 +1,7 @@ +package org.vafer.jdeb.examples; + +public class Main { + public static void main(String[] args) { + System.out.println("jdeb example!"); + } +} diff --git a/src/it/property-expansion/verify.groovy b/src/it/property-expansion/verify.groovy new file mode 100644 index 000000000..78df0e978 --- /dev/null +++ b/src/it/property-expansion/verify.groovy @@ -0,0 +1,54 @@ +import java.nio.file.Files +import java.util.zip.GZIPInputStream +import org.apache.commons.compress.archivers.ar.ArArchiveInputStream +import org.apache.commons.compress.archivers.ar.ArArchiveEntry +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream + +File deb = new File(basedir, 'target/jdeb-it_1.0_all.deb') +assert deb.exists() + +InputStream debInput = new FileInputStream(deb) +ArArchiveInputStream arInput = new ArArchiveInputStream(debInput) +List controlContent = [] + +ArArchiveEntry entry +while ((entry = arInput.getNextEntry()) != null) { + if (entry.getName() == 'control.tar.gz') { + + // Save control.tar.gz to a temporary file + File tempControlGz = File.createTempFile("control", ".tar.gz") + tempControlGz.deleteOnExit() + tempControlGz.withOutputStream { out -> + byte[] buffer = new byte[4096] + int len + while ((len = arInput.read(buffer)) != -1) { + out.write(buffer, 0, len) + } + } + + // Read the contents of control.tar.gz + GZIPInputStream gzipIn = new GZIPInputStream(new FileInputStream(tempControlGz)) + TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn) + + def tarEntry + while ((tarEntry = tarIn.nextEntry) != null) { + String name = tarEntry.name + + if (name == './control' || name == 'control') { + BufferedReader reader = new BufferedReader(new InputStreamReader(tarIn, 'UTF-8')) + while (reader.readLine() != null) { + controlContent.add(reader.readLine()); + } + break + } + } + + tarIn.close() + gzipIn.close() + } +} + +arInput.close() +debInput.close() + +assert "Version: 2.0.0-1".equals(controlContent.get(0)) diff --git a/src/main/java/org/vafer/jdeb/maven/DebMojo.java b/src/main/java/org/vafer/jdeb/maven/DebMojo.java index 0bd1daf4d..b6da2103a 100644 --- a/src/main/java/org/vafer/jdeb/maven/DebMojo.java +++ b/src/main/java/org/vafer/jdeb/maven/DebMojo.java @@ -414,37 +414,6 @@ protected void setData( Data[] dataSet ) { } } - @SuppressWarnings("unchecked,rawtypes") - protected VariableResolver initializeVariableResolver( Map variables, Long outputTimestampMs ) { - variables.putAll((Map) getProject().getProperties()); - variables.putAll((Map) System.getProperties()); - variables.put("name", name != null ? name : getProject().getName()); - variables.put("artifactId", getProject().getArtifactId()); - variables.put("groupId", getProject().getGroupId()); - variables.put("version", getProjectVersion(outputTimestampMs)); - variables.put("description", getProject().getDescription()); - variables.put("extension", "deb"); - variables.put("baseDir", getProject().getBasedir().getAbsolutePath()); - variables.put("buildDir", buildDirectory.getAbsolutePath()); - variables.put("project.version", getProject().getVersion()); - - if (getProject().getInceptionYear() != null) { - variables.put("project.inceptionYear", getProject().getInceptionYear()); - } - if (getProject().getOrganization() != null) { - if (getProject().getOrganization().getName() != null) { - variables.put("project.organization.name", getProject().getOrganization().getName()); - } - if (getProject().getOrganization().getUrl() != null) { - variables.put("project.organization.url", getProject().getOrganization().getUrl()); - } - } - - variables.put("url", getProject().getUrl()); - - return new MapVariableResolver(variables); - } - /** * Doc some cleanup and conversion on the Maven project version. *
    @@ -521,10 +490,16 @@ public void execute() throws MojoExecutionException { console = new MojoConsole(getLog(), verbose); initializeSignProperties(); - + Long outputTimestampMs = new OutputTimestampResolver(console).resolveOutputTimestamp(outputTimestamp); - final VariableResolver resolver = initializeVariableResolver(new HashMap(), outputTimestampMs); + final VariableResolver resolver = MapVariableResolver.builder() + .withName(this.name) + .withVersion(getProjectVersion(outputTimestampMs)) + .withMavenProject(getProject()) + .withSystemProperties(System.getProperties()) + .withBuildDirectory(this.buildDirectory.getAbsolutePath()) + .build(); final File debFile = new File(Utils.replaceVariables(resolver, deb, openReplaceToken, closeReplaceToken)); final File controlDirFile = new File(Utils.replaceVariables(resolver, controlDir, openReplaceToken, closeReplaceToken)); diff --git a/src/main/java/org/vafer/jdeb/utils/MapVariableResolver.java b/src/main/java/org/vafer/jdeb/utils/MapVariableResolver.java index 5dc2ab11c..303964e18 100644 --- a/src/main/java/org/vafer/jdeb/utils/MapVariableResolver.java +++ b/src/main/java/org/vafer/jdeb/utils/MapVariableResolver.java @@ -15,7 +15,13 @@ */ package org.vafer.jdeb.utils; +import java.util.HashMap; import java.util.Map; +import java.util.Properties; + +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; /** * Resolve variables based on a Map. @@ -34,4 +40,92 @@ public String get( String key ) { return map.get(key); } + public static MapVariableResolverBuilder builder() { + return new MapVariableResolverBuilder(); + } + + public static class MapVariableResolverBuilder { + private String name; + private MavenProject mavenProject; + private Properties systemProperties; + private String buildDirectory; + private String version; + + public MapVariableResolverBuilder withName(String name) { + this.name = name; + return this; + } + + public MapVariableResolverBuilder withMavenProject(MavenProject project) { + this.mavenProject = project; + return this; + } + + public MapVariableResolverBuilder withSystemProperties(Properties properties) { + this.systemProperties = properties; + return this; + } + + public MapVariableResolverBuilder withBuildDirectory(String directory) { + this.buildDirectory = directory; + return this; + } + + public MapVariableResolverBuilder withVersion(String version) { + this.version = version; + return this; + } + + public MapVariableResolver build() { + if( this.mavenProject == null || this.systemProperties == null ) { + throw new IllegalStateException("MavenProject and system properties must be set"); + } + + Map variables = new HashMap() ; + + Map combinedProperties = new HashMap<>(); + combinedProperties.putAll((Map) this.mavenProject.getProperties()); + combinedProperties.putAll((Map) this.systemProperties); + + // Expand (interpolate) values using RegexBasedInterpolator + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + for (Map.Entry entry : combinedProperties.entrySet()) { + interpolator.addValueSource(new org.codehaus.plexus.interpolation.MapBasedValueSource(combinedProperties)); + try { + String expandedValue = interpolator.interpolate(entry.getValue(), ""); + variables.put(entry.getKey(), expandedValue); + } catch (InterpolationException e) { + // Fallback to original value if interpolation fails + variables.put(entry.getKey(), entry.getValue()); + } + } + + variables.put("name", this.name != null ? this.name : this.mavenProject.getName()); + variables.put("artifactId", this.mavenProject.getArtifactId()); + variables.put("groupId", this.mavenProject.getGroupId()); + variables.put("version", this.version); + variables.put("description", this.mavenProject.getDescription()); + variables.put("extension", "deb"); + variables.put("baseDir", this.mavenProject.getBasedir().getAbsolutePath()); + variables.put("buildDir", this.buildDirectory); + variables.put("project.version", this.mavenProject.getVersion()); + + if (this.mavenProject.getInceptionYear() != null) { + variables.put("project.inceptionYear", this.mavenProject.getInceptionYear()); + } + if (this.mavenProject.getOrganization() != null) { + if (this.mavenProject.getOrganization().getName() != null) { + variables.put("project.organization.name", this.mavenProject.getOrganization().getName()); + } + if (this.mavenProject.getOrganization().getUrl() != null) { + variables.put("project.organization.url", this.mavenProject.getOrganization().getUrl()); + } + } + + variables.put("url", this.mavenProject.getUrl()); + + return new MapVariableResolver(variables); + } + } + } diff --git a/src/test/java/org/vafer/jdeb/utils/MapVariableResolverTestCase.java b/src/test/java/org/vafer/jdeb/utils/MapVariableResolverTestCase.java new file mode 100644 index 000000000..0261e2f02 --- /dev/null +++ b/src/test/java/org/vafer/jdeb/utils/MapVariableResolverTestCase.java @@ -0,0 +1,233 @@ +/* + * Copyright 2025 The jdeb developers. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.vafer.jdeb.utils; + +import java.io.File; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.model.Organization; +import org.apache.maven.project.MavenProject; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.vafer.jdeb.utils.MapVariableResolver.MapVariableResolverBuilder; + +public final class MapVariableResolverTestCase extends Assert { + + @Test(expected = IllegalStateException.class) + public void builderThrowsWithMissingMavenProject() throws Exception { + MapVariableResolverBuilder builder = MapVariableResolver.builder(); + + // Mock Maven Project + MavenProject mockMavenProject = Mockito.mock(MavenProject.class); + File expectedBaseDir = FileUtils.getTempDirectory(); + Mockito.when(mockMavenProject.getBasedir()).thenReturn(expectedBaseDir); + + Properties mockMavenProperties = new Properties(); + Mockito.when(mockMavenProject.getProperties()).thenReturn(mockMavenProperties); + + // Mock System properties + Properties mockSystemProperties = new Properties(); + + builder + .withName("Name") + .withVersion("2.0.0") + .withSystemProperties(mockSystemProperties) + .withBuildDirectory("aDirectory") + .build(); + } + + @Test(expected = IllegalStateException.class) + public void builderThrowsWithMissingSystemProperties() throws Exception { + MapVariableResolverBuilder builder = MapVariableResolver.builder(); + + // Mock Maven Project + MavenProject mockMavenProject = Mockito.mock(MavenProject.class); + File expectedBaseDir = FileUtils.getTempDirectory(); + Mockito.when(mockMavenProject.getBasedir()).thenReturn(expectedBaseDir); + + Properties mockMavenProperties = new Properties(); + Mockito.when(mockMavenProject.getProperties()).thenReturn(mockMavenProperties); + + builder + .withName("Name") + .withVersion("2.0.0") + .withMavenProject(mockMavenProject) + .withBuildDirectory("aDirectory") + .build(); + } + + @Test + public void builderWorksWithNoData() throws Exception { + MapVariableResolverBuilder builder = MapVariableResolver.builder(); + + // Mock Maven Project + MavenProject mockMavenProject = Mockito.mock(MavenProject.class); + File expectedBaseDir = FileUtils.getTempDirectory(); + Mockito.when(mockMavenProject.getBasedir()).thenReturn(expectedBaseDir); + + Properties mockMavenProperties = new Properties(); + Mockito.when(mockMavenProject.getProperties()).thenReturn(mockMavenProperties); + + // Mock System properties + Properties mockSystemProperties = new Properties(); + + // Builder + MapVariableResolver resolver = builder + .withName("test") + .withVersion("2.0.0") + .withMavenProject(mockMavenProject) + .withSystemProperties(mockSystemProperties) + .withBuildDirectory("aDirectory") + .build(); + + assertEquals(null, resolver.get("artifactId")); + assertEquals(expectedBaseDir.getAbsolutePath(), resolver.get("baseDir")); + assertEquals("aDirectory", resolver.get("buildDir")); + assertEquals(null, resolver.get("description")); + assertEquals("deb", resolver.get("extension")); + assertEquals(null, resolver.get("groupId")); + assertEquals("test", resolver.get("name")); + assertEquals(null, resolver.get("project.version")); + assertEquals(null, resolver.get("url")); + assertEquals("2.0.0", resolver.get("version")); + } + + @Test + public void builderWorksWithAllMandatoryData() throws Exception { + MapVariableResolverBuilder builder = MapVariableResolver.builder(); + + // Mock Maven Project + MavenProject mockMavenProject = Mockito.mock(MavenProject.class); + File expectedBaseDir = FileUtils.getTempDirectory(); + Mockito.when(mockMavenProject.getBasedir()).thenReturn(expectedBaseDir); + + LinkedHashMap mavenMap = new LinkedHashMap(); + mavenMap.put("maven.compiler.source", "1.8"); + mavenMap.put("maven.compiler.target", "1.8"); + mavenMap.put("property.one", "one"); + mavenMap.put("meta.property", "${property.one}-${property.two}-${property.three}"); + mavenMap.put("nested.meta.property", "${property.one}+${meta.property}"); + + Properties mockMavenProperties = new Properties(); + mockMavenProperties.putAll(mavenMap); + Mockito.when(mockMavenProject.getProperties()).thenReturn(mockMavenProperties); + + Mockito.when(mockMavenProject.getArtifactId()).thenReturn("anAwesomeArtifactId"); + Mockito.when(mockMavenProject.getGroupId()).thenReturn("anAwesomeGroupId"); + Mockito.when(mockMavenProject.getDescription()).thenReturn("anAwesomeDescription"); + Mockito.when(mockMavenProject.getVersion()).thenReturn("2.0.0"); + Mockito.when(mockMavenProject.getUrl()).thenReturn("https://github.com/tcurdt/jdeb"); + + // Mock System Properties + LinkedHashMap systemMap = new LinkedHashMap(); + systemMap.put("meta.property.system", "${property.three}"); + systemMap.put("property.three", "three"); + systemMap.put("property.two", "two"); + systemMap.put("java.specification.version", "17"); + + Properties mockSystemProperties = new Properties(); + mockSystemProperties.putAll(systemMap); + + // Builder + MapVariableResolver resolver = builder + .withName("test") + .withVersion("2.0.0") + .withMavenProject(mockMavenProject) + .withSystemProperties(mockSystemProperties) + .withBuildDirectory("aDirectory") + .build(); + + // Expected Resolver + assertEquals("anAwesomeArtifactId", resolver.get("artifactId")); + assertEquals(expectedBaseDir.getAbsolutePath(), resolver.get("baseDir")); + assertEquals("aDirectory", resolver.get("buildDir")); + assertEquals("anAwesomeDescription", resolver.get("description")); + assertEquals("deb", resolver.get("extension")); + assertEquals("anAwesomeGroupId", resolver.get("groupId")); + assertEquals("17", resolver.get("java.specification.version")); + assertEquals("1.8", resolver.get("maven.compiler.source")); + assertEquals("1.8", resolver.get("maven.compiler.target")); + assertEquals("one-two-three", resolver.get("meta.property")); + assertEquals("test", resolver.get("name")); + assertEquals("one+one-two-three", resolver.get("nested.meta.property")); + assertEquals("2.0.0", resolver.get("project.version")); + assertEquals("one", resolver.get("property.one")); + assertEquals("three", resolver.get("property.three")); + assertEquals("two", resolver.get("property.two")); + assertEquals("three", resolver.get("meta.property.system")); + assertEquals("https://github.com/tcurdt/jdeb", resolver.get("url")); + assertEquals("2.0.0", resolver.get("version")); + } + + @Test + public void builderWorksWithOptionalData() throws Exception { + MapVariableResolverBuilder builder = MapVariableResolver.builder(); + + // Mock Maven Project + MavenProject mockMavenProject = Mockito.mock(MavenProject.class); + File expectedBaseDir = FileUtils.getTempDirectory(); + Mockito.when(mockMavenProject.getBasedir()).thenReturn(expectedBaseDir); + + Properties mockMavenProperties = new Properties(); + Mockito.when(mockMavenProject.getProperties()).thenReturn(mockMavenProperties); + + Mockito.when(mockMavenProject.getArtifactId()).thenReturn("anAwesomeArtifactId"); + Mockito.when(mockMavenProject.getGroupId()).thenReturn("anAwesomeGroupId"); + Mockito.when(mockMavenProject.getDescription()).thenReturn("anAwesomeDescription"); + Mockito.when(mockMavenProject.getVersion()).thenReturn("2.0.0"); + Mockito.when(mockMavenProject.getUrl()).thenReturn("https://github.com/tcurdt/jdeb"); + + // Optional info + Mockito.when(mockMavenProject.getInceptionYear()).thenReturn("1990"); + + Organization mockOrganization = new Organization(); + mockOrganization.setName("anAwesomeOrganization"); + mockOrganization.setUrl("https://www.awesome.org"); + Mockito.when(mockMavenProject.getOrganization()).thenReturn(mockOrganization); + + // Mock System Properties + Properties mockSystemProperties = new Properties(); + + // Builder + MapVariableResolver resolver = builder + .withName("test") + .withVersion("2.0.0") + .withMavenProject(mockMavenProject) + .withSystemProperties(mockSystemProperties) + .withBuildDirectory("aDirectory") + .build(); + + // Expected Resolver + assertEquals("anAwesomeArtifactId", resolver.get("artifactId")); + assertEquals(expectedBaseDir.getAbsolutePath(), resolver.get("baseDir")); + assertEquals("aDirectory", resolver.get("buildDir")); + assertEquals("anAwesomeDescription", resolver.get("description")); + assertEquals("deb", resolver.get("extension")); + assertEquals("anAwesomeGroupId", resolver.get("groupId")); + assertEquals("test", resolver.get("name")); + assertEquals("2.0.0", resolver.get("project.version")); + assertEquals("https://github.com/tcurdt/jdeb", resolver.get("url")); + assertEquals("2.0.0", resolver.get("version")); + assertEquals("1990", resolver.get("project.inceptionYear")); + assertEquals("anAwesomeOrganization", resolver.get("project.organization.name")); + assertEquals("https://www.awesome.org", resolver.get("project.organization.url")); + } +}