diff --git a/Dockerfile b/Dockerfile index 8124bc9..b10ffcb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,65 +7,57 @@ ################################################################################ # Create a stage for building the application. ARG GO_VERSION=1.21 -ARG PLATFORM_VERSION=linux/arm64 -FROM --platform=${PLATFORM_VERSION} golang:${GO_VERSION} AS build +FROM golang:${GO_VERSION}-alpine AS build WORKDIR /src -COPY . . + +# Install build dependencies +RUN apk add --no-cache git + +# Copy only the go.mod and go.sum files first +COPY go.mod go.sum ./ # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds. -# Leverage bind mounts to go.sum and go.mod to avoid having to copy them into -# the container. RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,source=go.sum,target=go.sum \ - --mount=type=bind,source=go.mod,target=go.mod \ go mod download -x -# Build the application. +# Copy the rest of the source code +COPY . . + +# Build the application for ARM64 architecture. # Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds. -# Leverage a bind mount to the current directory to avoid having to copy the -# source code into the container. RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,target=. \ - CGO_ENABLED=0 go build -o /bin/server . + GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o /bin/server . RUN --mount=type=cache,target=/go/pkg/mod/ \ - --mount=type=bind,target=. \ - CGO_ENABLED=0 go build -o /bin/bun ./cmd/bun/main.go + GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o /bin/bun ./cmd/bun/main.go ################################################################################ # Create a new stage for running the application that contains the minimal -# runtime dependencies for the application. This often uses a different base -# image from the build stage where the necessary files are copied from the build -# stage. -# -# The example below uses the alpine image as the foundation for running the app. -# By specifying the "latest" tag, it will also use whatever happens to be the -# most recent version of that image when you build your Dockerfile. If -# reproducability is important, consider using a versioned tag -# (e.g., alpine:3.17.2) or SHA (e.g., alpine@sha256:c41ab5c992deb4fe7e5da09f67a8804a46bd0592bfdf0b1847dde0e0889d2bff). -FROM alpine:3 AS final +# runtime dependencies for the application. +FROM --platform=linux/arm64 alpine:3 AS final # Install any runtime dependencies that are needed to run your application. # Leverage a cache mount to /var/cache/apk/ to speed up subsequent builds. RUN --mount=type=cache,target=/var/cache/apk \ - apk --update add \ + apk --update --no-cache add \ ca-certificates \ tzdata \ && \ update-ca-certificates -# Copy the executable from the "build" stage. +# Copy the executables from the "build" stage. COPY --from=build /bin/server /bin/ COPY --from=build /bin/bun /bin/bun/ COPY entrypoint.sh /bin/entrypoint.sh -COPY . /src + +# Copy only necessary source files +COPY --from=build /src/go.mod /src/go.sum /src/ # Make the entrypoint script executable RUN chmod +x /bin/entrypoint.sh # Create a non-privileged user that the app will run under. -# See https://docs.docker.com/go/dockerfile-user-best-practices/ ARG UID=10001 RUN adduser \ --disabled-password \ diff --git a/config/core-config.go b/config/core-config.go index 9b9f039..26d0ab0 100644 --- a/config/core-config.go +++ b/config/core-config.go @@ -11,37 +11,39 @@ import ( ) var ( - Env string - UserMaxUrlCount int - Domain string - AuthRedirectUrl string - DbUrl string + Env string + UserMaxUrlCount int + Domain string + AuthRedirectUrl string + DbUrl string DbMaxOpenConnections int - GoogleClientId string - GoogleClientSecret string - GoogleRedirectUrl string - TokenValidity int - JwtSecret string - JwtValidity int - JwtIssuer string - AllowedOrigin string - Port string + GoogleClientId string + GoogleClientSecret string + GoogleRedirectUrl string + TokenValidity int + JwtSecret string + JwtValidity int + JwtIssuer string + AllowedOrigin string + Port string ) -func findAndLoadEnv(envFile string) { +func findAndLoadEnv(envFile string) error { cwd, err := os.Getwd() if err != nil { logger.Fatal("Could not get current working directory:", err) } + logger.Info("Starting search for .env file from:", cwd) + for { envPath := filepath.Join(cwd, envFile) if _, err := os.Stat(envPath); err == nil { if err := godotenv.Load(envPath); err != nil { - logger.Fatal("Error loading .env file:", err) + logger.Error("Error loading .env file:", err) } logger.Info("Loaded environment variables from:", envPath) - return + return nil } parent := filepath.Dir(cwd) @@ -51,7 +53,7 @@ func findAndLoadEnv(envFile string) { cwd = parent } - logger.Fatal("Could not find .env file at:", envFile) + return fmt.Errorf("could not find .env file: %s", envFile) } func loadEnv() { @@ -65,7 +67,10 @@ func loadEnv() { envFile = "environments/test.env" } - findAndLoadEnv(envFile) + if err := findAndLoadEnv(envFile); err != nil { + logger.Error("Failed to load .env file:", err) + return + } } func init() { @@ -109,6 +114,7 @@ func getEnvVar(key string) string { value := os.Getenv(key) if value == "" { logger.Fatal(fmt.Sprintf("Environment variable %s not set", key)) + os.Exit(1) } return value } diff --git a/docker-compose.yaml b/docker-compose.yaml index 120feee..d69affd 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -14,16 +14,38 @@ services: target: final ports: - 4001:4001 - -# The commented out section below is an example of how to define a PostgreSQL -# database that your application can use. `depends_on` tells Docker Compose to -# start the database before your application. The `db-data` volume persists the -# database data between container restarts. The `db-password` secret is used -# to set the database password. You must create `db/password.txt` and add -# a password of your choosing to it before running `docker compose up`. depends_on: db: condition: service_healthy + environment: + - DB_HOST=db + - DB_PORT=5432 + - DB_USER=tinysite + - DB_PASSWORD=tinysite + - DB_NAME=tinysite + - JWT_SECRET=secret + - JWT_ISSUER=tinysite + - DOMAIN=localhost + - PORT=4001 + - AUTH_REDIRECT_URL=http://localhost:4001/auth/callback + - DB_URL=postgres://tinysite:tinysite@db:5432/tinysite?sslmode=disable + - DB_MAX_OPEN_CONNECTIONS=20 + - GOOGLE_CLIENT_ID=1234567890 + - GOOGLE_CLIENT_SECRET=1234567890 + - ALLOWED_ORIGINS=http://localhost:3000 + - TOKEN_VALIDITY_IN_SECONDS=3600 + - JWT_VALIDITY_IN_HOURS=1 + - USER_MAX_URL_COUNT=100 + - GOOGLE_REDIRECT_URL=http://localhost:4001/auth/callback + # The commented out section below is an example of how to define a PostgreSQL + # database that your application can use. `depends_on` tells Docker Compose to + # start the database before your application. The `db-data` volume persists the + # database data between container restarts. The `db-password` secret is used + # to set the database password. You must create `db/password.txt` and add + # a password of your choosing to it before running `docker compose up`. + # depends_on: + # db: + # condition: service_healthy db: image: postgres user: postgres @@ -45,15 +67,14 @@ services: interval: 10s timeout: 5s retries: 5 - + adminer: image: adminer ports: - 9091:8080 - + # volumes: # db-data: # secrets: # db-password: # file: db/password.txt -