Skip to content

Commit

Permalink
feat: add "ftl init" with an initial scaffolder for Go
Browse files Browse the repository at this point in the history
Will add Kotlin in a followup.

This currently works, but `ftl-go deploy` needs to be adjusted to
support go.mod in the same directory as ftl.toml.

```
ftl init go ./examples/foo --go-module=github.com/TBD54566975/ftl/examples/foo
```
  • Loading branch information
alecthomas committed Oct 13, 2023
1 parent cb0495e commit 56e4cd0
Show file tree
Hide file tree
Showing 22 changed files with 599 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ reflex.conf
/node_modules
*.tsbuildinfo
generated_ftl_module.go

scaffolding.zip
dist/
25 changes: 20 additions & 5 deletions Bitfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,27 @@ NODE_MODULES_IN = console/client/package{,-lock}.json
build: install -m 0700 -d %{OUT}

# Build all binaries
implicit %{RELEASE}/%{1}: cmd/*
inputs: %{RELEASE} %{GO_SOURCES} %{CLIENT_OUT}
build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/%{1}
# implicit %{RELEASE}/%{1}: cmd/*
# inputs: %{RELEASE} %{GO_SOURCES} %{CLIENT_OUT}
# build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/%{1}

#%{RELEASE}/ftl-controller: %{RELEASE} %{GO_SOURCES} %{CLIENT_OUT}
# build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/ftl-controller
%{RELEASE}/ftl-controller: %{RELEASE} %{GO_SOURCES} %{CLIENT_OUT} cmd/ftl-controller/**/*.go
build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/ftl-controller

%{RELEASE}/ftl-runner: %{RELEASE} %{GO_SOURCES} cmd/ftl-runner/**/*.go
build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/ftl-runner

%{RELEASE}/ftl: %{RELEASE} %{GO_SOURCES} cmd/ftl/**/*.go go-runtime/scaffolding.zip
build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/ftl

%{RELEASE}/ftl-initdb: %{RELEASE} %{GO_SOURCES} cmd/ftl-initdb/**/*.go
build: go build -o %{OUT} -tags release -ldflags "-X main.version=%{VERSION} -X main.timestamp=$(date +%s)" ./cmd/ftl-initdb

