Skip to content
Open
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
105 changes: 0 additions & 105 deletions build.gradle

This file was deleted.

116 changes: 116 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import com.github.stickerifier.stickerify.JlinkJavaLauncher
import com.github.stickerifier.stickerify.JlinkTask
import io.spring.gradle.nullability.NullabilityOptions
import org.gradle.internal.buildconfiguration.DaemonJvmPropertiesConfigurator
import org.gradle.kotlin.dsl.support.serviceOf

plugins {
java
application
alias(libs.plugins.spring.nullability)
}

repositories {
mavenCentral()
}

dependencies {
implementation(libs.gson)
implementation(libs.jspecify)
implementation(libs.logback.classic)
implementation(libs.logstash.logback.encoder)
implementation(libs.telegram.bot.api)
implementation(libs.tika)

constraints {
add("implementation", libs.jackson.core)
}

testImplementation(libs.hamcrest)
testImplementation(libs.junit.jupiter)
testImplementation(libs.mockwebserver)
testRuntimeOnly(libs.junit.platform)
}

group = "com.github.stickerifier"
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)
vendor = JvmVendorSpec.ADOPTIUM
}

tasks.named<UpdateDaemonJvm>(DaemonJvmPropertiesConfigurator.TASK_NAME) {
languageVersion = JavaLanguageVersion.of(26)
vendor = JvmVendorSpec.ADOPTIUM
}

val jlink = tasks.register<JlinkTask>("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")
modules = listOf(
"java.instrument", // for junit
"java.naming", // for logback
"java.sql", // for tika
"jdk.unsupported" // for gson
)
includeModulePath = false
javaCompiler = javaToolchains.compilerFor(java.toolchain)

val execOps = serviceOf<ExecOperations>()
doLast {
val javaExe = outputDirectory.file("jre/bin/java").get().asFile.absolutePath
execOps.exec {
commandLine(javaExe, "-XX:+UseCompactObjectHeaders", "-Xshare:dump")
}
}
}

val CompileOptions.nullability: NullabilityOptions
get() = (this as ExtensionAware).extensions["nullability"] as NullabilityOptions

tasks.named<JavaCompile>(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) {
options.nullability.checking = "tests"
}

tasks.test {
inputs.dir(jlink.map { it.outputDirectory.get().asFile })
javaLauncher = providers.provider { JlinkJavaLauncher(jlink.get()) }

useJUnitPlatform()
jvmArgs("--enable-final-field-mutation=ALL-UNNAMED")

testLogging {
events("started", "passed", "failed", "skipped")
}

val seedProvider = providers.gradleProperty("junitSeed").orElse(providers.provider { System.nanoTime().toString() })
jvmArgumentProviders.add(CommandLineArgumentProvider {
val seed = seedProvider.get()
listOf("-Djunit.jupiter.execution.order.random.seed=$seed")
})

doFirst {
println("Test seed: ${seedProvider.get()}")
Comment on lines +89 to +96

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 5 'junitSeed|System\.nanoTime|jvmArgumentProviders|Test seed' build.gradle.kts

if rg -n 'providers\.provider\s*\{\s*System\.nanoTime' build.gradle.kts; then
  echo "The fallback seed is still recalculated by a live Provider."
  exit 1
fi

Repository: Stickerifier/Stickerify

Length of output: 932


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- build.gradle.kts context ---'
sed -n '60,110p' build.gradle.kts

printf '%s\n' '--- Gradle wrapper and related configuration ---'
find . -maxdepth 3 -type f \( -name 'gradle-wrapper.properties' -o -name 'settings.gradle*' -o -name 'gradle.properties' \) -print

printf '%s\n' '--- Seed-related usages ---'
rg -n -C 3 'junitSeed|random\.seed|Test seed|jvmArgumentProviders|CommandLineArgumentProvider' . \
  -g '!build' -g '!node_modules'

printf '%s\n' '--- Provider API documentation references in repository ---'
rg -n -i -C 2 'ProviderFactory|providers\.provider|orElse' . \
  -g '*.md' -g '*.gradle*' -g '*.kt' -g '*.java' | head -200

Repository: Stickerifier/Stickerify

Length of output: 6218


🌐 Web query:

Gradle ProviderFactory.provider Callable value recalculated each time queried Provider API documentation

💡 Result:

Yes, a Provider created via ProviderFactory.provider(Callable) is live, meaning its Callable is invoked every time the Provider's value is queried [1][2][3]. Key details regarding this behavior: - Live Evaluation: Because the provider is live, calling methods like get or getOrNull on the provider will re-execute the logic inside the provided Callable [1][4]. - Memoization Requirement: Since the Callable is not memoized by default, if your calculation is expensive, you must manually handle caching or memoization of the result if you wish to avoid repeated execution [4]. - Configuration Cache: While the provider itself is live, its usage within the Configuration Cache has specific behaviors. The provider is always computed and its value can be cached by the Configuration Cache when applicable; if created at configuration time, the Callable is permitted to use configuration-time-only APIs and capture objects of arbitrary types [1][2]. - Workarounds: To effectively cache or memoize the result of a provider created via a Callable, developers often use a Property object, set it with the provider, and apply configuration methods such as finalizeValueOnRead to ensure the value is fixed and no longer re-evaluated upon subsequent reads [5][4].

Citations:


Resolve the fallback seed once.

When junitSeed is unset, the live provider can return a different value for the JVM argument and the doFirst log. Resolve the seed once and reuse it for both.

Source: MCP tools

}
}

