diff --git a/Justfile b/Justfile
index 40d51ec90d..355402b84e 100644
--- a/Justfile
+++ b/Justfile
@@ -7,7 +7,7 @@ VERSION := `git describe --tags --always | sed -e 's/^v//'`
RUNNER_TEMPLATE_ZIP := "backend/controller/scaling/localscaling/template.zip"
TIMESTAMP := `date +%s`
SCHEMA_OUT := "backend/protos/xyz/block/ftl/v1/schema/schema.proto"
-ZIP_DIRS := "go-runtime/compile/build-template go-runtime/compile/external-module-template go-runtime/compile/main-work-template internal/projectinit/scaffolding go-runtime/scaffolding"
+ZIP_DIRS := "go-runtime/compile/build-template go-runtime/compile/external-module-template go-runtime/compile/main-work-template internal/projectinit/scaffolding go-runtime/scaffolding jvm-runtime/java/scaffolding jvm-runtime/kotlin/scaffolding"
FRONTEND_OUT := "frontend/dist/index.html"
EXTENSION_OUT := "extensions/vscode/dist/extension.js"
PROTOS_IN := "backend/protos/xyz/block/ftl/v1/schema/schema.proto backend/protos/xyz/block/ftl/v1/console/console.proto backend/protos/xyz/block/ftl/v1/ftl.proto backend/protos/xyz/block/ftl/v1/schema/runtime.proto"
diff --git a/cmd/ftl/cmd_new.go b/cmd/ftl/cmd_new.go
index 82dde183e4..4b204217fa 100644
--- a/cmd/ftl/cmd_new.go
+++ b/cmd/ftl/cmd_new.go
@@ -21,10 +21,13 @@ import (
"github.com/TBD54566975/ftl/internal/exec"
"github.com/TBD54566975/ftl/internal/log"
"github.com/TBD54566975/ftl/internal/projectconfig"
+ "github.com/TBD54566975/ftl/jvm-runtime/java"
+ "github.com/TBD54566975/ftl/jvm-runtime/kotlin"
)
type newCmd struct {
Go newGoCmd `cmd:"" help:"Initialize a new FTL Go module."`
+ Java newJavaCmd `cmd:"" help:"Initialize a new FTL Java module."`
Kotlin newKotlinCmd `cmd:"" help:"Initialize a new FTL Kotlin module."`
}
@@ -35,12 +38,18 @@ type newGoCmd struct {
GoVersion string
}
+type newJavaCmd struct {
+ Dir string `arg:"" help:"Directory to initialize the module in."`
+ Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
+ Group string `help:"The Maven groupId of the project." default:"com.example"`
+}
type newKotlinCmd struct {
- Dir string `arg:"" help:"Directory to initialize the module in."`
- Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
+ Dir string `arg:"" help:"Directory to initialize the module in."`
+ Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
+ Group string `help:"The Maven groupId of the project." default:"com.example"`
}
-func (i newGoCmd) Run(ctx context.Context) error {
+func (i newGoCmd) Run(ctx context.Context, config projectconfig.Config) error {
name, path, err := validateModule(i.Dir, i.Name)
if err != nil {
return err
@@ -51,11 +60,6 @@ func (i newGoCmd) Run(ctx context.Context) error {
return fmt.Errorf("module name %q must be a valid Go module name and not a reserved keyword", name)
}
- config, err := projectconfig.Load(ctx, "")
- if err != nil {
- return fmt.Errorf("failed to load project config: %w", err)
- }
-
logger := log.FromContext(ctx)
logger.Debugf("Creating FTL Go module %q in %s", name, path)
@@ -84,8 +88,54 @@ func (i newGoCmd) Run(ctx context.Context) error {
return nil
}
-func (i newKotlinCmd) Run(ctx context.Context) error {
- return fmt.Errorf("kotlin scaffolinging temporarily removed")
+func (i newJavaCmd) Run(ctx context.Context, config projectconfig.Config) error {
+ return RunJvmScaffolding(ctx, config, i.Dir, i.Name, i.Group, java.Files())
+}
+
+func (i newKotlinCmd) Run(ctx context.Context, config projectconfig.Config) error {
+ return RunJvmScaffolding(ctx, config, i.Dir, i.Name, i.Group, kotlin.Files())
+}
+
+func RunJvmScaffolding(ctx context.Context, config projectconfig.Config, dir string, name string, group string, source *zip.Reader) error {
+ name, path, err := validateModule(dir, name)
+ if err != nil {
+ return err
+ }
+
+ logger := log.FromContext(ctx)
+ logger.Debugf("Creating FTL module %q in %s", name, path)
+
+ packageDir := strings.ReplaceAll(group, ".", "/")
+
+ javaContext := struct {
+ Dir string
+ Name string
+ Group string
+ PackageDir string
+ }{
+ Dir: dir,
+ Name: name,
+ Group: group,
+ PackageDir: packageDir,
+ }
+
+ if err := scaffold(ctx, config.Hermit, source, dir, javaContext); err != nil {
+ return err
+ }
+
+ _, ok := internal.GitRoot(dir).Get()
+ if !config.NoGit && ok {
+ logger.Debugf("Adding files to git")
+ if config.Hermit {
+ if err := maybeGitAdd(ctx, dir, "bin/*"); err != nil {
+ return err
+ }
+ }
+ if err := maybeGitAdd(ctx, dir, filepath.Join(name, "*")); err != nil {
+ return err
+ }
+ }
+ return nil
}
func validateModule(dir string, name string) (string, string, error) {
diff --git a/jvm-runtime/java/devel.go b/jvm-runtime/java/devel.go
new file mode 100644
index 0000000000..6c09d47307
--- /dev/null
+++ b/jvm-runtime/java/devel.go
@@ -0,0 +1,12 @@
+//go:build !release
+
+package java
+
+import (
+ "archive/zip"
+
+ "github.com/TBD54566975/ftl/internal"
+)
+
+// Files is the FTL Go runtime scaffolding files.
+func Files() *zip.Reader { return internal.ZipRelativeToCaller("scaffolding") }
diff --git a/jvm-runtime/java/release.go b/jvm-runtime/java/release.go
new file mode 100644
index 0000000000..87bae32b32
--- /dev/null
+++ b/jvm-runtime/java/release.go
@@ -0,0 +1,21 @@
+//go:build release
+
+package java
+
+import (
+ "archive/zip"
+ "bytes"
+ _ "embed"
+)
+
+//go:embed scaffolding.zip
+var archive []byte
+
+// Files is the FTL Go runtime scaffolding files.
+func Files() *zip.Reader {
+ zr, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
+ if err != nil {
+ panic(err)
+ }
+ return zr
+}
diff --git a/jvm-runtime/java/scaffolding/bin/.ftl@latest.pkg b/jvm-runtime/java/scaffolding/bin/.ftl@latest.pkg
new file mode 120000
index 0000000000..383f4511d4
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/.ftl@latest.pkg
@@ -0,0 +1 @@
+hermit
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/.maven-3.9.8.pkg b/jvm-runtime/java/scaffolding/bin/.maven-3.9.8.pkg
new file mode 120000
index 0000000000..383f4511d4
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/.maven-3.9.8.pkg
@@ -0,0 +1 @@
+hermit
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/.openjdk@17.pkg b/jvm-runtime/java/scaffolding/bin/.openjdk@17.pkg
new file mode 120000
index 0000000000..383f4511d4
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/.openjdk@17.pkg
@@ -0,0 +1 @@
+hermit
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/README.hermit.md b/jvm-runtime/java/scaffolding/bin/README.hermit.md
new file mode 100644
index 0000000000..e889550ba4
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/README.hermit.md
@@ -0,0 +1,7 @@
+# Hermit environment
+
+This is a [Hermit](https://github.com/cashapp/hermit) bin directory.
+
+The symlinks in this directory are managed by Hermit and will automatically
+download and install Hermit itself as well as packages. These packages are
+local to this environment.
diff --git a/jvm-runtime/java/scaffolding/bin/activate-hermit b/jvm-runtime/java/scaffolding/bin/activate-hermit
new file mode 100755
index 0000000000..fe28214d33
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/activate-hermit
@@ -0,0 +1,21 @@
+#!/bin/bash
+# This file must be used with "source bin/activate-hermit" from bash or zsh.
+# You cannot run it directly
+#
+# THIS FILE IS GENERATED; DO NOT MODIFY
+
+if [ "${BASH_SOURCE-}" = "$0" ]; then
+ echo "You must source this script: \$ source $0" >&2
+ exit 33
+fi
+
+BIN_DIR="$(dirname "${BASH_SOURCE[0]:-${(%):-%x}}")"
+if "${BIN_DIR}/hermit" noop > /dev/null; then
+ eval "$("${BIN_DIR}/hermit" activate "${BIN_DIR}/..")"
+
+ if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ]; then
+ hash -r 2>/dev/null
+ fi
+
+ echo "Hermit environment $("${HERMIT_ENV}"/bin/hermit env HERMIT_ENV) activated"
+fi
diff --git a/jvm-runtime/java/scaffolding/bin/ftl b/jvm-runtime/java/scaffolding/bin/ftl
new file mode 120000
index 0000000000..47611de9b6
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/ftl
@@ -0,0 +1 @@
+.ftl@latest.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/hermit b/jvm-runtime/java/scaffolding/bin/hermit
new file mode 100755
index 0000000000..7fef769248
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/hermit
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# THIS FILE IS GENERATED; DO NOT MODIFY
+
+set -eo pipefail
+
+export HERMIT_USER_HOME=~
+
+if [ -z "${HERMIT_STATE_DIR}" ]; then
+ case "$(uname -s)" in
+ Darwin)
+ export HERMIT_STATE_DIR="${HERMIT_USER_HOME}/Library/Caches/hermit"
+ ;;
+ Linux)
+ export HERMIT_STATE_DIR="${XDG_CACHE_HOME:-${HERMIT_USER_HOME}/.cache}/hermit"
+ ;;
+ esac
+fi
+
+export HERMIT_DIST_URL="${HERMIT_DIST_URL:-https://github.com/cashapp/hermit/releases/download/stable}"
+HERMIT_CHANNEL="$(basename "${HERMIT_DIST_URL}")"
+export HERMIT_CHANNEL
+export HERMIT_EXE=${HERMIT_EXE:-${HERMIT_STATE_DIR}/pkg/hermit@${HERMIT_CHANNEL}/hermit}
+
+if [ ! -x "${HERMIT_EXE}" ]; then
+ echo "Bootstrapping ${HERMIT_EXE} from ${HERMIT_DIST_URL}" 1>&2
+ INSTALL_SCRIPT="$(mktemp)"
+ # This value must match that of the install script
+ INSTALL_SCRIPT_SHA256="180e997dd837f839a3072a5e2f558619b6d12555cd5452d3ab19d87720704e38"
+ if [ "${INSTALL_SCRIPT_SHA256}" = "BYPASS" ]; then
+ curl -fsSL "${HERMIT_DIST_URL}/install.sh" -o "${INSTALL_SCRIPT}"
+ else
+ # Install script is versioned by its sha256sum value
+ curl -fsSL "${HERMIT_DIST_URL}/install-${INSTALL_SCRIPT_SHA256}.sh" -o "${INSTALL_SCRIPT}"
+ # Verify install script's sha256sum
+ openssl dgst -sha256 "${INSTALL_SCRIPT}" | \
+ awk -v EXPECTED="$INSTALL_SCRIPT_SHA256" \
+ '$2!=EXPECTED {print "Install script sha256 " $2 " does not match " EXPECTED; exit 1}'
+ fi
+ /bin/bash "${INSTALL_SCRIPT}" 1>&2
+fi
+
+exec "${HERMIT_EXE}" --level=fatal exec "$0" -- "$@"
diff --git a/jvm-runtime/java/scaffolding/bin/hermit.hcl b/jvm-runtime/java/scaffolding/bin/hermit.hcl
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/jvm-runtime/java/scaffolding/bin/jar b/jvm-runtime/java/scaffolding/bin/jar
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jar
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jarsigner b/jvm-runtime/java/scaffolding/bin/jarsigner
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jarsigner
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/java b/jvm-runtime/java/scaffolding/bin/java
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/java
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/javac b/jvm-runtime/java/scaffolding/bin/javac
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/javac
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/javadoc b/jvm-runtime/java/scaffolding/bin/javadoc
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/javadoc
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/javap b/jvm-runtime/java/scaffolding/bin/javap
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/javap
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jcmd b/jvm-runtime/java/scaffolding/bin/jcmd
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jcmd
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jconsole b/jvm-runtime/java/scaffolding/bin/jconsole
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jconsole
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jdb b/jvm-runtime/java/scaffolding/bin/jdb
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jdb
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jdeprscan b/jvm-runtime/java/scaffolding/bin/jdeprscan
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jdeprscan
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jdeps b/jvm-runtime/java/scaffolding/bin/jdeps
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jdeps
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jfr b/jvm-runtime/java/scaffolding/bin/jfr
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jfr
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jhsdb b/jvm-runtime/java/scaffolding/bin/jhsdb
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jhsdb
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jimage b/jvm-runtime/java/scaffolding/bin/jimage
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jimage
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jinfo b/jvm-runtime/java/scaffolding/bin/jinfo
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jinfo
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jlink b/jvm-runtime/java/scaffolding/bin/jlink
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jlink
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jmap b/jvm-runtime/java/scaffolding/bin/jmap
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jmap
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jmod b/jvm-runtime/java/scaffolding/bin/jmod
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jmod
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jpackage b/jvm-runtime/java/scaffolding/bin/jpackage
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jpackage
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jps b/jvm-runtime/java/scaffolding/bin/jps
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jps
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jrunscript b/jvm-runtime/java/scaffolding/bin/jrunscript
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jrunscript
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jshell b/jvm-runtime/java/scaffolding/bin/jshell
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jshell
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jstack b/jvm-runtime/java/scaffolding/bin/jstack
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jstack
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jstat b/jvm-runtime/java/scaffolding/bin/jstat
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jstat
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/jstatd b/jvm-runtime/java/scaffolding/bin/jstatd
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/jstatd
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/keytool b/jvm-runtime/java/scaffolding/bin/keytool
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/keytool
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/mvn b/jvm-runtime/java/scaffolding/bin/mvn
new file mode 120000
index 0000000000..9158f83b62
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/mvn
@@ -0,0 +1 @@
+.maven-3.9.8.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/mvnDebug b/jvm-runtime/java/scaffolding/bin/mvnDebug
new file mode 120000
index 0000000000..9158f83b62
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/mvnDebug
@@ -0,0 +1 @@
+.maven-3.9.8.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/rmiregistry b/jvm-runtime/java/scaffolding/bin/rmiregistry
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/rmiregistry
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/bin/serialver b/jvm-runtime/java/scaffolding/bin/serialver
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/bin/serialver
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/ftl.toml b/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/ftl.toml
new file mode 100644
index 0000000000..828a8b57ce
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/ftl.toml
@@ -0,0 +1,2 @@
+module = "{{ .Name }}"
+language = "java"
diff --git a/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/pom.xml.tmpl b/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/pom.xml.tmpl
new file mode 100644
index 0000000000..cf8d370b0c
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/pom.xml.tmpl
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+ ftl.{{ .Group }}
+ {{ .Name }}
+ 1.0-SNAPSHOT
+
+
+ xyz.block.ftl
+ ftl-build-parent-java
+ 1.0-SNAPSHOT
+
+
+
diff --git a/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/src/main/java/{{ .PackageDir }}/EchoVerb.java b/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/src/main/java/{{ .PackageDir }}/EchoVerb.java
new file mode 100644
index 0000000000..29cd33c304
--- /dev/null
+++ b/jvm-runtime/java/scaffolding/{{ .Name | camel | lower }}/src/main/java/{{ .PackageDir }}/EchoVerb.java
@@ -0,0 +1,13 @@
+package {{ .Group }};
+
+import xyz.block.ftl.Export;
+import xyz.block.ftl.Verb;
+
+public class EchoVerb {
+
+ @Export
+ @Verb
+ public String echo(String request) {
+ return "Hello, " + request + "!";
+ }
+}
diff --git a/jvm-runtime/jvm_integration_test.go b/jvm-runtime/jvm_integration_test.go
index e7411f31f2..ff291b26ee 100644
--- a/jvm-runtime/jvm_integration_test.go
+++ b/jvm-runtime/jvm_integration_test.go
@@ -14,6 +14,22 @@ import (
"github.com/alecthomas/repr"
)
+func TestLifecycle(t *testing.T) {
+ in.Run(t,
+ in.WithLanguages("java", "kotlin"),
+ in.GitInit(),
+ in.Exec("rm", "ftl-project.toml"),
+ in.IfLanguage("kotlin", in.Exec("rm", "-r", "echo")), //horrible, but we need to do cleanup, I wonder if we should be running each test in a separate directory
+ in.Exec("ftl", "init", "test", "."),
+ in.IfLanguage("java", in.Exec("ftl", "new", "java", ".", "echo")),
+ in.IfLanguage("kotlin", in.Exec("ftl", "new", "kotlin", ".", "echo")),
+ in.Deploy("echo"),
+ in.Call("echo", "echo", "Bob", func(t testing.TB, response string) {
+ assert.Equal(t, "Hello, Bob!", response)
+ }),
+ )
+}
+
func TestJVMToGoCall(t *testing.T) {
exampleObject := TestObject{
diff --git a/jvm-runtime/kotlin/devel.go b/jvm-runtime/kotlin/devel.go
new file mode 100644
index 0000000000..b0cf1bb01f
--- /dev/null
+++ b/jvm-runtime/kotlin/devel.go
@@ -0,0 +1,12 @@
+//go:build !release
+
+package kotlin
+
+import (
+ "archive/zip"
+
+ "github.com/TBD54566975/ftl/internal"
+)
+
+// Files is the FTL Go runtime scaffolding files.
+func Files() *zip.Reader { return internal.ZipRelativeToCaller("scaffolding") }
diff --git a/jvm-runtime/kotlin/release.go b/jvm-runtime/kotlin/release.go
new file mode 100644
index 0000000000..b757334241
--- /dev/null
+++ b/jvm-runtime/kotlin/release.go
@@ -0,0 +1,21 @@
+//go:build release
+
+package kotlin
+
+import (
+ "archive/zip"
+ "bytes"
+ _ "embed"
+)
+
+//go:embed scaffolding.zip
+var archive []byte
+
+// Files is the FTL Go runtime scaffolding files.
+func Files() *zip.Reader {
+ zr, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
+ if err != nil {
+ panic(err)
+ }
+ return zr
+}
diff --git a/jvm-runtime/kotlin/scaffolding/bin/.ftl@latest.pkg b/jvm-runtime/kotlin/scaffolding/bin/.ftl@latest.pkg
new file mode 120000
index 0000000000..383f4511d4
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/.ftl@latest.pkg
@@ -0,0 +1 @@
+hermit
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/.maven-3.9.8.pkg b/jvm-runtime/kotlin/scaffolding/bin/.maven-3.9.8.pkg
new file mode 120000
index 0000000000..383f4511d4
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/.maven-3.9.8.pkg
@@ -0,0 +1 @@
+hermit
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/.openjdk@17.pkg b/jvm-runtime/kotlin/scaffolding/bin/.openjdk@17.pkg
new file mode 120000
index 0000000000..383f4511d4
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/.openjdk@17.pkg
@@ -0,0 +1 @@
+hermit
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/README.hermit.md b/jvm-runtime/kotlin/scaffolding/bin/README.hermit.md
new file mode 100644
index 0000000000..e889550ba4
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/README.hermit.md
@@ -0,0 +1,7 @@
+# Hermit environment
+
+This is a [Hermit](https://github.com/cashapp/hermit) bin directory.
+
+The symlinks in this directory are managed by Hermit and will automatically
+download and install Hermit itself as well as packages. These packages are
+local to this environment.
diff --git a/jvm-runtime/kotlin/scaffolding/bin/activate-hermit b/jvm-runtime/kotlin/scaffolding/bin/activate-hermit
new file mode 100755
index 0000000000..fe28214d33
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/activate-hermit
@@ -0,0 +1,21 @@
+#!/bin/bash
+# This file must be used with "source bin/activate-hermit" from bash or zsh.
+# You cannot run it directly
+#
+# THIS FILE IS GENERATED; DO NOT MODIFY
+
+if [ "${BASH_SOURCE-}" = "$0" ]; then
+ echo "You must source this script: \$ source $0" >&2
+ exit 33
+fi
+
+BIN_DIR="$(dirname "${BASH_SOURCE[0]:-${(%):-%x}}")"
+if "${BIN_DIR}/hermit" noop > /dev/null; then
+ eval "$("${BIN_DIR}/hermit" activate "${BIN_DIR}/..")"
+
+ if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ]; then
+ hash -r 2>/dev/null
+ fi
+
+ echo "Hermit environment $("${HERMIT_ENV}"/bin/hermit env HERMIT_ENV) activated"
+fi
diff --git a/jvm-runtime/kotlin/scaffolding/bin/ftl b/jvm-runtime/kotlin/scaffolding/bin/ftl
new file mode 120000
index 0000000000..47611de9b6
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/ftl
@@ -0,0 +1 @@
+.ftl@latest.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/hermit b/jvm-runtime/kotlin/scaffolding/bin/hermit
new file mode 100755
index 0000000000..7fef769248
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/hermit
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# THIS FILE IS GENERATED; DO NOT MODIFY
+
+set -eo pipefail
+
+export HERMIT_USER_HOME=~
+
+if [ -z "${HERMIT_STATE_DIR}" ]; then
+ case "$(uname -s)" in
+ Darwin)
+ export HERMIT_STATE_DIR="${HERMIT_USER_HOME}/Library/Caches/hermit"
+ ;;
+ Linux)
+ export HERMIT_STATE_DIR="${XDG_CACHE_HOME:-${HERMIT_USER_HOME}/.cache}/hermit"
+ ;;
+ esac
+fi
+
+export HERMIT_DIST_URL="${HERMIT_DIST_URL:-https://github.com/cashapp/hermit/releases/download/stable}"
+HERMIT_CHANNEL="$(basename "${HERMIT_DIST_URL}")"
+export HERMIT_CHANNEL
+export HERMIT_EXE=${HERMIT_EXE:-${HERMIT_STATE_DIR}/pkg/hermit@${HERMIT_CHANNEL}/hermit}
+
+if [ ! -x "${HERMIT_EXE}" ]; then
+ echo "Bootstrapping ${HERMIT_EXE} from ${HERMIT_DIST_URL}" 1>&2
+ INSTALL_SCRIPT="$(mktemp)"
+ # This value must match that of the install script
+ INSTALL_SCRIPT_SHA256="180e997dd837f839a3072a5e2f558619b6d12555cd5452d3ab19d87720704e38"
+ if [ "${INSTALL_SCRIPT_SHA256}" = "BYPASS" ]; then
+ curl -fsSL "${HERMIT_DIST_URL}/install.sh" -o "${INSTALL_SCRIPT}"
+ else
+ # Install script is versioned by its sha256sum value
+ curl -fsSL "${HERMIT_DIST_URL}/install-${INSTALL_SCRIPT_SHA256}.sh" -o "${INSTALL_SCRIPT}"
+ # Verify install script's sha256sum
+ openssl dgst -sha256 "${INSTALL_SCRIPT}" | \
+ awk -v EXPECTED="$INSTALL_SCRIPT_SHA256" \
+ '$2!=EXPECTED {print "Install script sha256 " $2 " does not match " EXPECTED; exit 1}'
+ fi
+ /bin/bash "${INSTALL_SCRIPT}" 1>&2
+fi
+
+exec "${HERMIT_EXE}" --level=fatal exec "$0" -- "$@"
diff --git a/jvm-runtime/kotlin/scaffolding/bin/hermit.hcl b/jvm-runtime/kotlin/scaffolding/bin/hermit.hcl
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jar b/jvm-runtime/kotlin/scaffolding/bin/jar
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jar
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jarsigner b/jvm-runtime/kotlin/scaffolding/bin/jarsigner
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jarsigner
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/java b/jvm-runtime/kotlin/scaffolding/bin/java
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/java
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/javac b/jvm-runtime/kotlin/scaffolding/bin/javac
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/javac
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/javadoc b/jvm-runtime/kotlin/scaffolding/bin/javadoc
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/javadoc
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/javap b/jvm-runtime/kotlin/scaffolding/bin/javap
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/javap
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jcmd b/jvm-runtime/kotlin/scaffolding/bin/jcmd
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jcmd
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jconsole b/jvm-runtime/kotlin/scaffolding/bin/jconsole
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jconsole
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jdb b/jvm-runtime/kotlin/scaffolding/bin/jdb
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jdb
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jdeprscan b/jvm-runtime/kotlin/scaffolding/bin/jdeprscan
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jdeprscan
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jdeps b/jvm-runtime/kotlin/scaffolding/bin/jdeps
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jdeps
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jfr b/jvm-runtime/kotlin/scaffolding/bin/jfr
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jfr
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jhsdb b/jvm-runtime/kotlin/scaffolding/bin/jhsdb
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jhsdb
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jimage b/jvm-runtime/kotlin/scaffolding/bin/jimage
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jimage
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jinfo b/jvm-runtime/kotlin/scaffolding/bin/jinfo
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jinfo
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jlink b/jvm-runtime/kotlin/scaffolding/bin/jlink
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jlink
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jmap b/jvm-runtime/kotlin/scaffolding/bin/jmap
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jmap
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jmod b/jvm-runtime/kotlin/scaffolding/bin/jmod
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jmod
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jpackage b/jvm-runtime/kotlin/scaffolding/bin/jpackage
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jpackage
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jps b/jvm-runtime/kotlin/scaffolding/bin/jps
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jps
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jrunscript b/jvm-runtime/kotlin/scaffolding/bin/jrunscript
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jrunscript
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jshell b/jvm-runtime/kotlin/scaffolding/bin/jshell
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jshell
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jstack b/jvm-runtime/kotlin/scaffolding/bin/jstack
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jstack
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jstat b/jvm-runtime/kotlin/scaffolding/bin/jstat
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jstat
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/jstatd b/jvm-runtime/kotlin/scaffolding/bin/jstatd
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/jstatd
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/keytool b/jvm-runtime/kotlin/scaffolding/bin/keytool
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/keytool
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/mvn b/jvm-runtime/kotlin/scaffolding/bin/mvn
new file mode 120000
index 0000000000..9158f83b62
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/mvn
@@ -0,0 +1 @@
+.maven-3.9.8.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/mvnDebug b/jvm-runtime/kotlin/scaffolding/bin/mvnDebug
new file mode 120000
index 0000000000..9158f83b62
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/mvnDebug
@@ -0,0 +1 @@
+.maven-3.9.8.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/rmiregistry b/jvm-runtime/kotlin/scaffolding/bin/rmiregistry
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/rmiregistry
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/bin/serialver b/jvm-runtime/kotlin/scaffolding/bin/serialver
new file mode 120000
index 0000000000..cf7b7e07ba
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/bin/serialver
@@ -0,0 +1 @@
+.openjdk@17.pkg
\ No newline at end of file
diff --git a/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/ftl.toml b/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/ftl.toml
new file mode 100644
index 0000000000..c9bc601c3e
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/ftl.toml
@@ -0,0 +1,2 @@
+module = "{{ .Name }}"
+language = "kotlin"
diff --git a/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/pom.xml.tmpl b/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/pom.xml.tmpl
new file mode 100644
index 0000000000..a4ea755f42
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/pom.xml.tmpl
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+ ftl.{{ .Group }}
+ {{ .Name }}
+ 1.0-SNAPSHOT
+
+
+ xyz.block.ftl
+ ftl-build-parent-kotlin
+ 1.0-SNAPSHOT
+
+
+
diff --git a/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/src/main/kotlin/{{ .PackageDir }}/EchoVerb.kt b/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/src/main/kotlin/{{ .PackageDir }}/EchoVerb.kt
new file mode 100644
index 0000000000..595bba72d7
--- /dev/null
+++ b/jvm-runtime/kotlin/scaffolding/{{ .Name | camel | lower }}/src/main/kotlin/{{ .PackageDir }}/EchoVerb.kt
@@ -0,0 +1,9 @@
+package {{ .Group }}
+
+import xyz.block.ftl.Export
+import xyz.block.ftl.Verb
+
+
+@Export
+@Verb
+fun echo(req: String): String = "Hello, $req!"