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 }