Skip to content

Commit 8f7f7ba

Browse files
authored
Add Fir support (#369)
1 parent 61a40e2 commit 8f7f7ba

File tree

6 files changed

+94
-14
lines changed

6 files changed

+94
-14
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
* When the target app is using [Fir](https://devcenter.heroku.com/articles/generations#fir), a default `project.toml` will now be generated and deployed alongside the app. This ensures that OpenJDK will be installed and `Procfile` works as expected without the need for explicit configuration. ([#369](https://github.com/heroku/heroku-jvm-application-deployer/pull/369))
1013

1114
## [4.0.7] - 2024-07-17
1215

integration-test/src/lib.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ use std::path::{Path, PathBuf};
66
use std::process::{Command, Output};
77
use std::time::Duration;
88

9-
pub fn prepare_test<F>(fixture_dir: &str, f: F)
9+
pub fn prepare_test<F>(fixture_dir: &str, space: Option<&str>, f: F)
1010
where
1111
F: Fn(TestContext),
1212
{
1313
let app_dir = prepare_fixture(fixture_dir);
1414
initialize_git_repository(&app_dir);
15-
let app_create_result = create_heroku_app(&app_dir);
15+
let app_create_result = create_heroku_app(&app_dir, space);
1616

1717
f(TestContext {
1818
app_name: app_create_result.name,
@@ -62,11 +62,16 @@ pub fn http_get_expect_200(url: &str) -> String {
6262
}
6363

6464
#[must_use]
65-
pub fn create_heroku_app(path: &Path) -> HerokuAppCreateResult {
65+
pub fn create_heroku_app(path: &Path, space: Option<&str>) -> HerokuAppCreateResult {
66+
let mut args = vec!["create", "--json"];
67+
68+
if let Some(space) = space {
69+
args.push("--space");
70+
args.push(space);
71+
}
72+
6673
let output = run_command(
67-
Command::new("heroku")
68-
.args(["create", "--json"])
69-
.current_dir(path),
74+
Command::new("heroku").args(args).current_dir(path),
7075
&format!("Could not create Heroku app in {path:?}"),
7176
true,
7277
);

integration-test/tests/integration_tests.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::process::Command;
66

77
#[test]
88
fn basic_war_app() {
9-
prepare_test("war-app", |context| {
9+
prepare_test("war-app", None, |context| {
1010
create_empty_git_commit(&context.app_dir);
1111

1212
run_command(
@@ -33,7 +33,7 @@ fn basic_war_app() {
3333

3434
#[test]
3535
fn basic_jar_app() {
36-
prepare_test("jar-app", |context| {
36+
prepare_test("jar-app", None, |context| {
3737
create_empty_git_commit(&context.app_dir);
3838

3939
run_command(
@@ -60,7 +60,7 @@ fn basic_jar_app() {
6060

6161
#[test]
6262
fn basic_jar_app_git_repo_without_commits() {
63-
prepare_test("jar-app", |context| {
63+
prepare_test("jar-app", None, |context| {
6464
// Note that no git commit is created
6565

6666
run_command(
@@ -84,3 +84,39 @@ fn basic_jar_app_git_repo_without_commits() {
8484
assert_eq!(response, String::from("Hello World!"));
8585
});
8686
}
87+
88+
#[test]
89+
#[ignore = "General Fir CI testing strategy for tools is pending"]
90+
fn basic_war_app_fir() {
91+
prepare_test("war-app", Some("heroku-languages-jvm-ci"), |context| {
92+
// Note that no project.toml exists in the test fixture and that it will be generated by
93+
// the JVM application deployer automatically.
94+
assert!(!context.app_dir.join("project.toml").exists());
95+
96+
create_empty_git_commit(&context.app_dir);
97+
98+
let output = run_command(
99+
Command::new("java")
100+
.args([
101+
"-jar",
102+
&heroku_jvm_application_deployer_jar_path().to_string_lossy(),
103+
&context
104+
.app_dir
105+
.join("target/test-1.0-SNAPSHOT.war")
106+
.to_string_lossy(),
107+
"--jdk",
108+
"21",
109+
])
110+
.current_dir(&context.app_dir),
111+
"Running heroku-jvm-application-deployer failed.",
112+
false,
113+
);
114+
115+
assert!(
116+
String::from_utf8_lossy(&output.stdout).contains("- including: project.toml (hidden)")
117+
);
118+
119+
let response = http_get_expect_200(&context.app_url);
120+
assert_eq!(response, String::from("Hello World!"));
121+
});
122+
}

src/main/java/com/heroku/deployer/Main.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.heroku.deployer;
22

3+
import java.nio.charset.StandardCharsets;
34
import java.nio.file.Files;
45
import java.nio.file.Path;
56
import java.nio.file.Paths;
@@ -16,6 +17,7 @@
1617
import com.heroku.deployer.sourceblob.SourceBlobDescriptor;
1718
import com.heroku.deployer.sourceblob.SourceBlobPackager;
1819
import org.apache.commons.codec.digest.DigestUtils;
20+
import org.apache.commons.io.IOUtils;
1921
import picocli.CommandLine;
2022
import picocli.CommandLine.Command;
2123
import picocli.CommandLine.Option;
@@ -87,7 +89,8 @@ public Integer call() throws Exception {
8789
List<Path> defaultPaths = Arrays.asList(
8890
Paths.get("Procfile"),
8991
Paths.get("system.properties"),
90-
Paths.get(".jdk-overlay")
92+
Paths.get(".jdk-overlay"),
93+
Paths.get("project.toml")
9194
);
9295

9396
for (Path defaultPath : defaultPaths) {
@@ -146,6 +149,13 @@ public Integer call() throws Exception {
146149
sourceBlobDescriptor.addSyntheticFile(procfilePath, defaultProcfile.asString(), true);
147150
}
148151

152+
// Add an auto-generated project-toml to the source blob if no project.toml has been added yet.
153+
Path projectTomlPath = Paths.get("project.toml");
154+
if (!sourceBlobDescriptor.containsPath(projectTomlPath) && HerokuCli.runIsCnb(projectDirectory, appName)) {
155+
String defaultProjectToml = IOUtils.toString(getClass().getResourceAsStream("/default-project.toml"), StandardCharsets.UTF_8);
156+
sourceBlobDescriptor.addSyntheticFile(projectTomlPath, defaultProjectToml, true);
157+
}
158+
149159
// Add an auto-generated system.properties to the source blob if no system.properties has been added yet.
150160
if (jdkString.isPresent()) {
151161
Path systemPropertiesPath = Paths.get("system.properties");

src/main/java/com/heroku/deployer/util/HerokuCli.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
import java.io.IOException;
55
import java.io.InputStreamReader;
66
import java.nio.file.Path;
7-
import java.util.ArrayList;
8-
import java.util.Arrays;
9-
import java.util.List;
10-
import java.util.Optional;
7+
import java.util.*;
8+
import java.util.regex.Pattern;
119
import java.util.stream.Collectors;
1210

1311
public class HerokuCli {
@@ -22,6 +20,26 @@ public static Optional<String> runAuthToken(Path workingDirectory) throws IOExce
2220
}
2321
}
2422

23+
public static boolean runIsCnb(Path workingDirectory, Optional<String> appName) throws IOException {
24+
List<String> commandArguments = new ArrayList<>();
25+
commandArguments.add("info");
26+
commandArguments.add("--shell");
27+
28+
if (appName.isPresent()) {
29+
commandArguments.add("--app");
30+
commandArguments.add(appName.get());
31+
}
32+
33+
List<String> lines = runRaw(workingDirectory, commandArguments.toArray(new String[0]));
34+
for (String line : lines) {
35+
if (line.equals("stack=cnb")) {
36+
return true;
37+
}
38+
}
39+
40+
return false;
41+
}
42+
2543
private static List<String> runRaw(Path workingDirectory, String... command) throws IOException {
2644
List<String> fullCommand = new ArrayList<>(Arrays.asList(command));
2745
fullCommand.add(0, "heroku");
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[_]
2+
schema-version = "0.2"
3+
4+
[[io.buildpacks.group]]
5+
id = "heroku/jvm"
6+
7+
[[io.buildpacks.group]]
8+
id = "heroku/procfile"

0 commit comments

Comments
 (0)