Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for rpm embedded lua interpreter #66

Merged
merged 3 commits into from
Sep 10, 2024
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
44 changes: 30 additions & 14 deletions rpm/src/main/java/org/eclipse/packager/rpm/build/RpmBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
Expand Down Expand Up @@ -582,6 +583,12 @@ protected void customizeSymbolicLink(final FileEntry entry, final FileInformatio

private static final String DEFAULT_INTERPRETER = "/bin/sh";

private static final String EMBEDDED_LUA_INTERPRETER = "<lua>";

private static final String EMBEDDED_LUA_INTERPRETER_REQUIREMENT_NAME = "rpmlib(BuiltinLuaScripts)";

private static final String EMBEDDED_LUA_INTERPRETER_REQUIREMENT_VERSION = "4.2.2-1";

protected final Header<RpmTag> header = new Header<>();

private final String name;
Expand Down Expand Up @@ -1178,8 +1185,7 @@ public void addSymbolicLink(final String targetName, final String linkTo, final
}

public void setPreInstallationScript(final String interpreter, final String script) {
addRequirement(interpreter, null, RpmDependencyFlags.INTERPRETER);
addRequirement(interpreter, null, RpmDependencyFlags.SCRIPT_PRE, RpmDependencyFlags.INTERPRETER);
addInterpreterRequirement(interpreter, RpmDependencyFlags.SCRIPT_PRE);
setScript(RpmTag.PREINSTALL_SCRIPT_PROG, RpmTag.PREINSTALL_SCRIPT, interpreter, script);
}

Expand All @@ -1188,8 +1194,7 @@ public void setPreInstallationScript(final String script) {
}

public void setPostInstallationScript(final String interpreter, final String script) {
addRequirement(interpreter, null, RpmDependencyFlags.INTERPRETER);
addRequirement(interpreter, null, RpmDependencyFlags.SCRIPT_POST, RpmDependencyFlags.INTERPRETER);
addInterpreterRequirement(interpreter, RpmDependencyFlags.SCRIPT_POST);
setScript(RpmTag.POSTINSTALL_SCRIPT_PROG, RpmTag.POSTINSTALL_SCRIPT, interpreter, script);
}

Expand All @@ -1198,8 +1203,7 @@ public void setPostInstallationScript(final String script) {
}

public void setPreRemoveScript(final String interpreter, final String script) {
addRequirement(interpreter, null, RpmDependencyFlags.INTERPRETER);
addRequirement(interpreter, null, RpmDependencyFlags.SCRIPT_PREUN, RpmDependencyFlags.INTERPRETER);
addInterpreterRequirement(interpreter, RpmDependencyFlags.SCRIPT_PREUN);
setScript(RpmTag.PREREMOVE_SCRIPT_PROG, RpmTag.PREREMOVE_SCRIPT, interpreter, script);
}

Expand All @@ -1208,8 +1212,7 @@ public void setPreRemoveScript(final String script) {
}

public void setPostRemoveScript(final String interpreter, final String script) {
addRequirement(interpreter, null, RpmDependencyFlags.INTERPRETER);
addRequirement(interpreter, null, RpmDependencyFlags.SCRIPT_POSTUN, RpmDependencyFlags.INTERPRETER);
addInterpreterRequirement(interpreter, RpmDependencyFlags.SCRIPT_POSTUN);
setScript(RpmTag.POSTREMOVE_SCRIPT_PROG, RpmTag.POSTREMOVE_SCRIPT, interpreter, script);
}

Expand All @@ -1218,8 +1221,7 @@ public void setPostRemoveScript(final String script) {
}

public void setVerifyScript(final String interpreter, final String script) {
addRequirement(interpreter, null, RpmDependencyFlags.INTERPRETER);
addRequirement(interpreter, null, RpmDependencyFlags.SCRIPT_VERIFY, RpmDependencyFlags.INTERPRETER);
addInterpreterRequirement(interpreter, RpmDependencyFlags.SCRIPT_VERIFY);
setScript(RpmTag.VERIFY_SCRIPT_PROG, RpmTag.VERIFY_SCRIPT, interpreter, script);
}

Expand All @@ -1228,8 +1230,7 @@ public void setVerifyScript(final String script) {
}

public void setPreTransactionScript(final String interpreter, final String script) {
addRequirement(interpreter, null, RpmDependencyFlags.INTERPRETER);
addRequirement(interpreter, null, RpmDependencyFlags.SCRIPT_PREUN, RpmDependencyFlags.INTERPRETER);
addInterpreterRequirement(interpreter, RpmDependencyFlags.PRETRANS);
setScript(RpmTag.PRETRANSACTION_SCRIPT_PROG, RpmTag.PRETRANSACTION_SCRIPT, interpreter, script);
}

Expand All @@ -1238,8 +1239,7 @@ public void setPreTransactionScript(final String script) {
}

public void setPostTransactionScript(final String interpreter, final String script) {
addRequirement(interpreter, null, RpmDependencyFlags.INTERPRETER);
addRequirement(interpreter, null, RpmDependencyFlags.SCRIPT_POSTUN, RpmDependencyFlags.INTERPRETER);
addInterpreterRequirement(interpreter, RpmDependencyFlags.POSTTRANS);
setScript(RpmTag.POSTTRANSACTION_SCRIPT_PROG, RpmTag.POSTTRANSACTION_SCRIPT, interpreter, script);
}

Expand All @@ -1257,6 +1257,22 @@ private void setScript(final RpmTag interpreterTag, final RpmTag scriptTag, fina
}
}