# Release builds include zipped scaffolding becaused raw go:embed doesn't
# preserve permissions or symlinks. Irritating.
go-runtime/scaffolding.zip: go-runtime/scaffolding/*
cd go-runtime/scaffolding
build: zip -q --symlinks -r ../scaffolding.zip .

%{SCHEMA_OUT}: %{SCHEMA_IN}
build:
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ release: build/release/ftl-controller build/release/ftl-runner build/release/ftl
build/release/%: console/client/dist/index.html
go build -o $@ -tags release -ldflags "-X main.version=$(VERSION) -X main.timestamp=$(shell date +%s)" ./cmd/$(shell basename $@)

build/release/ftl:
cd go-runtime/scaffolding && zip -q --symlinks -r ../scaffolding.zip .
go build -o $@ -tags release -ldflags "-X main.version=$(VERSION) -X main.timestamp=$(shell date +%s)" ./cmd/ftl

$(KT_MVN_OUT): $(KT_RUNTIME_IN)
mvn -pl :ftl-runtime clean package

Expand Down
52 changes: 52 additions & 0 deletions cmd/ftl/cmd_init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"context"
"os"
"path/filepath"

"github.com/alecthomas/errors"

"github.com/TBD54566975/ftl/backend/common/exec"
"github.com/TBD54566975/ftl/backend/common/log"
goruntime "github.com/TBD54566975/ftl/go-runtime"
"github.com/TBD54566975/ftl/internal"
)

type initCmd struct {
Hermit bool `default:"true" help:"Include Hermit language-specific toolchain binaries in the module." negatable:""`
Go initGoCmd `cmd:"" help:"Initialize a new FTL Go module."`
Kotlin initKotlinCmd `cmd:"" help:"Initialize a new FTL Kotlin module."`
}

type initGoCmd struct {
Dir string `arg:"" default:"." type:"dir" help:"Directory to initialize the module in."`
Name string `help:"Name of the FTL module (defaults to name of directory)."`
GoModule string `required:"" help:"Go module path."`
}

func (i initGoCmd) Run(ctx context.Context, parent *initCmd) error {
if i.Name == "" {
i.Name = filepath.Base(i.Dir)
}
if err := internal.Scaffold(goruntime.Files, i.Dir, i); err != nil {
return errors.WithStack(err)
}
if !parent.Hermit {
if err := os.RemoveAll(filepath.Join(i.Dir, "bin")); err != nil {
return errors.WithStack(err)
}
}
if err := exec.Command(ctx, log.Info, i.Dir, "go", "mod", "tidy").Run(); err != nil {
return errors.WithStack(err)
}
return nil
}

type initKotlinCmd struct {
Dir string `arg:"" default:"." help:"Directory to initialize the module in."`
}

func (i *initKotlinCmd) Run() error {
panic("??")
}
1 change: 1 addition & 0 deletions cmd/ftl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type CLI struct {
Authenticators map[string]string `help:"Authenticators to use for FTL endpoints." mapsep:"," env:"FTL_AUTHENTICATORS" placeholder:"HOST=EXE,…"`

Status statusCmd `cmd:"" help:"Show FTL status."`
Init initCmd `cmd:"" help:"Initialize a new FTL module."`
PS psCmd `cmd:"" help:"List deployments."`
Serve serveCmd `cmd:"" help:"Start the FTL server."`
Call callCmd `cmd:"" help:"Call an FTL function."`
Expand Down
18 changes: 0 additions & 18 deletions examples/echo/_ftl/main.go

This file was deleted.

18 changes: 0 additions & 18 deletions examples/time/_ftl/main.go

This file was deleted.

22 changes: 22 additions & 0 deletions go-runtime/devel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build !release

package goruntime

import (
"io/fs"
"os"
"os/exec"
"path/filepath"
"strings"
)

// Files is the FTL Go runtime scaffolding files.
var Files = func() fs.FS {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
out, err := cmd.CombinedOutput()
if err != nil {
panic(err)
}
dir := filepath.Join(strings.TrimSpace(string(out)), "go-runtime", "scaffolding")
return os.DirFS(dir)
}()
25 changes: 25 additions & 0 deletions go-runtime/release.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build release

package goruntime

import (
"archive/zip"
"bytes"
_ "embed"
"io/fs"
)

//go:embed scaffolding.zip
var archive []byte

// Files is the FTL Go runtime scaffolding files.
//
// scaffolding.zip can be generated by running `bit go-runtime/scaffolding.zip`
// or indirectly via `bit build/release/ftl`.
var Files fs.FS = func() fs.FS {
zr, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
if err != nil {
panic(err)
}
return zr
}()
1 change: 1 addition & 0 deletions go-runtime/scaffolding/bin/.go-1.21.3.pkg
7 changes: 7 additions & 0 deletions go-runtime/scaffolding/bin/README.hermit.md
Original file line number Diff line number Diff line change
@@ -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.
21 changes: 21 additions & 0 deletions go-runtime/scaffolding/bin/activate-hermit
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions go-runtime/scaffolding/bin/go
1 change: 1 addition & 0 deletions go-runtime/scaffolding/bin/gofmt
43 changes: 43 additions & 0 deletions go-runtime/scaffolding/bin/hermit
Original file line number Diff line number Diff line change
@@ -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" -- "$@"
Empty file.
2 changes: 2 additions & 0 deletions go-runtime/scaffolding/ftl.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module = "{{ .Name | lower }}"
language = "go"
5 changes: 5 additions & 0 deletions go-runtime/scaffolding/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module {{ .GoModule }}

go 1.21.0

require github.com/TBD54566975/ftl latest
18 changes: 18 additions & 0 deletions go-runtime/scaffolding/{{ .Name | lower }}.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//ftl:module {{ .Name | lower }}
package {{ .Name | lower }}

import (
"context"
_ "github.com/TBD54566975/ftl/go-runtime/sdk" // Import the FTL SDK.
)

type {{ .Name | camel }}Request struct {
}

type {{ .Name | camel }}Response struct {
}

//ftl:verb
func {{ .Name | camel }}(ctx context.Context, req {{ .Name | camel }}Request) ({{ .Name | camel }}Response, error) {
return {{ .Name | camel }}Response{}, nil
}
47 changes: 47 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/BurntSushi/toml v1.3.2
github.com/alecthomas/kong v0.8.1
github.com/alecthomas/kong-toml v0.0.0-20230922031405-31cfb1264a3b
github.com/buildpacks-community/scafall v0.0.0-00010101000000-000000000000
github.com/go-logr/logr v1.2.4
github.com/golang/protobuf v1.5.3
github.com/google/uuid v1.3.0
Expand All @@ -32,26 +33,70 @@ require (
)

require (
github.com/AlecAivazis/survey/v2 v2.3.6 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/PuerkitoBio/goquery v1.8.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/alecthomas/repr v0.2.0 // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/coveooss/gotemplate/v3 v3.7.2 // indirect
github.com/coveooss/multilogger v0.5.2 // indirect
github.com/danieljoos/wincred v1.1.0 // indirect
github.com/drhodes/goLorem v0.0.0-20220328165741-da82e5b29246 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-git/go-git/v5 v5.5.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/huandu/xstrings v1.3.1 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/src-d/gcfg v1.4.0 // indirect
github.com/swaggest/refl v1.2.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/grpc v1.58.2 // indirect
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
gopkg.in/src-d/go-git.v4 v4.13.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
modernc.org/token v1.1.0 // indirect
)

Expand All @@ -78,3 +123,5 @@ require (
golang.org/x/mod v0.12.0
golang.org/x/sys v0.13.0 // indirect
)

replace github.com/buildpacks-community/scafall => ../scafall
Loading

0 comments on commit 56e4cd0

Please sign in to comment.