diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3b062f8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: 'maven' + directory: '/' + schedule: + interval: 'daily' + - package-ecosystem: 'github-actions' + # Workflow files stored in the + # default location of `.github/workflows` + directory: '/' + schedule: + interval: 'daily' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6b6c8c3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,102 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: WildFly Launcher - CI + +on: + push: + branches-ignore: + - 'dependabot/**' + paths: + - '.github/workflows/ci.yml' + - '**/pom.xml' + - 'src/**' + pull_request: + paths: + - '.github/workflows/ci.yml' + - '**/pom.xml' + - 'src/**' + schedule: + - cron: '0 0 * * *' # Every day at 00:00 UTC + +# Only run the latest job +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + + build: + name: '${{ matrix.os }}-jdk${{ matrix.java }}' + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ 'ubuntu-latest' , 'windows-latest', 'macos-latest' ] + java: ['17', '21'] + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + cache: 'maven' + distribution: 'temurin' + - name: Build and Test on ${{ matrix.os }} - ${{ matrix.java }} + run: mvn clean install '-Dwildfly.feature.pack.version=' + - name: Upload surefire logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: surefire-reports-${{ matrix.os }}-${{ matrix.java }} + path: '**/surefire-reports/' + - name: Upload failsafe logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: failsafe-reports-${{ matrix.os }}-${{ matrix.java }} + path: '**/failsafe-reports/' + - name: Upload logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: server-logs-${{ matrix.os }}-${{ matrix.java }} + path: '**/*.log' + + build-jdk-11: + name: '${{ matrix.os }}-jdk11' + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ 'ubuntu-latest' , 'windows-latest', 'macos-latest' ] + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + java-version: 11 + cache: 'maven' + distribution: 'temurin' + - name: Build and Test on ${{ matrix.os }} - 11 + run: mvn -B clean install + - name: Upload surefire logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: surefire-reports-${{ matrix.os }}-11 + path: '**/surefire-reports/' + - name: Upload failsafe logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: failsafe-reports-${{ matrix.os }}-11 + path: '**/failsafe-reports/' + - name: Upload logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: server-logs-${{ matrix.os }}-11 + path: '**/*.log' \ No newline at end of file diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml new file mode 100644 index 0000000..8c359f0 --- /dev/null +++ b/.github/workflows/integration-tests.yml @@ -0,0 +1,136 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: WildFly Luancher Integration Tests + +on: + push: + branches: + - '**' + paths: + - '.github/workflows/integration-tests.yml' + - '**/pom.xml' + - 'src/main/**' + - '!src/test/**' + pull_request: + branches: + - '**' + paths: + - '.github/workflows/integration-tests.yml' + - '**/pom.xml' + - 'src/**' + - '!src/test/**' + schedule: + - cron: '0 0 * * *' # Every day at 00:00 UTC + +# Only run the latest job +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + + arquillian-test: + name: WildFly Arquillian Integration Test + runs-on: ubuntu-latest + steps: + - name: Checkout Project + uses: actions/checkout@v4 + with: + path: wildfly-launcher + - name: Set up JDKs + uses: actions/setup-java@v4 + with: + java-version: | + 11 + 17 + 21 + distribution: 'temurin' + architecture: x64 + cache: 'maven' + + - name: Install SNAPSHOT + run: | + cd wildfly-launcher + mvn -B -ntp install -DskipTests + - name: Check out WildFly Arquillian + uses: actions/checkout@v4 + with: + repository: wildfly/wildfly-arquillian + path: wildfly-arquillian + - name: Test WildFly Arquillian + run: | + cd wildfly-arquillian + mvn versions:use-latest-snapshots -DallowSnapshots -Dincludes=org.wildfly.launcher:wildfly-launcher versions:update-properties + git diff + mvn -B -ntp install -Djava11.home=${{env.JAVA_HOME_11_X64}} -Djava17.home=${{env.JAVA_HOME_17_X64}} + + maven-plugin-test: + name: WildFly Maven Plugin Integration Test + runs-on: ubuntu-latest + steps: + - name: Checkout Project + uses: actions/checkout@v4 + with: + path: wildfly-launcher + - name: Set up JDKs + uses: actions/setup-java@v4 + with: + java-version: | + 11 + 17 + 21 + distribution: 'temurin' + architecture: x64 + cache: 'maven' + + - name: Install SNAPSHOT + run: | + cd wildfly-launcher + mvn -B -ntp install -DskipTests + - name: Check out WildFly Maven Plugin + uses: actions/checkout@v4 + with: + repository: wildfly/wildfly-maven-plugin + path: wildfly-maven-plugin + - name: Test the WildFly Maven Plugin + run: | + cd wildfly-maven-plugin + mvn versions:use-latest-snapshots -DallowSnapshots -Dincludes=org.wildfly.launcher:wildfly-launcher versions:update-properties + git diff + mvn -B -ntp install -Djava11.home=${{env.JAVA_HOME_11_X64}} -Djava17.home=${{env.JAVA_HOME_17_X64}} + + wildfly-plugin-tools-test: + name: WildFly Plugin Tools Integration Test + runs-on: ubuntu-latest + steps: + - name: Checkout Project + uses: actions/checkout@v4 + with: + path: wildfly-launcher + - name: Set up JDKs + uses: actions/setup-java@v4 + with: + java-version: | + 11 + 17 + 21 + distribution: 'temurin' + architecture: x64 + cache: 'maven' + + - name: Install SNAPSHOT + run: | + cd wildfly-launcher + mvn -B -ntp install -DskipTests + - name: Check out WildFly Plugin Tools + uses: actions/checkout@v4 + with: + repository: wildfly/wildfly-plugin-tools + path: wildfly-plugin-tools + - name: Test the WildFly Plugin Tools + run: | + cd wildfly-plugin-tools + mvn versions:use-latest-snapshots -DallowSnapshots -Dincludes=org.wildfly.launcher:wildfly-launcher versions:update-properties + git diff + mvn -B -ntp install -Djava11.home=${{env.JAVA_HOME_11_X64}} -Djava17.home=${{env.JAVA_HOME_17_X64}} diff --git a/.github/workflows/wildfly-ci.yml b/.github/workflows/wildfly-ci.yml new file mode 100644 index 0000000..68f0adc --- /dev/null +++ b/.github/workflows/wildfly-ci.yml @@ -0,0 +1,102 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: WildFly Launcher Integration - CI + +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - '**' + paths: + - '.github/workflows/wildfly-ci.yml' + schedule: + - cron: '0 0 * * *' # Every day at 00:00 UTC + +# Only run the latest job +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + + wildfly-test-and-build: + name: '${{ matrix.os }}-jdk${{ matrix.java }}' + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ 'ubuntu-latest' , 'windows-latest' ] + java: ['17', '21', '24-ea'] + + steps: + - uses: actions/checkout@v4 + - uses: wildfly-extras/wildfly-nightly-download@v1 + id: wildfly-nightly + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + cache: 'maven' + distribution: 'temurin' + - name: Build and Test on ${{ matrix.os }} - ${{ matrix.java }} with WildFly ${{steps.wildfly-nightly.outputs.wildfly-version}} + run: mvn clean install '-Dwildfly.feature.pack.version=${{steps.wildfly-nightly.outputs.wildfly-version}}' + - name: Upload surefire logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: surefire-reports-${{ matrix.os }}-${{ matrix.java }} + path: '**/surefire-reports/' + - name: Upload failsafe logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: failsafe-reports-${{ matrix.os }}-${{ matrix.java }} + path: '**/failsafe-reports/' + - name: Upload logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: server-logs-${{ matrix.os }}-${{ matrix.java }} + path: '**/*.log' + + legacy-test-and-build: + name: 'legacy-${{ matrix.os }}-jdk${{ matrix.java }}' + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ 'ubuntu-latest' , 'windows-latest' ] + java: ['11', '17', '21'] + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + cache: 'maven' + distribution: 'temurin' + - name: Build and Test on ${{ matrix.os }} - ${{ matrix.java }} with WildFly 32.0.0.Final + # 32.0.0.Final was the first version which deployed channels + run: mvn clean install '-Dwildfly.feature.pack.version=32.0.0.Final' + - name: Upload surefire logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: legacy-surefire-reports-${{ matrix.os }}-${{ matrix.java }} + path: '**/surefire-reports/' + - name: Upload failsafe logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: legacy-failsafe-reports-${{ matrix.os }}-${{ matrix.java }} + path: '**/failsafe-reports/' + - name: Upload logs for failed run + uses: actions/upload-artifact@v4 + if: failure() + with: + name: legacy-server-logs-${{ matrix.os }}-${{ matrix.java }} + path: '**/*.log' \ No newline at end of file diff --git a/pom.xml b/pom.xml index b090c62..5a05324 100644 --- a/pom.xml +++ b/pom.xml @@ -40,8 +40,8 @@ git@github.com:wildfly/wildfly-launcher.git https://github.com/wildfly/wildfly-launcher - ${basedir}/target/fake-wildfly - ${basedir}/target/fake-wildfly.jar + wildfly-bootable.jar + ${basedir}/target/${wildfly.bootable.jar.name} 11 @@ -52,6 +52,7 @@ 1.0.8.Final + 5.1.0.Alpha1 3.6.1.Final @@ -59,6 +60,20 @@ 2.1.5.Final 5.11.3 + + + true + + + ${project.build.directory}/server + + org.wildfly + wildfly-ee-galleon-pack + 34.0.0.Final + + org.wildfly.channels + wildfly-ee + ${wildfly.feature.pack.version} @@ -113,6 +128,19 @@ ${version.org.jboss.modules.jboss-modules} test + + org.wildfly.plugins + wildfly-plugin-tools + 1.2.0.Beta1 + test + + + + org.wildfly.core + wildfly-launcher + + + @@ -163,7 +191,6 @@ checkstyle-suppressions.xml true **/*$logger.java,**/*$bundle.java - @@ -225,11 +252,65 @@ maven-surefire-plugin + ${jboss.home} ${wildfly.launcher.home} ${wildfly.launcher.bootable.jar} + + org.wildfly.plugins + wildfly-maven-plugin + ${version.org.wildfly.plugins.wildfly-maven-plugin} + + ${jboss.home} + ${jboss.home} + + + ${wildfly.feature.pack.groupId} + ${wildfly.feature.pack.artifactId} + ${wildfly.feature.pack.version} + + + + + + + ${wildfly.channel.manifest.groupId} + ${wildfly.channel.manifest.artifactId} + ${wildfly.channel.manifest.version} + + + + + + + + + + provision-test-server + process-test-classes + + provision + + + + provision-bootable-jar + process-test-classes + + package + + + ${project.build.directory}/bootable-jar-working + true + ${wildfly.bootable.jar.name} + true + + + + diff --git a/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java b/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java index ff88ca9..6f13284 100644 --- a/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java +++ b/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java @@ -9,8 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; @@ -18,7 +16,6 @@ import java.util.List; import org.junit.jupiter.api.Test; - import org.wildfly.core.launcher.Arguments.Argument; /** @@ -30,17 +27,8 @@ class CommandBuilderTest { private static final Path WILDFLY_BOOTABLE_JAR; static { - WILDFLY_HOME = Paths.get(System.getProperty("wildfly.launcher.home")).toAbsolutePath().normalize(); + WILDFLY_HOME = Paths.get(System.getProperty("jboss.home")).toAbsolutePath().normalize(); WILDFLY_BOOTABLE_JAR = Paths.get(System.getProperty("wildfly.launcher.bootable.jar")).toAbsolutePath().normalize(); - - // Create some default directories and empty bootable fake jar file - try { - Files.createFile(WILDFLY_BOOTABLE_JAR); - Files.createDirectories(WILDFLY_HOME.resolve("modules")); - Files.createDirectories(WILDFLY_HOME.resolve("configuration")); - Files.createDirectories(WILDFLY_HOME.resolve("data")); - } catch (IOException ignore) { - } } @Test diff --git a/src/test/java/org/wildfly/core/launcher/ServerLauncherTest.java b/src/test/java/org/wildfly/core/launcher/ServerLauncherTest.java new file mode 100644 index 0000000..95229af --- /dev/null +++ b/src/test/java/org/wildfly/core/launcher/ServerLauncherTest.java @@ -0,0 +1,174 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.wildfly.core.launcher; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; +import org.wildfly.plugin.tools.ConsoleConsumer; +import org.wildfly.plugin.tools.ContainerDescription; +import org.wildfly.plugin.tools.server.ServerManager; + +/** + * @author James R. Perkins + */ +class ServerLauncherTest { + private static final Path JBOSS_HOME = Path.of(System.getProperty("jboss.home")); + private static final Path BOOTABLE_JAR = Path.of(System.getProperty("wildfly.launcher.bootable.jar")); + + + @TestTemplate + @ExtendWith(ServerLaunchTestTemplateInvocationContextProvider.class) + void launch(final CommandBuilder commandBuilder, final long timeout, final TestInfo testInfo) throws Exception { + final Launcher launcher = Launcher.of(commandBuilder); + Process process = null; + try { + process = launcher.launch(); + final Process capturedProcess = process; + final CapturingOutputStream out = new CapturingOutputStream(); + ConsoleConsumer.start(process, out); + Assertions.assertTrue(process.isAlive(), () -> String.format("The process has terminated: %d - %s", capturedProcess.exitValue(), out)); + try ( + ServerManager serverManager = ServerManager.builder() + .process(process) + .shutdownOnClose(true) + .build() + .get(timeout, TimeUnit.SECONDS) + ) { + ConsoleConsumer.start(process, out); + Assertions.assertTrue(serverManager.waitFor(timeout, TimeUnit.SECONDS), () -> String.format("Failed to start %s within %d seconds. Process: %s%n%s", testInfo.getDisplayName(), timeout, capturedProcess, + out)); + Assertions.assertTrue(serverManager.isRunning(), () -> String.format("Server %s is not running. Process: %s%n%s", testInfo.getDisplayName(), capturedProcess, + out)); + final ContainerDescription description = serverManager.containerDescription(); + if (commandBuilder instanceof StandaloneCommandBuilder || commandBuilder instanceof BootableJarCommandBuilder) { + Assertions.assertFalse(description.isDomain(), () -> String.format("Expected the server to not be a domain server: " + description)); + } else { + Assertions.assertTrue(description.isDomain(), () -> String.format("Expected the server to be a domain server: " + description)); + } + } catch (Throwable e) { + Assertions.fail(String.format("Failed starting %s: %s", testInfo.getDisplayName(), out), e); + } + } finally { + if (process != null) { + process.destroyForcibly(); + } + } + } + + public static class ServerLaunchTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider { + + @Override + public boolean supportsTestTemplate(final ExtensionContext context) { + return true; + } + + @Override + public Stream provideTestTemplateInvocationContexts(final ExtensionContext context) { + return Stream.of(createTestContext("Standalone", StandaloneCommandBuilder.of(JBOSS_HOME), 60L), + createTestContext("Domain", DomainCommandBuilder.of(JBOSS_HOME), 60L), + createTestContext("Bootable JAR", BootableJarCommandBuilder.of(BOOTABLE_JAR), 60L) + ); + } + + @SuppressWarnings("SameParameterValue") + private TestTemplateInvocationContext createTestContext(final String name, final CommandBuilder commandBuilder, final long timeout) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(final int invocationIndex) { + return name; + } + + @Override + public List getAdditionalExtensions() { + return List.of( + new ParameterResolver() { + @Override + public boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException { + return parameterContext.getParameter().getType() == CommandBuilder.class; + } + + @Override + public Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException { + return commandBuilder; + } + }, + new ParameterResolver() { + @Override + public boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException { + return parameterContext.getParameter().getType() == long.class; + } + + @Override + public Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException { + return timeout; + } + } + ); + } + }; + } + } + + private static class CapturingOutputStream extends ByteArrayOutputStream { + @Override + public synchronized void write(final int b) { + try { + super.write(b); + } finally { + System.out.write(b); + } + } + + @Override + public synchronized void write(final byte[] b, final int off, final int len) { + try { + super.write(b, off, len); + } finally { + System.out.write(b, off, len); + } + } + + @Override + public synchronized void write(final byte[] b) throws IOException { + try { + super.write(b); + } finally { + System.out.write(b); + } + } + + @Override + public void flush() throws IOException { + try { + super.flush(); + } finally { + System.out.flush(); + } + } + + @Override + public synchronized String toString() { + return super.toString(StandardCharsets.UTF_8); + } + } +}