application {
mainClass = "com.github.stickerifier.stickerify.runner.Main"
applicationDefaultJvmArgs = listOf("-XX:+UseCompactObjectHeaders", "-XX:+UseShenandoahGC", "-XX:ShenandoahGCMode=generational", "--enable-final-field-mutation=ALL-UNNAMED")
}

distributions {
main {
contents {
from(jlink)
}
}
}

tasks.named<CreateStartScripts>(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")
}
2 changes: 1 addition & 1 deletion buildSrc/build.gradle → buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id('java-library')
`java-library`
}

repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
import org.gradle.api.file.FileSystemOperations;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
import org.gradle.jvm.toolchain.JavaCompiler;
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.process.ExecOperations;
import org.jetbrains.annotations.NotNull;

Expand All @@ -39,7 +37,7 @@ public abstract class JlinkTask extends DefaultTask {
public abstract DirectoryProperty getOutputDirectory();

@Nested
protected abstract Property<@NotNull JavaCompiler> getJavaCompiler();
public abstract Property<JavaCompiler> getJavaCompiler();

@Inject
protected abstract FileSystemOperations getFs();
Expand All @@ -48,14 +46,14 @@ public abstract class JlinkTask extends DefaultTask {
protected abstract ExecOperations getExec();

@Inject
public JlinkTask(ProjectLayout layout, JavaToolchainService javaToolchain) {
public JlinkTask(ProjectLayout layout) {
setGroup("build");
setDescription("Generates a custom Java runtime image using jlink.");

getOptions().convention(List.of());
getModules().convention(List.of("ALL-MODULE-PATH"));
getIncludeModulePath().convention(true);
getOutputDirectory().convention(layout.getBuildDirectory().dir(getName()));

var toolchain = getProject().getExtensions().getByType(JavaPluginExtension.class).getToolchain();
getJavaCompiler().convention(javaToolchain.compilerFor(toolchain));
}

@TaskAction
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.gradle.cache=true
org.gradle.configuration-cache=true
org.gradle.isolated-projects=true
org.gradle.jvmargs=-Dfile.encoding=UTF-8
5 changes: 0 additions & 5 deletions settings.gradle

This file was deleted.

5 changes: 5 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}

rootProject.name = "Stickerify"
2 changes: 2 additions & 0 deletions src/test/resources/junit-platform.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$Random
junit.jupiter.testmethod.order.default=org.junit.jupiter.api.MethodOrderer$Random
Loading