Resource filtering
Maven-style resource filtering, substituting ${VAR} placeholders inside
resource files at build time, isn't a built-in feature in bleep. It's
a focused sourcegen script: a few lines of
real logic, plus enough Java boilerplate for one class.
This is intentional: filtering rules vary wildly between projects (Spring
property syntax, Mustache templates, plain ${X} substitution, conditional
includes, multi-environment overrides). A built-in implementation would force
one of those models on you. A script lets you write exactly what you need,
debug it in your IDE, and version it alongside the rest of your build.
Example: substitute build-time values into application.properties
If what you're substituting is a version, a commit or a build id, use build stamps instead. A value derived from git isn't an input this script watches, so it goes stale without warning; and because its output is part of the cache key, a value that changes on every commit makes the project and everything downstream miss the remote cache. Stamps put those values into the artifact without either problem. The example below is still the right shape for everything else — names, environment-specific configuration, anything that comes from your own inputs.
Hand-written src/main/resources/application.properties.template:
app.name=${APP_NAME}
app.version=${APP_VERSION}
build.timestamp=${BUILD_TIMESTAMP}
Wire the script into bleep.yaml:
projects:
my-service:
sourcegen: scripts/myco.FilterResources
scripts:
dependencies:
- build.bleep:bleepscript:${BLEEP_VERSION}
java:
options: -proc:none --release 17
platform:
name: jvm
sources: ./src/main/java
The script:
package myco;
import bleepscript.BleepCodegenScript;
import bleepscript.CodegenTarget;
import bleepscript.Commands;
import bleepscript.Started;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.List;
import java.util.Map;
public final class FilterResources extends BleepCodegenScript {
public FilterResources() {
super("FilterResources");
}
@Override
public void run(Started started, Commands commands, List<CodegenTarget> targets, List<String> args) {
Map<String, String> substitutions = Map.of(
"APP_NAME", System.getenv().getOrDefault("APP_NAME", "my-service"),
"APP_VERSION", started.build().dynVer().version(),
"BUILD_TIMESTAMP", Instant.now().toString());
for (CodegenTarget target : targets) {
Path template = started.buildPaths().buildDir()
.resolve("my-service/src/main/resources/application.properties.template");
try {
String rendered = Files.readString(template);
for (Map.Entry<String, String> sub : substitutions.entrySet()) {
rendered = rendered.replace("${" + sub.getKey() + "}", sub.getValue());
}
Path out = target.resources().resolve("application.properties");
Files.createDirectories(out.getParent());
Files.writeString(out, rendered);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
That's it. The generated application.properties lands in the project's
resource path automatically and ships in the JAR / on the classpath at
runtime.
Why this is the right shape
- Your filtering, your rules. Need conditional sections? Mustache? Multi-environment YAML merge? Just write it.
- Debuggable. Set a breakpoint in IntelliJ and step through.
- Incremental. Bleep tracks the script's inputs and re-runs it only when they change.
- Atomic. A failed script never leaves stale generated resources behind.
See Source generation for the full script API.