From 343b0dda7ac1edd86a25c492fb4d1ceea9794287 Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sat, 3 Oct 2026 12:07:35 +0200 Subject: [PATCH 01/10] Updated to Java 27 - Compact Object Headers are on by default, so the jre creation was simplified - C1 is the new GC, and for the bot usage should be better than Generational Shenandoah --- .github/workflows/unit-test.yml | 2 +- Dockerfile | 2 +- README.md | 4 ++-- build.gradle.kts | 17 ++++------------- buildSrc/build.gradle.kts | 2 +- gradle/gradle-daemon-jvm.properties | 21 ++++++++++----------- qodana.yaml | 2 +- 7 files changed, 20 insertions(+), 30 deletions(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index b0b89c7a..b4cebc9d 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -49,7 +49,7 @@ jobs: uses: actions/setup-java@v6 with: distribution: temurin - java-version: 26 + java-version: 27 - name: Setup project and upload dependency graph uses: gradle/actions/setup-gradle@v6 diff --git a/Dockerfile b/Dockerfile index c445c4aa..ba744cbd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:26-alpine AS builder +FROM eclipse-temurin:27-alpine AS builder WORKDIR /app diff --git a/README.md b/README.md index 7dbba195..5121525f 100644 --- a/README.md +++ b/README.md @@ -55,11 +55,11 @@ And it's done! ## How to set up the project -1. Install JDK 26 (or higher) +1. Install JDK 27 (or higher) 2. Clone the project and move into its folder 3. Run the command `./gradlew build -x test` 4. Import the project inside your IDE as a Gradle project -5. Ensure your IDE is correctly configured to use a Java 26 (or higher) JDK +5. Ensure your IDE is correctly configured to use a Java 27 (or higher) JDK ## How to run the bot locally diff --git a/build.gradle.kts b/build.gradle.kts index aa283d14..d30a0046 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,6 @@ import com.github.stickerifier.stickerify.JlinkTask import com.github.stickerifier.stickerify.JunitSeedArgumentProvider import io.spring.gradle.nullability.NullabilityOptions import org.gradle.internal.buildconfiguration.DaemonJvmPropertiesConfigurator -import org.gradle.kotlin.dsl.support.serviceOf plugins { java @@ -39,19 +38,19 @@ version = "2.0" description = "Telegram bot to convert medias into the format required to be used as Telegram stickers" java.toolchain { - languageVersion = JavaLanguageVersion.of(26) + languageVersion = JavaLanguageVersion.of(27) vendor = JvmVendorSpec.ADOPTIUM } tasks.named(DaemonJvmPropertiesConfigurator.TASK_NAME) { - languageVersion = JavaLanguageVersion.of(26) + languageVersion = JavaLanguageVersion.of(27) vendor = JvmVendorSpec.ADOPTIUM } val jlink = tasks.register("jlink") { description = "Generates a minimal JRE for the project with compact object headers archive." - options = listOf("--strip-debug", "--no-header-files", "--no-man-pages", "--ignore-modified-runtime") + options = listOf("--strip-debug", "--no-header-files", "--no-man-pages", "--ignore-modified-runtime", "--generate-cds-archive") modules = listOf( "java.instrument", // for JUnit "java.naming", // for Logback @@ -61,14 +60,6 @@ val jlink = tasks.register("jlink") { ) includeModulePath = false javaCompiler = javaToolchains.compilerFor(java.toolchain) - - val execOps = serviceOf() - doLast { - val javaExe = outputDirectory.file("jre/bin/java").get().asFile.absolutePath - execOps.exec { - commandLine(javaExe, "-XX:+UseCompactObjectHeaders", "-Xshare:dump") - } - } } val CompileOptions.nullability: NullabilityOptions @@ -99,7 +90,7 @@ tasks.test { application { mainClass = "com.github.stickerifier.stickerify.runner.Main" - applicationDefaultJvmArgs = listOf("-XX:+UseCompactObjectHeaders", "-XX:+UseShenandoahGC", "-XX:ShenandoahGCMode=generational", "--enable-final-field-mutation=ALL-UNNAMED") + applicationDefaultJvmArgs = listOf("--enable-final-field-mutation=ALL-UNNAMED") } distributions { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index cec700b6..d20ccc3f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -7,6 +7,6 @@ repositories { } java.toolchain { - languageVersion = JavaLanguageVersion.of(26) + languageVersion = JavaLanguageVersion.of(27) vendor = JvmVendorSpec.ADOPTIUM } diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties index 59adfd73..64f3d710 100644 --- a/gradle/gradle-daemon-jvm.properties +++ b/gradle/gradle-daemon-jvm.properties @@ -1,12 +1,11 @@ -#This file is generated by updateDaemonJvm -toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/39e8ce74cee3e629d6fade20d237fff5/redirect -toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/b7f6da648647bf05d05a14cdca409f73/redirect -toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/39e8ce74cee3e629d6fade20d237fff5/redirect -toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/b7f6da648647bf05d05a14cdca409f73/redirect -toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/b5e76458e870841069679392d5855834/redirect -toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/ef59bf670a5688da71dc8f2e9d1f8b76/redirect -toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/39e8ce74cee3e629d6fade20d237fff5/redirect -toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/b7f6da648647bf05d05a14cdca409f73/redirect -toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/06340fb34ff0b1b2e0cde9562b75744e/redirect +#This file is generated by :updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/22fd4fd9a5dedf12964be0a987f4e90b/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/c753409d258f3e09db9cfda022a8b52a/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/22fd4fd9a5dedf12964be0a987f4e90b/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/c753409d258f3e09db9cfda022a8b52a/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7857181a003a2064a9272111de852844/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/22fd4fd9a5dedf12964be0a987f4e90b/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/c753409d258f3e09db9cfda022a8b52a/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/36c6fff412e42f23a647ccc8ac1ee8fe/redirect toolchainVendor=ADOPTIUM -toolchainVersion=26 +toolchainVersion=27 diff --git a/qodana.yaml b/qodana.yaml index a4e9751c..4199a154 100644 --- a/qodana.yaml +++ b/qodana.yaml @@ -5,4 +5,4 @@ include: - name: VulnerableLibrariesGlobal # bump: Qodana /qodana-jvm-community:([\d.]+)/ docker:jetbrains/qodana-jvm-community|/^[\d]{4}\.\d+$/|sort linter: jetbrains/qodana-jvm-community:2026.2 -projectJDK: temurin-26 +projectJDK: temurin-27 From d5bd90a128da4cd422b9968122b7d173cad7262d Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sat, 3 Oct 2026 12:08:07 +0200 Subject: [PATCH 02/10] Added DEFAULT_TASK_NAME to JlinkTask --- build.gradle.kts | 2 +- .../main/java/com/github/stickerifier/stickerify/JlinkTask.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index d30a0046..acff4310 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -47,7 +47,7 @@ tasks.named(DaemonJvmPropertiesConfigurator.TASK_NAME) { vendor = JvmVendorSpec.ADOPTIUM } -val jlink = tasks.register("jlink") { +val jlink = tasks.register(JlinkTask.DEFAULT_TASK_NAME) { description = "Generates a minimal JRE for the project with compact object headers archive." options = listOf("--strip-debug", "--no-header-files", "--no-man-pages", "--ignore-modified-runtime", "--generate-cds-archive") diff --git a/buildSrc/src/main/java/com/github/stickerifier/stickerify/JlinkTask.java b/buildSrc/src/main/java/com/github/stickerifier/stickerify/JlinkTask.java index 318c7f9d..639ccf59 100644 --- a/buildSrc/src/main/java/com/github/stickerifier/stickerify/JlinkTask.java +++ b/buildSrc/src/main/java/com/github/stickerifier/stickerify/JlinkTask.java @@ -21,6 +21,8 @@ public abstract class JlinkTask extends DefaultTask { + public static final String DEFAULT_TASK_NAME = "jlink"; + @Input public abstract ListProperty<@NotNull String> getOptions(); From ec5acfda20f0813312112256bab3bb523d91a014 Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sat, 3 Oct 2026 12:08:30 +0200 Subject: [PATCH 03/10] Updated customWindowsStartScript.txt to latest version --- .../resources/customWindowsStartScript.txt | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/resources/customWindowsStartScript.txt b/src/main/resources/customWindowsStartScript.txt index 3e1884ee..98ef3b1c 100644 --- a/src/main/resources/customWindowsStartScript.txt +++ b/src/main/resources/customWindowsStartScript.txt @@ -1,4 +1,4 @@ -@rem from https://github.com/gradle/gradle/blob/41203b0be36cabf983b0ebe9e402e1f37a67b0cc/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/windowsStartScript.txt +@rem from https://github.com/gradle/gradle/blob/fc7a4c9710a3df2c33a439fff4799bce057d89f9/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/windowsStartScript.txt @rem @rem Copyright 2015 the original author or authors. @@ -51,13 +51,14 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +1>&2 echo. +1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +1>&2 echo. +1>&2 echo Please set the JAVA_HOME variable in your environment to match the +1>&2 echo location of your Java installation. "%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,13 +66,14 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +1>&2 echo. +1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +1>&2 echo. +1>&2 echo Please set the JAVA_HOME variable in your environment to match the +1>&2 echo location of your Java installation. "%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :execute @rem Setup the command line @@ -84,8 +86,9 @@ set CLASSPATH=$classpath @rem Execute ${applicationName} @rem endlocal doesn't take effect until after the line is parsed and variables are expanded @rem which allows us to clear the local environment before executing the java command -endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %><% if ( classpath ) {%> -classpath "%CLASSPATH%"<% } %> <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName ?: entryPointArgs} %* & call :exitWithErrorLevel +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %><% if ( classpath ) {%> -classpath "%CLASSPATH%"<% } %> <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName ?: entryPointArgs} %* & call :exitWithErrorLevel & goto exitWithErrorLevel +@rem This label must not be changed. We rely on old scripts being able to jump to this point. :exitWithErrorLevel @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts "%COMSPEC%" /c exit %ERRORLEVEL% From 00365377b121cab7d6ead77a23dbc0aafaefd440 Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sat, 3 Oct 2026 12:09:54 +0200 Subject: [PATCH 04/10] Added experimental mode to jspecify nullability checks These checks enable the new jdk nullability data, so nullable lib methods are properly annotated now --- build.gradle.kts | 11 ++++++++++- .../stickerifier/stickerify/bot/Stickerify.java | 2 +- .../stickerifier/stickerify/process/OsConstants.java | 3 ++- .../stickerifier/stickerify/ResourceHelper.java | 5 ++++- .../stickerify/junit/TempFilesCleanupExtension.java | 5 ++++- .../stickerify/media/MediaHelperTest.java | 3 ++- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index acff4310..5ac7e642 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -62,11 +62,20 @@ val jlink = tasks.register(JlinkTask.DEFAULT_TASK_NAME) { javaCompiler = javaToolchains.compilerFor(java.toolchain) } +nullability { + jspecify { + experimental = true + } +} + val CompileOptions.nullability: NullabilityOptions - get() = (this as ExtensionAware).extensions["nullability"] as NullabilityOptions + get() = (this as ExtensionAware).extensions.getByName("nullability") as NullabilityOptions tasks.named(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) { options.nullability.checking = "tests" + options.nullability.jspecify { + experimental = true + } } tasks.test { diff --git a/src/main/java/com/github/stickerifier/stickerify/bot/Stickerify.java b/src/main/java/com/github/stickerifier/stickerify/bot/Stickerify.java index ed0ce675..35f06f5f 100644 --- a/src/main/java/com/github/stickerifier/stickerify/bot/Stickerify.java +++ b/src/main/java/com/github/stickerifier/stickerify/bot/Stickerify.java @@ -57,7 +57,7 @@ public record Stickerify(TelegramBot bot, Executor executor) implements UpdatesListener, ExceptionHandler, AutoCloseable { private static final StructuredLogger LOGGER = new StructuredLogger(Stickerify.class); - private static final String BOT_TOKEN = System.getenv("STICKERIFY_TOKEN"); + private static final @Nullable String BOT_TOKEN = System.getenv("STICKERIFY_TOKEN"); private static final ThreadFactory VIRTUAL_THREAD_FACTORY = Thread.ofVirtual().name("Virtual-", 0).factory(); private static final InputRichMessage PROCESSING_MESSAGE = new InputRichMessage().html(PROCESSING.getText()); diff --git a/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java b/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java index 4120ce55..652cbe96 100644 --- a/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java +++ b/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java @@ -1,7 +1,8 @@ package com.github.stickerifier.stickerify.process; public final class OsConstants { - private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows"); + private static final String OS = System.getenv().getOrDefault("os.name", ""); + private static final boolean IS_WINDOWS = OS.toLowerCase().contains("windows"); public static final String NULL_FILE = IS_WINDOWS ? "NUL" : "/dev/null"; diff --git a/src/test/java/com/github/stickerifier/stickerify/ResourceHelper.java b/src/test/java/com/github/stickerifier/stickerify/ResourceHelper.java index 5a751081..36f97ae9 100644 --- a/src/test/java/com/github/stickerifier/stickerify/ResourceHelper.java +++ b/src/test/java/com/github/stickerifier/stickerify/ResourceHelper.java @@ -10,7 +10,10 @@ public final class ResourceHelper { public static File loadResource(String filename) { - var resource = ResourceHelper.class.getClassLoader().getResource(filename); + var cl = ResourceHelper.class.getClassLoader(); + assertNotNull(cl, "Classloader not found"); + + var resource = cl.getResource(filename); assertNotNull(resource, "Test resource [%s] not found.".formatted(filename)); return new File(resource.getFile()); diff --git a/src/test/java/com/github/stickerifier/stickerify/junit/TempFilesCleanupExtension.java b/src/test/java/com/github/stickerifier/stickerify/junit/TempFilesCleanupExtension.java index 4b3bc897..4cac979a 100644 --- a/src/test/java/com/github/stickerifier/stickerify/junit/TempFilesCleanupExtension.java +++ b/src/test/java/com/github/stickerifier/stickerify/junit/TempFilesCleanupExtension.java @@ -20,6 +20,7 @@ public void afterAll(ExtensionContext context) throws IOException { private void deleteTempFiles() throws IOException { var tempFolder = System.getProperty("java.io.tmpdir"); + if (tempFolder == null) return; try (var files = Files.list(Path.of(tempFolder))) { for (var file : files.toList()) { @@ -31,7 +32,9 @@ private void deleteTempFiles() throws IOException { } private boolean isStickerifyFile(Path path) { - var fileName = path.getFileName().toString(); + var file = path.getFileName(); + if (file == null) return false; + var fileName = file.toString(); return Files.isRegularFile(path) && (fileName.startsWith("Stickerify-") || fileName.startsWith("OriginalFile-")); } diff --git a/src/test/java/com/github/stickerifier/stickerify/media/MediaHelperTest.java b/src/test/java/com/github/stickerifier/stickerify/media/MediaHelperTest.java index c21dc047..d059e5f8 100644 --- a/src/test/java/com/github/stickerifier/stickerify/media/MediaHelperTest.java +++ b/src/test/java/com/github/stickerifier/stickerify/media/MediaHelperTest.java @@ -5,6 +5,7 @@ import static com.github.stickerifier.stickerify.media.MediaConstraints.MAX_IMAGE_FILE_SIZE; import static com.github.stickerifier.stickerify.media.MediaConstraints.MAX_VIDEO_FILE_SIZE; import static com.github.stickerifier.stickerify.media.MediaConstraints.VP9_CODEC; +import static java.util.Objects.requireNonNullElse; import static java.util.concurrent.Executors.newVirtualThreadPerTaskExecutor; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -352,7 +353,7 @@ private static void executeConcurrentConversionsOf(File inputFile) { MediaHelper.convert(inputFile); } catch (Throwable e) { failedConversions.incrementAndGet(); - failureReasons.add(e.getMessage()); + failureReasons.add(requireNonNullElse(e.getMessage(), e.getClass().getSimpleName())); } })); } From dab606c13bb120e069f42c05bb5bb8a00cd00aa7 Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sat, 3 Oct 2026 12:10:23 +0200 Subject: [PATCH 05/10] Avoid the ffmpeg filters to use more than 1 CPU --- .../com/github/stickerifier/stickerify/media/MediaHelper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/github/stickerifier/stickerify/media/MediaHelper.java b/src/main/java/com/github/stickerifier/stickerify/media/MediaHelper.java index e5172161..d8448160 100644 --- a/src/main/java/com/github/stickerifier/stickerify/media/MediaHelper.java +++ b/src/main/java/com/github/stickerifier/stickerify/media/MediaHelper.java @@ -486,6 +486,7 @@ private static File convertToWebm(File file) throws MediaException, InterruptedE "-c:v", "libvpx-" + VP9_CODEC, "-row-mt", "1", "-threads", "2", + "-filter_threads", "1", "-g", "120", "-auto-alt-ref", "0", "-pix_fmt", "yuv420p", From aa296c5432e12b93a82660b3fe554253fc3b4365 Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sat, 3 Oct 2026 11:07:26 +0200 Subject: [PATCH 06/10] Move download of jar to Gradle task --- Dockerfile | 5 --- build.gradle.kts | 11 ++++- .../DownloadOpenTelemetryAgentTask.java | 45 +++++++++++++++++++ gradle/libs.versions.toml | 3 +- 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 buildSrc/src/main/java/com/github/stickerifier/stickerify/DownloadOpenTelemetryAgentTask.java diff --git a/Dockerfile b/Dockerfile index ba744cbd..f93a5db2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,10 +10,6 @@ RUN --mount=type=cache,target=/root/.gradle ./gradlew check installDist --no-dae FROM alpine:3.24.2 -# bump: OpenTelemetry /opentelemetry-java-instrumentation\/releases\/download\/v([\d.]+)/ git:https://github.com/open-telemetry/opentelemetry-java-instrumentation.git|/\d+\./|* -ADD https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.31.1/opentelemetry-javaagent.jar \ - /app/opentelemetry-agent.jar - COPY --from=builder /usr/bin/ff* /usr/bin/ COPY --from=builder /app/build/install/Stickerify/ . @@ -21,6 +17,5 @@ ENV OTEL_SDK_DISABLED=true ENV OTEL_METRICS_EXPORTER=none ENV OTEL_LOGS_EXPORTER=none ENV OTEL_JAVAAGENT_LOGGING=none -ENV JAVA_TOOL_OPTIONS=-javaagent:/app/opentelemetry-agent.jar ENV CONCURRENT_PROCESSES=5 CMD ["./bin/Stickerify"] diff --git a/build.gradle.kts b/build.gradle.kts index 5ac7e642..3c466616 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,4 @@ +import com.github.stickerifier.stickerify.DownloadOpenTelemetryAgentTask import com.github.stickerifier.stickerify.JlinkJavaLauncher import com.github.stickerifier.stickerify.JlinkTask import com.github.stickerifier.stickerify.JunitSeedArgumentProvider @@ -97,15 +98,23 @@ tasks.test { } } +val openTelemetryAgent = tasks.register(DownloadOpenTelemetryAgentTask.DEFAULT_TASK_NAME) { + description = "Downloads the OpenTelemetry agent for the distribution package." + + version = libs.versions.opentelemetry.get() + destinationFile = layout.buildDirectory.file("openTelemetryAgent/opentelemetry-javaagent.jar") +} + application { mainClass = "com.github.stickerifier.stickerify.runner.Main" - applicationDefaultJvmArgs = listOf("--enable-final-field-mutation=ALL-UNNAMED") + applicationDefaultJvmArgs = listOf("--enable-final-field-mutation=ALL-UNNAMED", "-javaagent:" + openTelemetryAgent.get().destinationFile.get().asFile.name) } distributions { main { contents { from(jlink) + from(openTelemetryAgent) } } } diff --git a/buildSrc/src/main/java/com/github/stickerifier/stickerify/DownloadOpenTelemetryAgentTask.java b/buildSrc/src/main/java/com/github/stickerifier/stickerify/DownloadOpenTelemetryAgentTask.java new file mode 100644 index 00000000..599916c3 --- /dev/null +++ b/buildSrc/src/main/java/com/github/stickerifier/stickerify/DownloadOpenTelemetryAgentTask.java @@ -0,0 +1,45 @@ +package com.github.stickerifier.stickerify; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; +import org.jetbrains.annotations.NotNull; + +import javax.inject.Inject; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +public abstract class DownloadOpenTelemetryAgentTask extends DefaultTask { + + public static final String DEFAULT_TASK_NAME = "downloadOpenTelemetryAgent"; + + @Input + public abstract Property<@NotNull String> getVersion(); + + @OutputFile + public abstract RegularFileProperty getDestinationFile(); + + @Inject + public DownloadOpenTelemetryAgentTask() { + setGroup("distribution"); + setDescription("Downloads OpenTelemetry agent jar."); + } + + @TaskAction + public void downloadJar() throws IOException { + var version = getVersion().get(); + var urlString = "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v%s/opentelemetry-javaagent.jar".formatted(version); + var targetFile = getDestinationFile().get().getAsFile(); + + var _ = targetFile.getParentFile().mkdirs(); + try (var inputStream = URI.create(urlString).toURL().openStream()) { + Files.copy(inputStream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } + +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b59083ad..550b196c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] junit = "6.1.3" +opentelemetry = "2.32.0" [libraries] gson = "com.google.code.gson:gson:2.14.0" @@ -11,7 +12,7 @@ junit-platform = { module = "org.junit.platform:junit-platform-launcher", versio logback-classic = "ch.qos.logback:logback-classic:1.6.5" logstash-logback-encoder = "net.logstash.logback:logstash-logback-encoder:9.0" mockwebserver = "com.squareup.okhttp3:mockwebserver3-junit5:5.5.0" -opentelemetry-instrumentation-annotations = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.31.1" +opentelemetry-instrumentation-annotations = { module = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations", version.ref = "opentelemetry" } telegram-bot-api = "com.github.pengrad:java-telegram-bot-api:10.3.0" tika = "org.apache.tika:tika-core:4.1.0" From 7f7ead7f02ed426f1874a5ab8d3b04253d99f084 Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sun, 4 Oct 2026 11:13:06 +0200 Subject: [PATCH 07/10] Code review changes --- build.gradle.kts | 2 +- .../com/github/stickerifier/stickerify/bot/Stickerify.java | 3 ++- .../github/stickerifier/stickerify/process/OsConstants.java | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3c466616..f553332d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -54,8 +54,8 @@ val jlink = tasks.register(JlinkTask.DEFAULT_TASK_NAME) { options = listOf("--strip-debug", "--no-header-files", "--no-man-pages", "--ignore-modified-runtime", "--generate-cds-archive") modules = listOf( "java.instrument", // for JUnit - "java.naming", // for Logback "java.management", // for OpenTelemetry + "java.naming", // for Logback "java.sql", // for Tika "jdk.unsupported" // for Gson ) diff --git a/src/main/java/com/github/stickerifier/stickerify/bot/Stickerify.java b/src/main/java/com/github/stickerifier/stickerify/bot/Stickerify.java index 35f06f5f..23b4f4bb 100644 --- a/src/main/java/com/github/stickerifier/stickerify/bot/Stickerify.java +++ b/src/main/java/com/github/stickerifier/stickerify/bot/Stickerify.java @@ -34,6 +34,7 @@ import com.pengrad.telegrambot.request.richmessages.SendRichMessage; import com.pengrad.telegrambot.request.richmessages.SendRichMessageDraft; import com.pengrad.telegrambot.response.BaseResponse; +import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.annotations.WithSpan; import org.jspecify.annotations.Nullable; import org.slf4j.event.Level; @@ -107,7 +108,7 @@ public void close() { bot.shutdown(); } - @WithSpan("bot.answer") + @WithSpan(value = "bot.answer", kind = SpanKind.CONSUMER) private void answer(TelegramRequest request) { LOGGER.at(Level.INFO).log("Received request"); diff --git a/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java b/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java index 652cbe96..ce7f8acf 100644 --- a/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java +++ b/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java @@ -1,8 +1,7 @@ package com.github.stickerifier.stickerify.process; public final class OsConstants { - private static final String OS = System.getenv().getOrDefault("os.name", ""); - private static final boolean IS_WINDOWS = OS.toLowerCase().contains("windows"); + private static final boolean IS_WINDOWS = System.getenv().getOrDefault("os.name", "").toLowerCase().contains("windows"); public static final String NULL_FILE = IS_WINDOWS ? "NUL" : "/dev/null"; From 5c9eba0882607b397ce30f7242d70c25aef8cea5 Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sun, 4 Oct 2026 11:17:20 +0200 Subject: [PATCH 08/10] Fix IS_WINDOWS check --- .../github/stickerifier/stickerify/process/OsConstants.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java b/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java index ce7f8acf..b8a99fa3 100644 --- a/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java +++ b/src/main/java/com/github/stickerifier/stickerify/process/OsConstants.java @@ -1,7 +1,9 @@ package com.github.stickerifier.stickerify.process; +import static java.util.Objects.requireNonNullElse; + public final class OsConstants { - private static final boolean IS_WINDOWS = System.getenv().getOrDefault("os.name", "").toLowerCase().contains("windows"); + private static final boolean IS_WINDOWS = requireNonNullElse(System.getProperty("os.name"), "").toLowerCase().contains("windows"); public static final String NULL_FILE = IS_WINDOWS ? "NUL" : "/dev/null"; From 45e671f0f4467406e7e62f48d865cfc1e9dcf841 Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sun, 4 Oct 2026 19:00:47 +0200 Subject: [PATCH 09/10] More code review fixes --- build.gradle.kts | 29 ++++++++++--------- .../DownloadOpenTelemetryAgentTask.java | 13 ++++++++- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f553332d..9c144fbf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -53,11 +53,11 @@ val jlink = tasks.register(JlinkTask.DEFAULT_TASK_NAME) { options = listOf("--strip-debug", "--no-header-files", "--no-man-pages", "--ignore-modified-runtime", "--generate-cds-archive") modules = listOf( - "java.instrument", // for JUnit - "java.management", // for OpenTelemetry - "java.naming", // for Logback - "java.sql", // for Tika - "jdk.unsupported" // for Gson + "java.instrument", // for JUnit + "java.management", // for OpenTelemetry + "java.naming", // for Logback + "java.sql", // for Tika + "jdk.unsupported" // for Gson ) includeModulePath = false javaCompiler = javaToolchains.compilerFor(java.toolchain) @@ -98,6 +98,11 @@ tasks.test { } } +application { + mainClass = "com.github.stickerifier.stickerify.runner.Main" + applicationDefaultJvmArgs = listOf("--enable-final-field-mutation=ALL-UNNAMED") +} + val openTelemetryAgent = tasks.register(DownloadOpenTelemetryAgentTask.DEFAULT_TASK_NAME) { description = "Downloads the OpenTelemetry agent for the distribution package." @@ -105,9 +110,12 @@ val openTelemetryAgent = tasks.register(Download destinationFile = layout.buildDirectory.file("openTelemetryAgent/opentelemetry-javaagent.jar") } -application { - mainClass = "com.github.stickerifier.stickerify.runner.Main" - applicationDefaultJvmArgs = listOf("--enable-final-field-mutation=ALL-UNNAMED", "-javaagent:" + openTelemetryAgent.get().destinationFile.get().asFile.name) +tasks.named(ApplicationPlugin.TASK_START_SCRIPTS_NAME) { + val agentJarName = openTelemetryAgent.get().destinationFile.get().asFile.name + defaultJvmOpts = (defaultJvmOpts ?: emptyList()) + "-javaagent:$agentJarName" + + (unixStartScriptGenerator as TemplateBasedScriptGenerator).template = resources.text.fromFile("src/main/resources/customUnixStartScript.txt") + (windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = resources.text.fromFile("src/main/resources/customWindowsStartScript.txt") } distributions { @@ -118,8 +126,3 @@ distributions { } } } - -tasks.named(ApplicationPlugin.TASK_START_SCRIPTS_NAME) { - (unixStartScriptGenerator as TemplateBasedScriptGenerator).template = resources.text.fromFile("src/main/resources/customUnixStartScript.txt") - (windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = resources.text.fromFile("src/main/resources/customWindowsStartScript.txt") -} diff --git a/buildSrc/src/main/java/com/github/stickerifier/stickerify/DownloadOpenTelemetryAgentTask.java b/buildSrc/src/main/java/com/github/stickerifier/stickerify/DownloadOpenTelemetryAgentTask.java index 599916c3..88c66586 100644 --- a/buildSrc/src/main/java/com/github/stickerifier/stickerify/DownloadOpenTelemetryAgentTask.java +++ b/buildSrc/src/main/java/com/github/stickerifier/stickerify/DownloadOpenTelemetryAgentTask.java @@ -21,6 +21,9 @@ public abstract class DownloadOpenTelemetryAgentTask extends DefaultTask { @Input public abstract Property<@NotNull String> getVersion(); + @Input + public abstract Property<@NotNull Integer> getDownloadTimeoutMillis(); + @OutputFile public abstract RegularFileProperty getDestinationFile(); @@ -28,6 +31,8 @@ public abstract class DownloadOpenTelemetryAgentTask extends DefaultTask { public DownloadOpenTelemetryAgentTask() { setGroup("distribution"); setDescription("Downloads OpenTelemetry agent jar."); + + getDownloadTimeoutMillis().convention(12000); } @TaskAction @@ -37,7 +42,13 @@ public void downloadJar() throws IOException { var targetFile = getDestinationFile().get().getAsFile(); var _ = targetFile.getParentFile().mkdirs(); - try (var inputStream = URI.create(urlString).toURL().openStream()) { + + var connection = URI.create(urlString).toURL().openConnection(); + int timeoutMillis = getDownloadTimeoutMillis().get(); + connection.setConnectTimeout(timeoutMillis); + connection.setReadTimeout(timeoutMillis); + + try (var inputStream = connection.getInputStream()) { Files.copy(inputStream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } } From d7cf7c12e539620bfe1e3056e62d540af07a4f5c Mon Sep 17 00:00:00 2001 From: Enrico Martelli Date: Sun, 4 Oct 2026 19:15:58 +0200 Subject: [PATCH 10/10] Removed Qodana You will not be missed --- .dockerignore | 1 - .github/workflows/unit-test.yml | 31 ------------------------------- .gitignore | 1 - Bumpfile | 1 - qodana.yaml | 8 -------- 5 files changed, 42 deletions(-) delete mode 100644 qodana.yaml diff --git a/.dockerignore b/.dockerignore index 9d65160a..99311d0c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -13,4 +13,3 @@ LICENSE Dockerfile *.dockerfile Bumpfile -qodana.yaml diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index b4cebc9d..566f1fe2 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -61,34 +61,3 @@ jobs: - name: Execute tests run: ./gradlew check - - qodana: - name: Perform Qodana analysis - needs: tests - runs-on: ubuntu-latest - permissions: - checks: write - security-events: write - steps: - - name: Checkout code changes - uses: actions/checkout@v7 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - fetch-depth: 0 - - - name: Execute analysis - uses: JetBrains/qodana-action@v2026.2.2 - env: - QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} - with: - use-caches: false - post-pr-comment: false - pr-mode: false - # bump: Qodana /qodana-jvm-community:([\d.]+)/ docker:jetbrains/qodana-jvm-community|/^[\d]{4}\.\d+$/|sort - args: --image jetbrains/qodana-jvm-community:2026.2 - - - name: Upload results to GitHub - if: ${{ !cancelled() }} - uses: github/codeql-action/upload-sarif@v4 - with: - sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json diff --git a/.gitignore b/.gitignore index 46064dfe..22fd85e1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ buildNumber.properties .idea/ src/main/resources/*.png .gradle/ -.qodana diff --git a/Bumpfile b/Bumpfile index 2e8d2f0d..4468ce39 100644 --- a/Bumpfile +++ b/Bumpfile @@ -1,3 +1,2 @@ .github/workflows/unit-test.yml Dockerfile -qodana.yaml diff --git a/qodana.yaml b/qodana.yaml deleted file mode 100644 index 4199a154..00000000 --- a/qodana.yaml +++ /dev/null @@ -1,8 +0,0 @@ -version: "1.0" -profile: - name: qodana.recommended -include: - - name: VulnerableLibrariesGlobal -# bump: Qodana /qodana-jvm-community:([\d.]+)/ docker:jetbrains/qodana-jvm-community|/^[\d]{4}\.\d+$/|sort -linter: jetbrains/qodana-jvm-community:2026.2 -projectJDK: temurin-27