Skip to content

Commit

Permalink
🎌 flags: Take -a and -p from environment as default (#5)
Browse files Browse the repository at this point in the history
Take the values for the flags `-a` and `-p` from environment variables
as the default, still allowing them to be overridden on the command
line.

Sometimes using `servedir`, you may always want to listen on all
interfaces but the default invocation of `servedir` in a Makefile recipe
does not specify `-a`. You can now set `SERVEDIR_ALL_INTERFACES=true` in
the environment and these pre-canned `servedir` invocations can work
with your defaults.

This switches the use of the `flag` package to use an explicit `FlagSet`
instead of using the global one in the `flag` package. Using the global
one makes it a lot harder to test as flags get redefined.

This merges the following commits:
* Remove globals when parsing flags
* Take -a and -p from environment as default

     Makefile     |  2 +-
     main.go      | 51 +++++++++++++++++++++++++++++++++------------------
     main_test.go | 16 ++++++++++++++--
     3 files changed, 48 insertions(+), 21 deletions(-)

Pull-Request: #5
  • Loading branch information
camh- committed Jan 1, 2024
2 parents 9d44744 + 4ea0c40 commit f156a8c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# --- Global -------------------------------------------------------------------
O = out
COVERAGE = 60
COVERAGE = 66
VERSION ?= $(shell git describe --tags --dirty --always)

all: build test check-coverage lint ## build, test, check coverage and lint
Expand Down
51 changes: 33 additions & 18 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
// the current directory or a specified directory, on the next free
// ephemeral port or a specified port.
//
// go get foxygo.at/servedir
// servedir --help
// usage: servedir [-a] [-p <port>] [<dir>]
// go run foxygo.at/servedir@latest --help
// usage: servedir [-a] [-p <port>] [<dir>]
//
// Simple HTTP server, serving files from given directory.
// Simple HTTP server, serving files from given directory.
//
// -a listen on all interfaces not just localhost
// -p int
// port number (default: os chosen free port)
// <dir> defaults to current directory if not specified
// -a listen on all interfaces not just localhost (env: SERVEDIR_ALL_INTERFACES)
// -p int
// port number (default: os chosen free port) (env: SERVEDIR_PORT)
// <dir> defaults to current directory if not specified
package main

import (
Expand All @@ -21,14 +20,15 @@ import (
"net"
"net/http"
"os"
"strconv"
"strings"
)

func usage() {
w := flag.CommandLine.Output()
func usage(fs *flag.FlagSet) {
w := fs.Output()
fmt.Fprintf(w, "usage: %s [-a] [-p <port>] [<dir>]\n\n", os.Args[0])
fmt.Fprintf(w, "Simple HTTP server, serving files from given directory.\n\n")
flag.PrintDefaults()
fs.PrintDefaults()
fmt.Fprintf(w, " <dir> defaults to current directory if not specified\n")
}

Expand Down Expand Up @@ -67,19 +67,34 @@ type config struct {
listenAddr string
}

func parseFlags() config {
port := flag.Int("p", 0, "port number (default: os chosen free port)")
allInterfaces := flag.Bool("a", false, "listen on all interfaces not just localhost")
flag.Usage = usage
flag.Parse()
func parseFlags(args ...string) config {
var portDefault int = 0
if s, ok := os.LookupEnv("SERVEDIR_PORT"); ok {
if v, err := strconv.ParseInt(s, 0, strconv.IntSize); err == nil {
portDefault = int(v)
}
}

var allInterfacesDefault bool = false
if s, ok := os.LookupEnv("SERVEDIR_ALL_INTERFACES"); ok {
if v, err := strconv.ParseBool(s); err == nil {
allInterfacesDefault = v
}
}

fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
port := fs.Int("p", portDefault, "port number (default: os chosen free port)")
allInterfaces := fs.Bool("a", allInterfacesDefault, "listen on all interfaces not just localhost")
fs.Usage = func() { usage(fs) }
fs.Parse(args) //nolint:errcheck // ExitOnError means this does not return an error
return config{
dir: dir(flag.Args()),
dir: dir(fs.Args()),
listenAddr: listenAddr(*port, *allInterfaces),
}
}

func main() {
cfg := parseFlags()
cfg := parseFlags(os.Args[1:]...)
http.Handle("/", http.FileServer(http.Dir(cfg.dir)))
listener, err := net.Listen("tcp", cfg.listenAddr)
if err != nil {
Expand Down
16 changes: 14 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import (

func TestUsage(t *testing.T) {
w := bytes.NewBuffer(nil)
flag.CommandLine.SetOutput(w)
usage()
fs := flag.NewFlagSet("servedir", flag.ExitOnError)
fs.SetOutput(w)
usage(fs)
opts := `[-a] [-p <port>] [<dir>]`
desc := `Simple HTTP server, serving files from given directory.`
require.Contains(t, w.String(), opts)
Expand Down Expand Up @@ -60,3 +61,14 @@ func TestParseFlags(t *testing.T) {
got := parseFlags()
require.Equal(t, want, got)
}

func TestParseFlagsFromEnvironment(t *testing.T) {
want := config{
listenAddr: ":1",
dir: ".",
}
t.Setenv("SERVEDIR_ALL_INTERFACES", "true")
t.Setenv("SERVEDIR_PORT", "1")
got := parseFlags()
require.Equal(t, want, got)
}

0 comments on commit f156a8c

Please sign in to comment.