github.com/adharshmk96/stk@v1.2.3/pkg/project/tpl/project.go (about)

     1  package tpl
     2  
     3  var DOCKERCOMPOSEYAML_TPL = Template{
     4  	FilePath: "docker-compose.yaml",
     5  	Render: true,
     6  	Content: `services:
     7    {{ .AppName }}:
     8      build:
     9        context: .
    10        dockerfile: Dockerfile
    11      ports:
    12        - "8080:8080"
    13      networks:
    14        - {{ .AppName }}-network
    15  
    16  networks:
    17    {{ .AppName }}-network:
    18      driver: bridge
    19  `,
    20  }
    21  
    22  var GORELEASERYAML_TPL = Template{
    23  	FilePath: ".goreleaser.yaml",
    24  	Render: true,
    25  	Content: `project_name: {{ .AppName }}
    26  
    27  before:
    28    hooks:
    29      # You may remove this if you don't use go modules.
    30      - go mod tidy
    31  
    32  builds:
    33    - main: ./main.go
    34      binary: {{ .AppName }}
    35      ldflags:
    36        - -s -w -X "{{ .PkgName }}/cmd.SemVer={{"{{ .Tag }}"}}"
    37      env:
    38        - CGO_ENABLED=0
    39      goos:
    40        - linux
    41        - windows
    42        - darwin
    43      goarch:
    44        - amd64
    45        - arm64
    46  
    47  archives:
    48    - format: tar.gz
    49      format_overrides:
    50        - goos: windows
    51          format: zip
    52      
    53      # this name template makes the OS and Arch compatible with the results of uname.
    54      name_template: "{{"{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"}}"
    55  
    56  changelog:
    57    sort: asc
    58    use: github
    59    filters:
    60      exclude:
    61        - '^docs:'
    62        - '^test:'
    63      include:
    64        - "^feat:"
    65        - "^fix:"
    66        - "^refactor:"
    67        - "^chore:"
    68        - "^perf:"
    69  
    70  
    71  `,
    72  }
    73  
    74  var DOCKERFILE_TPL = Template{
    75  	FilePath: "Dockerfile",
    76  	Render: true,
    77  	Content: `# Start from the official Golang image to build our application.
    78  FROM golang:1.21 AS build
    79  
    80  # Set the working directory inside the container.
    81  WORKDIR /app
    82  
    83  # Copy go.mod and go.sum to download all dependencies.
    84  COPY go.mod go.sum ./
    85  RUN go mod download
    86  
    87  # Copy the rest of the source code.
    88  COPY . .
    89  
    90  # Build the application. 
    91  # This produces a statically linked executable by disabling cgo which 
    92  # is not needed in a scratch container.
    93  RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o {{ .AppName }} .
    94  
    95  # Now, start from a new stage with a minimal base image for a smaller final image.
    96  FROM alpine:latest
    97  
    98  # Copy the statically linked executable from the build stage to the current stage.
    99  COPY --from=build /app/{{ .AppName }} .
   100  
   101  # Expose the port the application listens on.
   102  EXPOSE 8080
   103  
   104  # Command to run the executable.
   105  CMD ["./{{ .AppName }}"]
   106  `,
   107  }
   108  
   109  var MAKEFILE_TPL = Template{
   110  	FilePath: "makefile",
   111  	Render: true,
   112  	Content: `publish:
   113  	@git push && semver push
   114  
   115  
   116  ##########################
   117  ### Build Commands
   118  ##########################
   119  
   120  BINARY_NAME={{ .AppName }}
   121  
   122  build:
   123  	@go build -o ./out/$(BINARY_NAME) -v
   124  
   125  run: 
   126  	@go run . serve -p 8080
   127  
   128  test:
   129  	@go test ./...
   130  
   131  coverage:
   132  	@go test -v ./... -coverprofile=coverage.out 
   133  	@go tool cover -html=coverage.out
   134  
   135  testci:
   136  	@go test ./... -coverprofile=coverage.out
   137  
   138  clean:
   139  	@go clean
   140  	@rm -f ./out/$(BINARY_NAME)
   141  	@rm -f coverage.out
   142  
   143  deps:
   144  	@go mod download
   145  
   146  tidy:
   147  	@go mod tidy
   148  
   149  lint:
   150  	@golangci-lint run --enable-all
   151  
   152  vet:
   153  	@go vet
   154  
   155  clean-branch:
   156  	@git branch | egrep -v "(^\*|main|master)" | xargs git branch -D
   157  	@git tag -d $(shell git tag -l)
   158  
   159  	
   160  ##########################
   161  ### Setup Commands
   162  ##########################
   163  
   164  init: 
   165  	@go mod tidy
   166  # Install tools
   167  	@go install github.com/adharshmk96/semver@latest
   168  	@go install github.com/vektra/mockery/v2@latest
   169  # Setup Git hooks
   170  	@git config core.hooksPath .githooks
   171  
   172  # mockgen:
   173  	@rm -rf ./mocks
   174  	@mockery --all	
   175  
   176  	@echo "Project initialized."
   177  `,
   178  }
   179  
   180  var READMEMD_TPL = Template{
   181  	FilePath: "README.md",
   182  	Render: true,
   183  	Content: `# {{ .AppName }}
   184  
   185  Run
   186  
   187  go run main.go serve -p 8080
   188  
   189  - or -
   190  
   191  make run
   192  
   193  
   194  ## Project Structure Documentation
   195  
   196  This is the project structure generated by "stk init" command.
   197  
   198  read more about project structure [here](https://stk-docs.netlify.app/getting-started/project-structure)`,
   199  }
   200  
   201  var GITIGNORE_TPL = Template{
   202  	FilePath: ".gitignore",
   203  	Render: true,
   204  	Content: `# If you prefer the allow list template instead of the deny list, see community template:
   205  # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
   206  #
   207  # Binaries for programs and plugins
   208  *.exe
   209  *.exe~
   210  *.dll
   211  *.so
   212  *.dylib
   213  
   214  # Test binary
   215  *.test
   216  
   217  # Output of the go coverage tool, specifically when used with LiteIDE
   218  *.out
   219  dist
   220  
   221  *.db
   222  .stk.yaml`,
   223  }
   224  
   225  var MAINGO_TPL = Template{
   226  	FilePath: "main.go",
   227  	Render: true,
   228  	Content: `package main
   229  
   230  import "{{ .PkgName }}/cmd"
   231  
   232  func main() {
   233  	cmd.Execute()
   234  }
   235  `,
   236  }
   237  
   238  var CMD_SERVEGO_TPL = Template{
   239  	FilePath: "cmd/serve.go",
   240  	Render: true,
   241  	Content: `package cmd
   242  
   243  import (
   244  	"sync"
   245  
   246  	"{{ .PkgName }}/server"
   247  	"github.com/spf13/cobra"
   248  )
   249  
   250  var startingPort string
   251  
   252  // serveCmd represents the serve command
   253  var serveCmd = &cobra.Command{
   254  	Use:   "serve",
   255  	Short: "Start the server",
   256  	Run: func(cmd *cobra.Command, args []string) {
   257  		var wg sync.WaitGroup
   258  
   259  		wg.Add(1)
   260  
   261  		startAddr := "0.0.0.0:"
   262  
   263  		go func() {
   264  			defer wg.Done()
   265  			_, done := server.StartHttpServer(startAddr + startingPort)
   266  			// blocks the routine until done is closed
   267  			<-done
   268  		}()
   269  
   270  		wg.Wait()
   271  	},
   272  }
   273  
   274  func init() {
   275  	serveCmd.Flags().StringVarP(&startingPort, "port", "p", "8080", "Port to start the server on")
   276  
   277  	rootCmd.AddCommand(serveCmd)
   278  }
   279  `,
   280  }
   281  
   282  var CMD_ROOTGO_TPL = Template{
   283  	FilePath: "cmd/root.go",
   284  	Render: true,
   285  	Content: `package cmd
   286  
   287  import (
   288  	"fmt"
   289  	"os"
   290  	"runtime/debug"
   291  	"strings"
   292  
   293  	"github.com/spf13/cobra"
   294  	"github.com/spf13/viper"
   295  )
   296  
   297  var cfgFile string
   298  var SemVer = "development"
   299  
   300  func displaySemverInfo() {
   301  	if SemVer != "development" {
   302  		fmt.Printf("v%s", SemVer)
   303  		return
   304  	}
   305  	version, ok := debug.ReadBuildInfo()
   306  	if ok && version.Main.Version != "(devel)" && version.Main.Version != "" {
   307  		SemVer = version.Main.Version
   308  	}
   309  	fmt.Printf("v%s", SemVer)
   310  }
   311  
   312  // rootCmd represents the base command when called without any subcommands
   313  var rootCmd = &cobra.Command{
   314  	Use:   "{{ .AppName }}",
   315  	Short: "{{ .AppName }} is an stk project.",
   316  	Long:  "{{ .AppName }} is generated using stk cli.",
   317  	Run: func(cmd *cobra.Command, args []string) {
   318  		if cmd.Flag("version").Value.String() == "true" {
   319  			displaySemverInfo()
   320  		} else {
   321  			cmd.Help()
   322  		}
   323  	},
   324  }
   325  
   326  func Execute() {
   327  	err := rootCmd.Execute()
   328  	if err != nil {
   329  		os.Exit(1)
   330  	}
   331  }
   332  
   333  func init() {
   334  	cobra.OnInitialize(initConfig)
   335  	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", ".stk.yaml", "config file.")
   336  	rootCmd.Flags().BoolP("version", "v", false, "display {{ .AppName }} version")
   337  }
   338  
   339  // initConfig reads in config file and ENV variables if set.
   340  func initConfig() {
   341  	if cfgFile != "" {
   342  		// Use config file from the flag.
   343  		viper.SetConfigFile(cfgFile)
   344  	}
   345  
   346  	viper.AutomaticEnv()
   347  
   348  	// Set the key replacer for env variables.
   349  	replacer := strings.NewReplacer(".", "_")
   350  	viper.SetEnvKeyReplacer(replacer)
   351  
   352  	// If a config file is found, read it in.
   353  	if err := viper.ReadInConfig(); err == nil {
   354  		fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
   355  	}
   356  
   357  }
   358  `,
   359  }
   360  
   361  var GITHOOKS_PREPUSH_TPL = Template{
   362  	FilePath: ".githooks/pre-push",
   363  	Render: true,
   364  	Content: `#!/bin/sh
   365  make testci`,
   366  }
   367  
   368  var SERVER_SETUPGO_TPL = Template{
   369  	FilePath: "server/setup.go",
   370  	Render: true,
   371  	Content: `package server
   372  
   373  import (
   374  	"os"
   375  	"os/signal"
   376  	"syscall"
   377  
   378  	"github.com/adharshmk96/stk/gsk"
   379  	"github.com/adharshmk96/stk/pkg/middleware"
   380  	"{{ .PkgName }}/server/infra"
   381  	svrmw "{{ .PkgName }}/server/middleware"
   382  	"{{ .PkgName }}/server/routing"
   383  )
   384  
   385  func StartHttpServer(port string) (*gsk.Server, chan bool) {
   386  
   387  	logger := infra.GetLogger()
   388  
   389  	serverConfig := &gsk.ServerConfig{
   390  		Port:   port,
   391  		Logger: logger,
   392  	}
   393  
   394  	server := gsk.New(serverConfig)
   395  
   396  	rateLimiter := svrmw.RateLimiter()
   397  	server.Use(rateLimiter)
   398  	server.Use(middleware.RequestLogger)
   399  	server.Use(middleware.CORS(middleware.CORSConfig{
   400  		AllowAll: true,
   401  	}))
   402  
   403  	infra.LoadDefaultConfig()
   404  
   405  	routing.SetupTemplateRoutes(server)
   406  	routing.SetupApiRoutes(server)
   407  
   408  	server.Start()
   409  
   410  	// graceful shutdown
   411  	done := make(chan bool)
   412  
   413  	// A go routine that listens for os signals
   414  	// it will block until it receives a signal
   415  	// once it receives a signal, it will shut down close the done channel
   416  	go func() {
   417  		sigint := make(chan os.Signal, 1)
   418  		signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
   419  		<-sigint
   420  
   421  		if err := server.Shutdown(); err != nil {
   422  			logger.Error(err.Error())
   423  		}
   424  
   425  		close(done)
   426  	}()
   427  
   428  	return server, done
   429  }
   430  `,
   431  }
   432  
   433  var SERVER_MIDDLEWARE_RATELIMITERGO_TPL = Template{
   434  	FilePath: "server/middleware/rateLimiter.go",
   435  	Render: true,
   436  	Content: `package middleware
   437  
   438  import (
   439  	"time"
   440  
   441  	"github.com/adharshmk96/stk/gsk"
   442  	gskmw "github.com/adharshmk96/stk/pkg/middleware"
   443  )
   444  
   445  func RateLimiter() gsk.Middleware {
   446  	rlConfig := gskmw.RateLimiterConfig{
   447  		RequestsPerInterval: 10,
   448  		Interval:            60 * time.Second,
   449  	}
   450  	rateLimiter := gskmw.NewRateLimiter(rlConfig)
   451  	return rateLimiter.Middleware
   452  }
   453  `,
   454  }
   455  
   456  var SERVER_INFRA_CONFIGGO_TPL = Template{
   457  	FilePath: "server/infra/config.go",
   458  	Render: true,
   459  	Content: `package infra
   460  
   461  import "github.com/spf13/viper"
   462  
   463  // Configurations are loaded from the environment variables using viper.
   464  // callin this function will reLoad the config. (useful for testing)
   465  // WARN: this will reload all the config.
   466  func LoadDefaultConfig() {
   467  	viper.SetDefault(ENV_SQLITE_FILEPATH, "database.db")
   468  
   469  	viper.AutomaticEnv()
   470  }
   471  `,
   472  }
   473  
   474  var SERVER_INFRA_LOGGERGO_TPL = Template{
   475  	FilePath: "server/infra/logger.go",
   476  	Render: true,
   477  	Content: `package infra
   478  
   479  import (
   480  	"log/slog"
   481  	"os"
   482  )
   483  
   484  var logger *slog.Logger
   485  
   486  func init() {
   487  	logger = slog.New(slog.NewJSONHandler(os.Stdout, nil))
   488  }
   489  
   490  func GetLogger() *slog.Logger {
   491  	return logger
   492  }
   493  `,
   494  }
   495  
   496  var SERVER_INFRA_CONSTANTSGO_TPL = Template{
   497  	FilePath: "server/infra/constants.go",
   498  	Render: true,
   499  	Content: `package infra
   500  
   501  // Environment Variable Names
   502  const (
   503  	ENV_SQLITE_FILEPATH = "SQLITE_FILEPATH"
   504  )
   505  `,
   506  }
   507  
   508  var SERVER_INFRA_DB_SQLITEGO_TPL = Template{
   509  	FilePath: "server/infra/db/sqlite.go",
   510  	Render: true,
   511  	Content: `package db
   512  
   513  import (
   514  	"database/sql"
   515  	"sync"
   516  
   517  	"{{ .PkgName }}/server/infra"
   518  	_ "github.com/mattn/go-sqlite3"
   519  	"github.com/spf13/viper"
   520  )
   521  
   522  var (
   523  	sqliteInstance *sql.DB
   524  	sqliteOnce     sync.Once
   525  )
   526  
   527  // GetSqliteConnection returns a singleton database connection
   528  func GetSqliteConnection() *sql.DB {
   529  	filepath := viper.GetString(infra.ENV_SQLITE_FILEPATH)
   530  	sqliteOnce.Do(func() {
   531  		db, err := sql.Open("sqlite3", filepath)
   532  		if err != nil {
   533  			panic(err)
   534  		}
   535  		sqliteInstance = db
   536  	})
   537  	return sqliteInstance
   538  }
   539  
   540  // ResetSqliteConnection resets the singleton database connection
   541  func ResetSqliteConnection() {
   542  	sqliteInstance = nil
   543  	sqliteOnce = sync.Once{}
   544  }
   545  `,
   546  }
   547  
   548  var SERVER_ROUTING_SETUPROUTESGO_TPL = Template{
   549  	FilePath: "server/routing/setupRoutes.go",
   550  	Render: true,
   551  	Content: `package routing
   552  
   553  import (
   554  	"github.com/adharshmk96/stk/gsk"
   555  )
   556  
   557  var webRouteGroups = []func(*gsk.RouteGroup){}
   558  var apiRouteGroups = []func(*gsk.RouteGroup){}
   559  
   560  func RegisterApiRoutes(routeGroup func(*gsk.RouteGroup)) {
   561  	apiRouteGroups = append(apiRouteGroups, routeGroup)
   562  }
   563  
   564  func RegisterWebRoutes(routeGroup func(*gsk.RouteGroup)) {
   565  	webRouteGroups = append(webRouteGroups, routeGroup)
   566  }
   567  
   568  func SetupApiRoutes(server *gsk.Server) {
   569  	apiRoutes := server.RouteGroup("/api")
   570  
   571  	for _, routeGroup := range apiRouteGroups {
   572  		routeGroup(apiRoutes)
   573  	}
   574  }
   575  
   576  func SetupTemplateRoutes(server *gsk.Server) {
   577  	templateRoutes := server.RouteGroup("/")
   578  
   579  	for _, routeGroup := range webRouteGroups {
   580  		routeGroup(templateRoutes)
   581  	}
   582  
   583  }
   584  `,
   585  }
   586  
   587  var PUBLIC_TEMPLATES_INDEXHTML_TPL = Template{
   588  	FilePath: "public/templates/index.html",
   589  	Render: false,
   590  	Content: `<!DOCTYPE html>
   591  <html lang="en">
   592      <head>
   593      <meta charset="UTF-8">
   594      <title>{{ .Var.Title }}</title>
   595      <link rel="stylesheet" href="{{ .Config.Static }}/style.css">
   596  </head>
   597  <body>
   598      <h1>{{ .Var.Title }}</h1>
   599      <p>{{ .Var.Content }}</p>
   600  
   601      <script src="{{ .Config.Static }}/script.js"></script>
   602  </body>
   603  </html>
   604  `,
   605  }
   606  
   607  var PUBLIC_ASSETS_STYLESCSS_TPL = Template{
   608  	FilePath: "public/assets/styles.css",
   609  	Render: true,
   610  	Content: ``,
   611  }
   612  
   613  var PUBLIC_ASSETS_SCRIPTJS_TPL = Template{
   614  	FilePath: "public/assets/script.js",
   615  	Render: true,
   616  	Content: ``,
   617  }
   618  
   619  var GITHUB_WORKFLOWS_GOBUILDTESTYML_TPL = Template{
   620  	FilePath: ".github/workflows/go-build-test.yml",
   621  	Render: false,
   622  	Content: `# This workflow will build a golang project
   623  # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
   624  
   625  name: Go Build and Test
   626  
   627  on:
   628    push:
   629      branches: [ "main" ]
   630    pull_request:
   631      branches: [ "main" ]
   632  
   633  jobs:
   634  
   635    build:
   636      runs-on: ubuntu-latest
   637      steps:
   638      - uses: actions/checkout@v3
   639  
   640      - name: Build
   641        run: "make build"
   642  
   643      - name: Test
   644        run: "make testci"
   645  `,
   646  }
   647  
   648  var GITHUB_WORKFLOWS_GORELEASEYML_TPL = Template{
   649  	FilePath: ".github/workflows/go-release.yml",
   650  	Render: false,
   651  	Content: `name: Go Release Workflow
   652  
   653  on:
   654    push:
   655      # Sequence of patterns matched against refs/tags
   656      tags:
   657        - "v*.*.*" # Push events to matching v*, i.e. v1.0, v20.15.10
   658  
   659  jobs:
   660    release:
   661      runs-on: ubuntu-latest
   662      permissions:
   663        contents: write
   664      steps:
   665        - uses: actions/checkout@v3
   666          with:
   667            fetch-depth: 0
   668        - run: git fetch --force --tags # Ensures go releaser picks us previous tags.
   669        - uses: actions/setup-go@v4
   670          with:
   671            go-version: '1.21' # The Go version to download (if necessary) and use.
   672       
   673        - name: Run GoReleaser
   674          uses: goreleaser/goreleaser-action@v5
   675          with:
   676            distribution: goreleaser
   677            version: latest
   678            args: release --clean
   679          env:
   680            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}`,
   681  }
   682  
   683  var VSCODE_LAUNCHJSON_TPL = Template{
   684  	FilePath: ".vscode/launch.json",
   685  	Render: true,
   686  	Content: `{
   687      // Use IntelliSense to learn about possible attributes.
   688      // Hover to view descriptions of existing attributes.
   689      // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
   690      "version": "0.2.0",
   691      "configurations": [
   692          {
   693              "name": "serve in port 8080",
   694              "type": "go",
   695              "request": "launch",
   696              "mode": "auto",
   697              "program": "${workspaceFolder}/main.go",
   698              "args": ["serve", "-p", "8080"]
   699            }
   700      ]
   701  }`,
   702  }
   703  
   704  var ProjectTemplates = []Template{
   705  	DOCKERCOMPOSEYAML_TPL,
   706  	GORELEASERYAML_TPL,
   707  	DOCKERFILE_TPL,
   708  	MAKEFILE_TPL,
   709  	READMEMD_TPL,
   710  	GITIGNORE_TPL,
   711  	MAINGO_TPL,
   712  	CMD_SERVEGO_TPL,
   713  	CMD_ROOTGO_TPL,
   714  	GITHOOKS_PREPUSH_TPL,
   715  	SERVER_SETUPGO_TPL,
   716  	SERVER_MIDDLEWARE_RATELIMITERGO_TPL,
   717  	SERVER_INFRA_CONFIGGO_TPL,
   718  	SERVER_INFRA_LOGGERGO_TPL,
   719  	SERVER_INFRA_CONSTANTSGO_TPL,
   720  	SERVER_INFRA_DB_SQLITEGO_TPL,
   721  	SERVER_ROUTING_SETUPROUTESGO_TPL,
   722  	PUBLIC_TEMPLATES_INDEXHTML_TPL,
   723  	PUBLIC_ASSETS_STYLESCSS_TPL,
   724  	PUBLIC_ASSETS_SCRIPTJS_TPL,
   725  	GITHUB_WORKFLOWS_GOBUILDTESTYML_TPL,
   726  	GITHUB_WORKFLOWS_GORELEASEYML_TPL,
   727  	VSCODE_LAUNCHJSON_TPL,
   728  }