From ff3dba998080ee50328b06a2d37e770cecf0f672 Mon Sep 17 00:00:00 2001 From: Alessio Date: Sat, 19 Aug 2023 01:58:31 -0300 Subject: [PATCH] Add build option to embed static files in the built binary --- cmd/compile.sh | 4 +- internal/webserver/files.go | 10 +++++ internal/webserver/response_helpers.go | 55 +++++++++++++++++++------- internal/webserver/server.go | 19 +++------ 4 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 internal/webserver/files.go diff --git a/cmd/compile.sh b/cmd/compile.sh index 94bd136..eef7801 100755 --- a/cmd/compile.sh +++ b/cmd/compile.sh @@ -4,8 +4,8 @@ set -x set -e if [[ -n "$1" ]]; then - go build -ldflags="-s -w -X main.version_string=$1" -o tw ./twitter + go build -ldflags="-s -w -X main.version_string=$1 -X webserver.is_production=true" -o tw ./twitter else - go build -ldflags="-s -w" -o tw ./twitter + go build -ldflags="-s -w -X webserver.is_production=true" -o tw ./twitter fi chmod +x tw diff --git a/internal/webserver/files.go b/internal/webserver/files.go new file mode 100644 index 0000000..8b55c8b --- /dev/null +++ b/internal/webserver/files.go @@ -0,0 +1,10 @@ +package webserver + +import ( + "embed" +) + +//go:embed "tpl" "static" +var embedded_files embed.FS + +var use_embedded = false diff --git a/internal/webserver/response_helpers.go b/internal/webserver/response_helpers.go index c94f37f..2b35307 100644 --- a/internal/webserver/response_helpers.go +++ b/internal/webserver/response_helpers.go @@ -5,8 +5,11 @@ import ( "fmt" "html/template" "io" + "io/fs" "net/http" + "path" "path/filepath" + "runtime" "runtime/debug" "github.com/Masterminds/sprig/v3" @@ -20,6 +23,32 @@ func panic_if(err error) { } } +var this_dir string + +func init() { + _, this_file, _, _ := runtime.Caller(0) // `this_file` is absolute path to this source file + this_dir = path.Dir(this_file) +} + +func get_filepath(s string) string { + if use_embedded { + return s + } + return path.Join(this_dir, s) +} + +func glob(path string) []string { + var ret []string + var err error + if use_embedded { + ret, err = fs.Glob(embedded_files, get_filepath(path)) + } else { + ret, err = filepath.Glob(get_filepath(path)) + } + panic_if(err) + return ret +} + // func (app *Application) error_400(w http.ResponseWriter) { // http.Error(w, "Bad Request", 400) // } @@ -52,11 +81,7 @@ type TweetCollection interface { // Creates a template from the given template file using all the available partials. // Calls `app.buffered_render` to render the created template. func (app *Application) buffered_render_tweet_page(w http.ResponseWriter, tpl_file string, data TweetCollection) { - partials, err := filepath.Glob(get_filepath("tpl/includes/*.tpl")) - panic_if(err) - tweet_partials, err := filepath.Glob(get_filepath("tpl/tweet_page_includes/*.tpl")) - panic_if(err) - partials = append(partials, tweet_partials...) + partials := append(glob("tpl/includes/*.tpl"), glob("tpl/tweet_page_includes/*.tpl")...) r := renderer{ Funcs: func_map(template.FuncMap{ @@ -77,8 +102,7 @@ func (app *Application) buffered_render_tweet_page(w http.ResponseWriter, tpl_fi // Creates a template from the given template file using all the available partials. // Calls `app.buffered_render` to render the created template. func (app *Application) buffered_render_basic_page(w http.ResponseWriter, tpl_file string, data interface{}) { - partials, err := filepath.Glob(get_filepath("tpl/includes/*.tpl")) - panic_if(err) + partials := glob("tpl/includes/*.tpl") r := renderer{ Funcs: func_map(template.FuncMap{"active_user": app.get_active_user}), @@ -90,11 +114,7 @@ func (app *Application) buffered_render_basic_page(w http.ResponseWriter, tpl_fi } func (app *Application) buffered_render_tweet_htmx(w http.ResponseWriter, tpl_name string, data TweetCollection) { - partials, err := filepath.Glob(get_filepath("tpl/includes/*.tpl")) - panic_if(err) - tweet_partials, err := filepath.Glob(get_filepath("tpl/tweet_page_includes/*.tpl")) - panic_if(err) - partials = append(partials, tweet_partials...) + partials := append(glob("tpl/includes/*.tpl"), glob("tpl/tweet_page_includes/*.tpl")...) r := renderer{ Funcs: func_map(template.FuncMap{ @@ -113,8 +133,7 @@ func (app *Application) buffered_render_tweet_htmx(w http.ResponseWriter, tpl_na } func (app *Application) buffered_render_basic_htmx(w http.ResponseWriter, tpl_name string, data interface{}) { - partials, err := filepath.Glob(get_filepath("tpl/includes/*.tpl")) - panic_if(err) + partials := glob("tpl/includes/*.tpl") r := renderer{ Funcs: func_map(template.FuncMap{"active_user": app.get_active_user}), @@ -147,7 +166,13 @@ type renderer struct { // Render the given template using a bytes.Buffer. This avoids the possibility of failing partway // through the rendering, and sending an imcomplete response with "Bad Request" or "Server Error" at the end. func (r renderer) BufferedRender(w io.Writer) { - tpl, err := template.New("").Funcs(r.Funcs).ParseFiles(r.Filenames...) + var tpl *template.Template + var err error + if use_embedded { + tpl, err = template.New("").Funcs(r.Funcs).ParseFS(embedded_files, r.Filenames...) + } else { + tpl, err = template.New("").Funcs(r.Funcs).ParseFiles(r.Filenames...) + } panic_if(err) buf := new(bytes.Buffer) diff --git a/internal/webserver/server.go b/internal/webserver/server.go index b9af5da..eded00f 100644 --- a/internal/webserver/server.go +++ b/internal/webserver/server.go @@ -9,7 +9,6 @@ import ( "net/http" "os" "path" - "runtime" "strconv" "strings" "time" @@ -88,17 +87,6 @@ func get_default_user() scraper.User { } } -var this_dir string - -func init() { - _, this_file, _, _ := runtime.Caller(0) // `this_file` is absolute path to this source file - this_dir = path.Dir(this_file) -} - -func get_filepath(s string) string { - return path.Join(this_dir, s) -} - // Manual router implementation. // I don't like the weird matching behavior of http.ServeMux, and it's not hard to write by hand. func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -110,7 +98,12 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) { parts := strings.Split(r.URL.Path, "/")[1:] switch parts[0] { case "static": - http.StripPrefix("/static", http.FileServer(http.Dir(get_filepath("static")))).ServeHTTP(w, r) + if use_embedded { + // Serve directly from the embedded files + http.FileServer(http.FS(embedded_files)).ServeHTTP(w, r) + } else { + http.StripPrefix("/static", http.FileServer(http.Dir(get_filepath("static")))).ServeHTTP(w, r) + } case "tweet": app.TweetDetail(w, r) case "content":