diff --git a/.github/workflows/cc.yml b/.github/workflows/cc.yml index 32f61aadc0..ab985f345d 100644 --- a/.github/workflows/cc.yml +++ b/.github/workflows/cc.yml @@ -12,7 +12,7 @@ jobs: - uses: cashapp/activate-hermit@v1 - name: Update PR title run: | - orig="${{ github.event.pull_request.title }}" + orig=${{ toJSON(github.event.pull_request.title) }} modified="$(gptcc "$orig")" diff -u <(echo "$orig") <(echo "$modified") | tail +4 || true gh pr edit --title "$modified" diff --git a/cmd/ftl/cmd_init.go b/cmd/ftl/cmd_init.go index 2ce75f2c08..a97aecd8ea 100644 --- a/cmd/ftl/cmd_init.go +++ b/cmd/ftl/cmd_init.go @@ -8,11 +8,12 @@ import ( "reflect" "strings" - "github.com/TBD54566975/scaffolder" "github.com/alecthomas/errors" + "github.com/beevik/etree" "github.com/iancoleman/strcase" - goruntime "github.com/TBD54566975/ftl/go-runtime" + "github.com/TBD54566975/scaffolder" + "github.com/TBD54566975/ftl/internal" kotlinruntime "github.com/TBD54566975/ftl/kotlin-runtime" ) @@ -32,7 +33,13 @@ func (i initGoCmd) Run(parent *initCmd) error { if i.Name == "" { i.Name = filepath.Base(i.Dir) } - return errors.WithStack(scaffold(goruntime.Files, parent.Hermit, i.Dir, i)) + tmpDir, err := unzipToTmpDir(kotlinruntime.Files) + if err != nil { + return errors.Wrap(err, "failed to unzip kotlin runtime") + } + defer os.RemoveAll(tmpDir) + + return errors.WithStack(scaffold(parent.Hermit, tmpDir, i.Dir, i)) } type initKotlinCmd struct { @@ -42,28 +49,87 @@ type initKotlinCmd struct { Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."` } -func (i *initKotlinCmd) Run(parent *initCmd) error { +func (i initKotlinCmd) Run(parent *initCmd) error { if i.Name == "" { i.Name = filepath.Base(i.Dir) } - return errors.WithStack(scaffold(kotlinruntime.Files, parent.Hermit, i.Dir, i)) + + if _, err := os.Stat(filepath.Join(i.Dir, i.Name)); err == nil { + return errors.Errorf("module directory %s already exists", filepath.Join(i.Dir, i.Name)) + } + + options := []scaffolder.Option{} + + // Update root POM if it already exists. + pomFile := filepath.Join(i.Dir, "pom.xml") + if _, err := os.Stat(pomFile); err == nil { + options = append(options, scaffolder.Exclude("pom.xml")) + if err := updatePom(pomFile, i.Name); err != nil { + return errors.WithStack(err) + } + } + + tmpDir, err := unzipToTmpDir(kotlinruntime.Files) + if err != nil { + return errors.Wrap(err, "failed to unzip kotlin runtime") + } + defer os.RemoveAll(tmpDir) + + return errors.WithStack(scaffold(parent.Hermit, tmpDir, i.Dir, initKotlinContext{initKotlinCmd: i}, options...)) } -func scaffold(reader *zip.Reader, hermit bool, dir string, ctx any) error { - tmpDir, err := os.MkdirTemp("", "ftl-init-*") +func updatePom(pomFile, name string) error { + tree := etree.NewDocument() + err := tree.ReadFromFile(pomFile) if err != nil { return errors.WithStack(err) } - defer os.RemoveAll(tmpDir) - err = internal.UnzipDir(reader, tmpDir) + + // Add new module entry to root of XML file + root := tree.Root() + modules := root.SelectElement("modules") + if modules == nil { + modules = root.CreateElement("modules") + } + modules.CreateText(" ") + module := modules.CreateElement("module") + module.SetText("ftl-module-" + name) + modules.CreateText("\n ") + + // Write updated XML file back to disk + err = tree.WriteToFile(pomFile) if err != nil { return errors.WithStack(err) } + return nil +} + +type initKotlinContext struct { + initKotlinCmd + Modules []string + Prelude string + Postlude string +} + +func unzipToTmpDir(reader *zip.Reader) (string, error) { + tmpDir, err := os.MkdirTemp("", "ftl-init-*") + if err != nil { + return "", errors.WithStack(err) + } + err = internal.UnzipDir(reader, tmpDir) + if err != nil { + return "", errors.WithStack(err) + } + return tmpDir, nil +} + +func scaffold(hermit bool, source string, destination string, ctx any, options ...scaffolder.Option) error { opts := []scaffolder.Option{scaffolder.Functions(scaffoldFuncs), scaffolder.Exclude("go.mod")} if !hermit { opts = append(opts, scaffolder.Exclude("bin")) } - if err := scaffolder.Scaffold(tmpDir, dir, ctx, opts...); err != nil { + opts = append(opts, options...) + if err := scaffolder.Scaffold(source, destination, ctx, opts...); err != nil { return errors.Wrap(err, "failed to scaffold") } return nil diff --git a/go.mod b/go.mod index 59ced25aee..86fa90f425 100644 --- a/go.mod +++ b/go.mod @@ -68,6 +68,7 @@ require ( github.com/alecthomas/errors v0.4.0 github.com/alecthomas/participle/v2 v2.0.0 github.com/alecthomas/types v0.7.1 + github.com/beevik/etree v1.2.0 github.com/deckarep/golang-set/v2 v2.3.0 github.com/gofrs/flock v0.8.1 github.com/iancoleman/strcase v0.2.0 diff --git a/go.sum b/go.sum index ccf692f908..838a981a51 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVK github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/amacneil/dbmate/v2 v2.7.0 h1:aaTyfVPxf01KyIx5E0i/OATygrit2hjivyKSX0YSJxI= github.com/amacneil/dbmate/v2 v2.7.0/go.mod h1:I13evRylGros6OXuJij+oskvr+YMaPvSD9Z7PNucXWA= +github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw= +github.com/beevik/etree v1.2.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc= github.com/bool64/dev v0.2.31 h1:OS57EqYaYe2M/2bw9uhDCIFiZZwywKFS/4qMLN6JUmQ= github.com/bool64/dev v0.2.31/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg= github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= diff --git a/kotlin-runtime/scaffolding/ftl-module-{{ .Name | lower }}/pom.xml b/kotlin-runtime/scaffolding/ftl-module-{{ .Name | lower }}/pom.xml index 76bbd57d2c..5b6c7cafee 100644 --- a/kotlin-runtime/scaffolding/ftl-module-{{ .Name | lower }}/pom.xml +++ b/kotlin-runtime/scaffolding/ftl-module-{{ .Name | lower }}/pom.xml @@ -12,6 +12,10 @@ 1.0-SNAPSHOT + + {{ .Name | lower }} + + diff --git a/kotlin-runtime/scaffolding/pom.xml b/kotlin-runtime/scaffolding/pom.xml index f7dfe6ddf9..48c7f36176 100644 --- a/kotlin-runtime/scaffolding/pom.xml +++ b/kotlin-runtime/scaffolding/pom.xml @@ -136,7 +136,7 @@ target/dependency/ftl-generator.jar --endpoint=http://127.0.0.1:8892 --dest=${project.build.directory} - --module={{ .Name | camel | lower }} + --module=${ftlModuleName} --module-client-suffix=ModuleClient diff --git a/scripts/integration-tests b/scripts/integration-tests index 1aa2c3a683..1727b02303 100755 --- a/scripts/integration-tests +++ b/scripts/integration-tests @@ -13,19 +13,12 @@ error() { build_release() { info "Building release" - bit build/release/ftl-controller \ - build/release/ftl-runner \ - build/release/ftl \ + bit build/release/ftl \ kotlin-runtime/ftl-runtime/target/ftl-runtime-1.0-SNAPSHOT.jar \ kotlin-runtime/ftl-generator/target/ftl-generator-1.0-SNAPSHOT-jar-with-dependencies.jar \ build/template/ftl/jars/ftl-runtime.jar } -wipe_database() { - info "Initialising database" - ftl-initdb --recreate -} - wait_for() { info "Waiting for $1" for _ in {1..240}; do @@ -45,7 +38,7 @@ stop_cluster() { start_cluster() { info "Starting cluster" - goreman -logtime=false -f Procfile.integration start & + ftl serve --recreate & wait_for "cluster to become ready" "ftl status" trap stop_cluster EXIT INT TERM } @@ -59,27 +52,29 @@ deploy_echo_kotlin() ( deploy_fresh_kotlin() ( info "Deploying newly initialised Kotlin module" - rm -rf build/echo2 - ftl init kotlin build/modules echo2 - cd build/modules/ftl-module-echo2 + IT_MODULES=build/modules + rm -rf "${IT_MODULES}" + ftl init kotlin "${IT_MODULES}" echo2 + ftl init kotlin "${IT_MODULES}" echo3 + cd "${IT_MODULES}" mvn compile - ftl deploy target + ftl deploy ftl-module-echo2/target + ftl deploy ftl-module-echo3/target ) deploy_time_go() ( info "Deploying time" cd examples # Pull a supported platforms from the cluster. - platform="$(ftl status | jq -r '.runners[].labels | "\(.os)-\(.arch)"' | sort | uniq | head -1)" + platform="$(ftl status | jq -r '(.runners // [])[].labels | "\(.os)-\(.arch)"' | sort | uniq | head -1)" ftl-go --os "${platform%-*}" --arch "${platform#*-}" deploy time ) wait_for_deploys() { - wait_for "deployments to come up" 'ftl status | jq -r ".routes[].module" | sort | paste -sd " " - | grep -q "echo echo2 time"' + wait_for "deployments to come up" 'ftl status | jq -r "(.routes // [])[].module" | sort | paste -sd " " - | grep -q "echo echo2 echo3 time"' } build_release -wipe_database start_cluster # Cluster is up, start interacting with it. @@ -95,4 +90,8 @@ message="$(ftl call echo.echo '{"name": "Alice"}' | jq -r .message)" message="$(ftl call echo2.echo '{"name": "Alice"}' | jq -r .message)" [[ "$message" =~ "Hello, Alice!" ]] || error "Unexpected response from echo2: $message" + +message="$(ftl call echo3.echo '{"name": "Alice"}' | jq -r .message)" +[[ "$message" =~ "Hello, Alice!" ]] || error "Unexpected response from echo2: $message" + info "Success!"