(Feat): Initial Commit
Some checks failed
Build & Test / build-test (push) Has been cancelled
Build & Test / swagger-codegen-cli (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled

This commit is contained in:
2026-07-03 19:41:31 +05:30
commit 7e940c83a7
461 changed files with 45002 additions and 0 deletions

11
cmd/README.md Normal file
View File

@@ -0,0 +1,11 @@
# `/cmd`
Main applications for this project.
Don't put a lot of code in the application directory. If you think the code can be imported and used in other projects, then it should live in the `/pkg` directory. If the code is not reusable or if you don't want others to reuse it, put that code in the `/internal` directory. You'll be surprised what others will do, so be explicit about your intentions!
We manage our applications via cobra ([`cobra-cli`](https://github.com/spf13/cobra-cli) is installed within the `Dockerfile` within the development stage, `/cmd` consumes the core [`cobra`](https://github.com/spf13/cobra) library), see:
* https://github.com/spf13/cobra#getting-started
* https://github.com/spf13/cobra-cli/blob/main/README.md#add-commands-to-a-project
Also see https://github.com/golang-standards/project-layout/tree/master/cmd

13
cmd/db/db.go Normal file
View File

@@ -0,0 +1,13 @@
package db
import (
"allaboutapps.dev/aw/go-starter/internal/util/command"
"github.com/spf13/cobra"
)
func New() *cobra.Command {
return command.NewSubcommandGroup("db",
newMigrate(),
newSeed(),
)
}

97
cmd/db/migrate.go Normal file
View File

@@ -0,0 +1,97 @@
package db
import (
"context"
"database/sql"
"fmt"
"allaboutapps.dev/aw/go-starter/internal/api"
"allaboutapps.dev/aw/go-starter/internal/config"
"allaboutapps.dev/aw/go-starter/internal/util"
"allaboutapps.dev/aw/go-starter/internal/util/command"
"github.com/rs/zerolog/log"
migrate "github.com/rubenv/sql-migrate"
"github.com/spf13/cobra"
)
func newMigrate() *cobra.Command {
return &cobra.Command{
Use: "migrate",
Short: "Executes all migrations which are not yet applied.",
Run: func(_ *cobra.Command, _ []string) {
migrateCmdFunc()
},
}
}
func migrateCmdFunc() {
err := command.WithServer(context.Background(), config.DefaultServiceConfigFromEnv(), func(ctx context.Context, s *api.Server) error {
log := util.LogFromContext(ctx)
n, err := ApplyMigrations(ctx, s.Config)
if err != nil {
log.Err(err).Msg("Error while applying migrations")
return err
}
log.Info().Int("appliedMigrationsCount", n).Msg("Successfully applied migrations")
return nil
})
if err != nil {
log.Fatal().Err(err).Msg("Failed to apply migrations")
}
}
func ApplyMigrations(ctx context.Context, serviceConfig config.Server) (int, error) {
log := util.LogFromContext(ctx)
// pin migrate to use the globally defined `migrations` table identifier
migrate.SetTable(config.DatabaseMigrationTable)
db, err := sql.Open("postgres", serviceConfig.Database.ConnectionString())
if err != nil {
return 0, fmt.Errorf("failed to open the database: %w", err)
}
defer db.Close()
if err := db.PingContext(ctx); err != nil {
return 0, fmt.Errorf("failed to ping the database: %w", err)
}
// In case an old default sql-migrate migration table (named "gorp_migrations") still exists we rename it to the new name equivalent
// in sync with the settings in dbconfig.yml and config.DatabaseMigrationTable.
if _, err := db.ExecContext(ctx, fmt.Sprintf("ALTER TABLE IF EXISTS gorp_migrations RENAME TO %s;", config.DatabaseMigrationTable)); err != nil {
return 0, fmt.Errorf("failed to rename migrations table: %w", err)
}
migrations := &migrate.FileMigrationSource{
Dir: config.DatabaseMigrationFolder,
}
missingMigrations, _, err := migrate.PlanMigration(db, "postgres", migrations, migrate.Up, 0)
if err != nil {
log.Err(err).Msg("Error while planning migrations")
return 0, fmt.Errorf("failed to plan migrations: %w", err)
}
var appliedMigrationsCount int
for i := 0; i < len(missingMigrations); i++ {
log.Info().Str("migrationId", missingMigrations[i].Id).Msg("Applying migration")
n, err := migrate.ExecMax(db, "postgres", migrations, migrate.Up, 1)
if err != nil {
log.Err(err).Msg("Error while applying migration")
return 0, fmt.Errorf("failed to apply migration: %w", err)
}
log.Info().Int("appliedMigrationsCount", n).Msg("Applied migration")
appliedMigrationsCount += n
if n == 0 {
break
}
}
return appliedMigrationsCount, nil
}

77
cmd/db/seed.go Normal file
View File

@@ -0,0 +1,77 @@
package db
import (
"context"
"database/sql"
"fmt"
"allaboutapps.dev/aw/go-starter/internal/api"
"allaboutapps.dev/aw/go-starter/internal/config"
data "allaboutapps.dev/aw/go-starter/internal/data/fixtures"
"allaboutapps.dev/aw/go-starter/internal/util"
"allaboutapps.dev/aw/go-starter/internal/util/command"
dbutil "allaboutapps.dev/aw/go-starter/internal/util/db"
"github.com/aarondl/sqlboiler/v4/boil"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
func newSeed() *cobra.Command {
return &cobra.Command{
Use: "seed",
Short: "Inserts or updates fixtures to the database.",
Long: `Uses upsert to add test data to the current environment.`,
Run: func(_ *cobra.Command, _ []string) {
seedCmdFunc()
},
}
}
func seedCmdFunc() {
err := command.WithServer(context.Background(), config.DefaultServiceConfigFromEnv(), func(ctx context.Context, s *api.Server) error {
log := util.LogFromContext(ctx)
err := ApplySeedFixtures(ctx, s.Config)
if err != nil {
log.Err(err).Msg("Error while applying seed fixtures")
return err
}
log.Info().Msg("Successfully applied seed fixtures")
return nil
})
if err != nil {
log.Fatal().Err(err).Msg("Failed to apply migrations")
}
}
func ApplySeedFixtures(ctx context.Context, config config.Server) error {
log := util.LogFromContext(ctx)
db, err := sql.Open("postgres", config.Database.ConnectionString())
if err != nil {
return fmt.Errorf("failed to open the database: %w", err)
}
defer db.Close()
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("failed to ping the database: %w", err)
}
// insert fixtures in an auto-managed db transaction
return dbutil.WithTransaction(ctx, db, func(tx boil.ContextExecutor) error {
fixtures := data.Upserts()
for _, fixture := range fixtures {
if err := fixture.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil {
log.Error().Err(err).Msg("Failed to upsert fixture")
return err
}
}
log.Info().Int("fixturesCount", len(fixtures)).Msg("Successfully upserted fixtures")
return nil
})
}

38
cmd/env/env.go vendored Normal file
View File

@@ -0,0 +1,38 @@
package env
import (
"encoding/json"
"fmt"
"allaboutapps.dev/aw/go-starter/internal/config"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
func New() *cobra.Command {
return &cobra.Command{
Use: "env",
Short: "Prints the env",
Long: `Prints the currently applied env
You may use this cmd to get an overview about how
your ENV_VARS are bound by the server config.
Please note that certain secrets are automatically
removed from this output.`,
Run: func(_ *cobra.Command /* cmd */, _ []string /* args */) {
runEnv()
},
}
}
func runEnv() {
config := config.DefaultServiceConfigFromEnv()
result, err := json.MarshalIndent(config, "", " ")
if err != nil {
log.Fatal().Err(err).Msg("Failed to marshal the env")
}
//nolint:forbidigo
fmt.Println(string(result))
}

89
cmd/probe/liveness.go Normal file
View File

@@ -0,0 +1,89 @@
package probe
import (
"context"
"database/sql"
"fmt"
"allaboutapps.dev/aw/go-starter/internal/api"
"allaboutapps.dev/aw/go-starter/internal/api/handlers/common"
"allaboutapps.dev/aw/go-starter/internal/config"
"allaboutapps.dev/aw/go-starter/internal/util"
"allaboutapps.dev/aw/go-starter/internal/util/command"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
type LivenessFlags struct {
Verbose bool
}
func newLiveness() *cobra.Command {
var flags LivenessFlags
cmd := &cobra.Command{
Use: "liveness",
Short: "Runs liveness probes",
Long: `Runs connection livenesss probes
This command triggers the same livenesss probes as in
/-/healthy (apart from the actual server.ready
probe) and prints the results to stdout. Fails with
non zero exitcode on encountered errors.
A typical usecase of this command are liveness probes
to take action if dependant services (e.g. DB, NFS
mounts) become unstable. You may also use this to
ensure all requirements are fulfilled before starting
the app server.`,
Run: func(_ *cobra.Command, _ []string) {
livenessCmdFunc(flags)
},
}
cmd.Flags().BoolVarP(&flags.Verbose, verboseFlag, "v", false, "Show verbose output.")
return cmd
}
func livenessCmdFunc(flags LivenessFlags) {
err := command.WithServer(context.Background(), config.DefaultServiceConfigFromEnv(), func(ctx context.Context, s *api.Server) error {
log := util.LogFromContext(ctx)
errs, err := runLiveness(ctx, s.Config, flags)
if err != nil {
log.Fatal().Err(err).Msg("Failed to run liveness probes")
}
if len(errs) > 0 {
log.Fatal().Errs("errs", errs).Msg("Unhealthy.")
}
return nil
})
if err != nil {
log.Fatal().Err(err).Msg("Failed to run liveness probes")
}
}
func runLiveness(ctx context.Context, config config.Server, flags LivenessFlags) ([]error, error) {
log := util.LogFromContext(ctx)
db, err := sql.Open("postgres", config.Database.ConnectionString())
if err != nil {
log.Error().Err(err).Msg("Failed to open the database")
return nil, fmt.Errorf("failed to open the database: %w", err)
}
defer db.Close()
livenessCtx, cancel := context.WithTimeout(context.Background(), config.Management.LivenessTimeout)
defer cancel()
str, errs := common.ProbeLiveness(livenessCtx, db, config.Management.ProbeWriteablePathsAbs, config.Management.ProbeWriteableTouchfile)
if flags.Verbose {
log.Info().Msg(str)
}
return errs, nil
}

17
cmd/probe/probe.go Normal file
View File

@@ -0,0 +1,17 @@
package probe
import (
"allaboutapps.dev/aw/go-starter/internal/util/command"
"github.com/spf13/cobra"
)
const (
verboseFlag string = "verbose"
)
func New() *cobra.Command {
return command.NewSubcommandGroup("probe",
newLiveness(),
newReadiness(),
)
}

89
cmd/probe/readiness.go Normal file
View File

@@ -0,0 +1,89 @@
package probe
import (
"context"
"database/sql"
"fmt"
"allaboutapps.dev/aw/go-starter/internal/api"
"allaboutapps.dev/aw/go-starter/internal/api/handlers/common"
"allaboutapps.dev/aw/go-starter/internal/config"
"allaboutapps.dev/aw/go-starter/internal/util"
"allaboutapps.dev/aw/go-starter/internal/util/command"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
type ReadinessFlags struct {
Verbose bool
}
func newReadiness() *cobra.Command {
var flags ReadinessFlags
cmd := &cobra.Command{
Use: "readiness",
Short: "Runs readiness probes",
Long: `Runs connection readinesss probes
This command triggers the same readinesss probes as in
/-/ready (apart from the actual server.ready
probe) and prints the results to stdout. Fails with
non zero exitcode on encountered errors.
A typical usecase of this command are readiness probes
to take action if dependant services (e.g. DB, NFS
mounts) become unstable. You may also use this to
ensure all requirements are fulfilled before starting
the app server.`,
Run: func(_ *cobra.Command, _ []string /* args */) {
readinessCmdFunc(flags)
},
}
cmd.Flags().BoolVarP(&flags.Verbose, verboseFlag, "v", false, "Show verbose output.")
return cmd
}
func readinessCmdFunc(flags ReadinessFlags) {
err := command.WithServer(context.Background(), config.DefaultServiceConfigFromEnv(), func(ctx context.Context, s *api.Server) error {
log := util.LogFromContext(ctx)
errs, err := RunReadiness(ctx, s.Config, flags)
if err != nil {
log.Fatal().Err(err).Msg("Failed to run readiness probes")
}
if len(errs) > 0 {
log.Fatal().Errs("errs", errs).Msg("Unhealthy.")
}
return nil
})
if err != nil {
log.Fatal().Err(err).Msg("Failed to run readiness probes")
}
}
func RunReadiness(ctx context.Context, config config.Server, flags ReadinessFlags) ([]error, error) {
log := util.LogFromContext(ctx)
db, err := sql.Open("postgres", config.Database.ConnectionString())
if err != nil {
log.Error().Err(err).Msg("Failed to open the database")
return nil, fmt.Errorf("failed to open the database: %w", err)
}
defer db.Close()
readinessCtx, cancel := context.WithTimeout(context.Background(), config.Management.ReadinessTimeout)
defer cancel()
str, errs := common.ProbeReadiness(readinessCtx, db, config.Management.ProbeWriteablePathsAbs)
if flags.Verbose {
log.Info().Msg(str)
}
return errs, nil
}

44
cmd/root.go Normal file
View File

@@ -0,0 +1,44 @@
package cmd
import (
"fmt"
"os"
"allaboutapps.dev/aw/go-starter/cmd/db"
"allaboutapps.dev/aw/go-starter/cmd/env"
"allaboutapps.dev/aw/go-starter/cmd/probe"
"allaboutapps.dev/aw/go-starter/cmd/server"
"allaboutapps.dev/aw/go-starter/internal/config"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Version: config.GetFormattedBuildArgs(),
Use: "app",
Short: config.ModuleName,
Long: fmt.Sprintf(`%v
A stateless RESTful JSON service written in Go.
Requires configuration through ENV.`, config.ModuleName),
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
rootCmd.SetVersionTemplate(`{{printf "%s\n" .Version}}`)
// attach the subcommands
rootCmd.AddCommand(
db.New(),
env.New(),
probe.New(),
server.New(),
)
if err := rootCmd.Execute(); err != nil {
log.Error().Err(err).Msg("Failed to execute root command")
os.Exit(1)
}
}

105
cmd/server/server.go Normal file
View File

@@ -0,0 +1,105 @@
package server
import (
"context"
"errors"
"net/http"
"os"
"os/signal"
"syscall"
"allaboutapps.dev/aw/go-starter/cmd/db"
"allaboutapps.dev/aw/go-starter/cmd/probe"
"allaboutapps.dev/aw/go-starter/internal/api"
"allaboutapps.dev/aw/go-starter/internal/api/router"
"allaboutapps.dev/aw/go-starter/internal/config"
"allaboutapps.dev/aw/go-starter/internal/util"
"allaboutapps.dev/aw/go-starter/internal/util/command"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
type Flags struct {
ProbeReadiness bool
ApplyMigrations bool
SeedFixtures bool
}
func New() *cobra.Command {
var flags Flags
cmd := &cobra.Command{
Use: "server",
Short: "Starts the server",
Long: `Starts the stateless RESTful JSON server
Requires configuration through ENV and
a fully migrated PostgreSQL database.`,
Run: func(_ *cobra.Command, _ []string) {
runServer(flags)
},
}
cmd.Flags().BoolVarP(&flags.ProbeReadiness, "probe", "p", false, "Probe readiness before startup.")
cmd.Flags().BoolVarP(&flags.ApplyMigrations, "migrate", "m", false, "Apply migrations before startup.")
cmd.Flags().BoolVarP(&flags.SeedFixtures, "seed", "s", false, "Seed fixtures into database before startup.")
return cmd
}
func runServer(flags Flags) {
err := command.WithServer(context.Background(), config.DefaultServiceConfigFromEnv(), func(ctx context.Context, s *api.Server) error {
log := util.LogFromContext(ctx)
if flags.ProbeReadiness {
errs, err := probe.RunReadiness(ctx, s.Config, probe.ReadinessFlags{
Verbose: true,
})
if err != nil {
log.Fatal().Err(err).Msg("Failed to run readiness probes")
}
if len(errs) > 0 {
log.Fatal().Errs("errs", errs).Msg("Unhealthy.")
}
}
if flags.ApplyMigrations {
_, err := db.ApplyMigrations(ctx, s.Config)
if err != nil {
log.Fatal().Err(err).Msg("Error while applying migrations")
}
}
if flags.SeedFixtures {
err := db.ApplySeedFixtures(ctx, s.Config)
if err != nil {
log.Fatal().Err(err).Msg("Error while applying seed fixtures")
}
}
err := router.Init(s)
if err != nil {
log.Fatal().Err(err).Msg("Failed to initialize router")
}
go func() {
if err := s.Start(); err != nil {
if errors.Is(err, http.ErrServerClosed) {
log.Info().Msg("Server closed")
} else {
log.Fatal().Err(err).Msg("Failed to start server")
}
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
<-quit
return nil
})
if err != nil {
log.Fatal().Err(err).Msg("Failed to start server")
}
}