private void addInterpreterRequirement(final String interpreter, RpmDependencyFlags scriptPhaseFlag) {
if (isEmbeddedLuaInterpreter(interpreter)) {
addRequirement(EMBEDDED_LUA_INTERPRETER_REQUIREMENT_NAME, EMBEDDED_LUA_INTERPRETER_REQUIREMENT_VERSION,
RpmDependencyFlags.INTERPRETER, RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB);
addRequirement(EMBEDDED_LUA_INTERPRETER_REQUIREMENT_NAME, EMBEDDED_LUA_INTERPRETER_REQUIREMENT_VERSION,
scriptPhaseFlag, RpmDependencyFlags.INTERPRETER, RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB);
} else {
addRequirement(interpreter, null, RpmDependencyFlags.INTERPRETER);
addRequirement(interpreter, null, scriptPhaseFlag, RpmDependencyFlags.INTERPRETER);
}
}

private boolean isEmbeddedLuaInterpreter(final String interpreter) {
return Objects.equals(interpreter, EMBEDDED_LUA_INTERPRETER);
}

/**
* Return the minimum required version of RPM for supporting all features of
* this generated RPM.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public enum RpmDependencyFlags {
LESS(1),
GREATER(2),
EQUAL(3),
POSTTRANS(5),
PREREQ(6),
PRETRANS(7),
INTERPRETER(8),
Expand Down
183 changes: 183 additions & 0 deletions rpm/src/test/java/org/eclipse/packager/rpm/ScriptletWriterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.packager.rpm;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import org.eclipse.packager.rpm.build.RpmBuilder;
import org.eclipse.packager.rpm.deps.Dependency;
import org.eclipse.packager.rpm.deps.RpmDependencyFlags;
import org.eclipse.packager.rpm.parse.RpmInputStream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

public class ScriptletWriterTest {

@TempDir
private Path outBase;

@DisplayName("Test single scriptlet setter with different interpreters")
@ParameterizedTest(name = "[{index}] {1} with \"{3}\" interpreter")
@MethodSource({"scriptSource"})
void singleScriptTest(ScriptletConsumer scriptConsumer, RpmTag scriptTag, RpmTag interpreterTag, String interpreter, String script, List<Dependency> expectedRequiredDependencies) throws IOException {
final Path outFile;

try (final RpmBuilder builder = new RpmBuilder("singleScriptTest", "1.0.0", "1", "noarch", outBase)) {
outFile = builder.getTargetFile();
scriptConsumer.accept(builder, interpreter, script);
builder.build();
}

try (final RpmInputStream in = new RpmInputStream(new BufferedInputStream(Files.newInputStream(outFile)))) {
List<Dependency> requiredDependencies = readRequiredDependencies(in);
String resultScript = readTag(in, scriptTag);
String resultInterpreter = readTag(in, interpreterTag);

assertThat(requiredDependencies).containsExactlyElementsOf(expectedRequiredDependencies);
assertThat(resultScript).isEqualTo(script);
assertThat(resultInterpreter).isEqualTo(interpreter);
}
}

@DisplayName("Test multiple scriptlet setter at once with different interpreters")
@Test
void combinedScriptTest() throws IOException {
final String luaInterpreter = "<lua>";
final String customInterpreter = "/bin/custom/interpreter";
final String shellInterpreter = "/bin/sh";
Path outFile;

List<Dependency> expectedRequiredDependencies = List.of(
new Dependency("rpmlib(BuiltinLuaScripts)", "4.2.2-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB, RpmDependencyFlags.INTERPRETER),
new Dependency("rpmlib(BuiltinLuaScripts)", "4.2.2-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB, RpmDependencyFlags.INTERPRETER, RpmDependencyFlags.SCRIPT_PRE),
new Dependency("rpmlib(BuiltinLuaScripts)", "4.2.2-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB, RpmDependencyFlags.INTERPRETER, RpmDependencyFlags.PRETRANS),
new Dependency(customInterpreter, "", RpmDependencyFlags.INTERPRETER),
new Dependency(customInterpreter, "", RpmDependencyFlags.INTERPRETER, RpmDependencyFlags.SCRIPT_POST),
new Dependency(shellInterpreter, "", RpmDependencyFlags.INTERPRETER),
new Dependency(shellInterpreter, "", RpmDependencyFlags.INTERPRETER, RpmDependencyFlags.SCRIPT_POSTUN),
new Dependency(shellInterpreter, "", RpmDependencyFlags.INTERPRETER, RpmDependencyFlags.POSTTRANS),
new Dependency("rpmlib(CompressedFileNames)", "3.0.4-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB),
new Dependency("rpmlib(PayloadFilesHavePrefix)", "4.0-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB));

try (final RpmBuilder builder = new RpmBuilder("combinedScriptTest", "1.0.0", "1", "noarch", outBase)) {
outFile = builder.getTargetFile();
builder.setPreTransactionScript(luaInterpreter, "my pretransaction script");
builder.setPreInstallationScript(luaInterpreter, "my preinstallation script");
builder.setPostInstallationScript(customInterpreter, "my postinstallation script");
builder.setPostRemoveScript("my postremove script");
builder.setPostTransactionScript("my posttransaction script");
builder.build();
}

try (final RpmInputStream in = new RpmInputStream(new BufferedInputStream(Files.newInputStream(outFile)))) {
List<Dependency> requiredDependencies = readRequiredDependencies(in);
assertThat(requiredDependencies).containsExactlyInAnyOrderElementsOf(expectedRequiredDependencies);

assertThat(readTag(in, RpmTag.PRETRANSACTION_SCRIPT)).isEqualTo("my pretransaction script");
assertThat(readTag(in, RpmTag.PRETRANSACTION_SCRIPT_PROG)).isEqualTo(luaInterpreter);

assertThat(readTag(in, RpmTag.PREINSTALL_SCRIPT)).isEqualTo("my preinstallation script");
assertThat(readTag(in, RpmTag.PREINSTALL_SCRIPT_PROG)).isEqualTo(luaInterpreter);

assertThat(readTag(in, RpmTag.POSTINSTALL_SCRIPT)).isEqualTo("my postinstallation script");
assertThat(readTag(in, RpmTag.POSTINSTALL_SCRIPT_PROG)).isEqualTo(customInterpreter);

assertThat(readTag(in, RpmTag.POSTREMOVE_SCRIPT)).isEqualTo("my postremove script");
assertThat(readTag(in, RpmTag.POSTREMOVE_SCRIPT_PROG)).isEqualTo(shellInterpreter);

assertThat(readTag(in, RpmTag.POSTTRANSACTION_SCRIPT)).isEqualTo("my posttransaction script");
assertThat(readTag(in, RpmTag.POSTTRANSACTION_SCRIPT_PROG)).isEqualTo(shellInterpreter);
}
}

public static Stream<Arguments> scriptSource() {
final String lua = "<lua>";
final String shell = "/bin/sh";
return Stream.of(
// lua interpreter
Arguments.of((ScriptletConsumer) RpmBuilder::setPreInstallationScript, RpmTag.PREINSTALL_SCRIPT, RpmTag.PREINSTALL_SCRIPT_PROG, lua, "my preinstall script", simpleLuaDependencyFor(RpmDependencyFlags.SCRIPT_PRE)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPostInstallationScript, RpmTag.POSTINSTALL_SCRIPT, RpmTag.POSTINSTALL_SCRIPT_PROG, lua, "my postinstall script", simpleLuaDependencyFor(RpmDependencyFlags.SCRIPT_POST)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPreRemoveScript, RpmTag.PREREMOVE_SCRIPT, RpmTag.PREREMOVE_SCRIPT_PROG, lua, "my preremove script", simpleLuaDependencyFor(RpmDependencyFlags.SCRIPT_PREUN)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPostRemoveScript, RpmTag.POSTREMOVE_SCRIPT, RpmTag.POSTREMOVE_SCRIPT_PROG, lua, "my postremove script", simpleLuaDependencyFor(RpmDependencyFlags.SCRIPT_POSTUN)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPreTransactionScript, RpmTag.PRETRANSACTION_SCRIPT, RpmTag.PRETRANSACTION_SCRIPT_PROG, lua, "my pretransaction script", simpleLuaDependencyFor(RpmDependencyFlags.PRETRANS)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPostTransactionScript, RpmTag.POSTTRANSACTION_SCRIPT, RpmTag.POSTTRANSACTION_SCRIPT_PROG, lua, "my posttransaction script", simpleLuaDependencyFor(RpmDependencyFlags.POSTTRANS)),
Arguments.of((ScriptletConsumer) RpmBuilder::setVerifyScript, RpmTag.VERIFY_SCRIPT, RpmTag.VERIFY_SCRIPT_PROG, lua, "my verify script", simpleLuaDependencyFor(RpmDependencyFlags.SCRIPT_VERIFY)),
// shell interpreter
Arguments.of((ScriptletConsumer) RpmBuilder::setPreInstallationScript, RpmTag.PREINSTALL_SCRIPT, RpmTag.PREINSTALL_SCRIPT_PROG, shell, "my preinstall script", simpleInterpreterDependencyFor(shell, RpmDependencyFlags.SCRIPT_PRE)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPostInstallationScript, RpmTag.POSTINSTALL_SCRIPT, RpmTag.POSTINSTALL_SCRIPT_PROG, shell, "my postinstall script", simpleInterpreterDependencyFor(shell, RpmDependencyFlags.SCRIPT_POST)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPreRemoveScript, RpmTag.PREREMOVE_SCRIPT, RpmTag.PREREMOVE_SCRIPT_PROG, shell, "my preremove script", simpleInterpreterDependencyFor(shell, RpmDependencyFlags.SCRIPT_PREUN)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPostRemoveScript, RpmTag.POSTREMOVE_SCRIPT, RpmTag.POSTREMOVE_SCRIPT_PROG, shell, "my postremove script", simpleInterpreterDependencyFor(shell, RpmDependencyFlags.SCRIPT_POSTUN)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPreTransactionScript, RpmTag.PRETRANSACTION_SCRIPT, RpmTag.PRETRANSACTION_SCRIPT_PROG, shell, "my pretransaction script", simpleInterpreterDependencyFor(shell, RpmDependencyFlags.PRETRANS)),
Arguments.of((ScriptletConsumer) RpmBuilder::setPostTransactionScript, RpmTag.POSTTRANSACTION_SCRIPT, RpmTag.POSTTRANSACTION_SCRIPT_PROG, shell, "my posttransaction script", simpleInterpreterDependencyFor(shell, RpmDependencyFlags.POSTTRANS)),
Arguments.of((ScriptletConsumer) RpmBuilder::setVerifyScript, RpmTag.VERIFY_SCRIPT, RpmTag.VERIFY_SCRIPT_PROG, shell, "my verify script", simpleInterpreterDependencyFor(shell, RpmDependencyFlags.SCRIPT_VERIFY))
);
}

public static List<Dependency> simpleLuaDependencyFor(RpmDependencyFlags scriptletPhase) {
return List.of(
new Dependency("rpmlib(BuiltinLuaScripts)", "4.2.2-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB, RpmDependencyFlags.INTERPRETER),
new Dependency("rpmlib(BuiltinLuaScripts)", "4.2.2-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB, RpmDependencyFlags.INTERPRETER, scriptletPhase),
new Dependency("rpmlib(CompressedFileNames)", "3.0.4-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB),
new Dependency("rpmlib(PayloadFilesHavePrefix)", "4.0-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB));
}

public static List<Dependency> simpleInterpreterDependencyFor(String interpreter, RpmDependencyFlags scriptletPhase) {
return List.of(
new Dependency(interpreter, "", RpmDependencyFlags.INTERPRETER),
new Dependency(interpreter, "", RpmDependencyFlags.INTERPRETER, scriptletPhase),
new Dependency("rpmlib(CompressedFileNames)", "3.0.4-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB),
new Dependency("rpmlib(PayloadFilesHavePrefix)", "4.0-1", RpmDependencyFlags.LESS, RpmDependencyFlags.EQUAL, RpmDependencyFlags.RPMLIB));
}


@FunctionalInterface
interface ScriptletConsumer {
void accept(RpmBuilder rpmBuilder, String interpreter, String script);
}

private static List<Dependency> readRequiredDependencies(final RpmInputStream in) throws IOException {
return readGroup(in, RpmTag.REQUIRE_NAME, RpmTag.REQUIRE_VERSION, RpmTag.REQUIRE_FLAGS);
}

private static String readTag(final RpmInputStream in, final RpmTag tag) throws IOException {
return new RpmTagValue(in.getPayloadHeader().getTag(tag)).asString().orElse(null);
}

private static List<Dependency> readGroup(final RpmInputStream in, final RpmTag nameTag, final RpmTag versionTag, final RpmTag flagTag) throws IOException {
final String[] names = new RpmTagValue(in.getPayloadHeader().getTag(nameTag)).asStringArray().orElse(null);
final String[] versions = new RpmTagValue(in.getPayloadHeader().getTag(versionTag)).asStringArray().orElse(null);
final Integer[] flags = new RpmTagValue(in.getPayloadHeader().getTag(flagTag)).asIntegerArray().orElse(null);

List<Dependency> dependencies = new ArrayList<>();
if (names == null) {
return dependencies;
}
for (int i = 0; i < names.length; i++) {
dependencies.add(new Dependency(names[i], versions[i], RpmDependencyFlags.parse(flags[i])));
}
return dependencies;
}

}