"
# Copy Create Go App CLI binary.
COPY cgapp /cgapp
@@ -11,13 +11,5 @@ COPY cgapp /cgapp
# Install git, npm (with nodejs).
RUN apk add --no-cache git npm
-# Install frontend CLIs (globally and in silent mode).
-RUN npm i -g -s --unsafe-perm \
- create-react-app \
- preact-cli \
- @vue/cli \
- @angular/cli \
- degit
-
# Set entry point.
ENTRYPOINT ["/cgapp"]
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 9dd2fcc..4e222f3 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ security:
gosec -quiet ./...
install: security lint test
- CGO_ENABLED=0 go build -ldflags="-s -w" -o $(GOPATH)/bin/cgapp main.go
+ CGO_ENABLED=0 go build -ldflags="-s -w" -o $(GOPATH)/bin/cgapp ./cmd/cgapp/main.go
build: security test
goreleaser --snapshot --skip-publish --rm-dist
diff --git a/README.md b/README.md
index 8ad6ec0..bfc8cf3 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
Create a new production-ready project with backend (Golang), frontend (JavaScript, TypeScript)
and deploy automation (Ansible, Docker) by running one CLI command.
Focus on writing code and thinking of business-logic! The CLI will take care of the rest.
-
![license](https://img.shields.io/badge/license-apache_2.0-red?style=for-the-badge&logo=none)
+
![license](https://img.shields.io/badge/license-apache_2.0-red?style=for-the-badge&logo=none)
## ⚡️ Quick start
@@ -13,7 +13,7 @@ First of all, [download](https://golang.org/dl/) and install **Go**. Version `1.
Installation is done by using the [`go install`](https://golang.org/cmd/go/#hdr-Compile_and_install_packages_and_dependencies) command and rename installed binary in `$GOPATH/bin`:
```bash
-go install -ldflags="-s -w" github.com/create-go-app/cli && mv $GOPATH/bin/cli $GOPATH/bin/cgapp
+go install github.com/create-go-app/cli/cmd/cgapp@latest
```
Also, macOS and GNU/Linux users available way to install via [Homebrew](https://brew.sh/):
@@ -26,292 +26,118 @@ brew tap create-go-app/cli
brew install create-go-app/cli/cgapp
```
-Let's create a new project via **interactive console UI** (or **CUI** for short) into current folder:
+Let's create a new project via **interactive console UI** (or **CUI** for short) in current folder:
```bash
cgapp create
```
-Okay, it works! Now, you can run this project on your **local machine** or deploy to a **remote server**. Project works in isolated Docker containers and automates via Ansible playbook:
+Next, open the generated Ansible inventory file (called `hosts.ini`) and fill in the variables according to your server configuration. And you're ready to **automatically deploy** this project:
```bash
cgapp deploy
```
-That's all you need to start! 🎉
+That's all you need to know to start! 🎉
-### ~ Docker-way to quick start
+### 🐳 Docker-way to quick start
If you don't want to install Create Go App CLI to your system, you feel free to using our official [Docker image](https://hub.docker.com/r/koddr/cgapp) and run CLI from isolated container:
```bash
-docker run --rm -it -v ${PWD}:${PWD} -w ${PWD} koddr/cgapp:latest
+docker run --rm -it -v ${PWD}:${PWD} -w ${PWD} koddr/cgapp:latest [COMMAND]
```
-With this Docker image, you do **not** have to worry about installing tools/CLI of frontend UI libraries/frameworks. Everything is **already included**: `create-react-app`, `preact-cli`, `vue-cli`, `ng-cli` and `degit` (for Svelte and Sapper).
+> 🔔 Please note: the `deploy` command is currently **unavailable** in this image.
-Available commands for [official Docker image](https://create-go.app/detailed-guides/official-docker-image/):
+## 📖 Project Wiki
-- [x] [`init`](https://create-go.app/detailed-guides/commands-and-options/#init)
-- [x] [`create`](https://create-go.app/detailed-guides/commands-and-options/#create)
-
-> 🔔 Please note: a [`deploy`](https://create-go.app/detailed-guides/commands-and-options/#deploy) command is currently unavailable in this image.
-
-## 📺 Video screencast
-
-A short video screencast to introduce main features of the Create Go App CLI.
-
-
- ![youtube preview](https://create-go.app/assets/images/youtube-preview.jpg)
- 🔗 https://youtu.be/5-DNZFU9TOQ
-
-
-## 📖 Official Documentation
-
-Unfortunately, we are unable to include all helpful documentation to the `README` file. That's why, the best way to better explore all the features of the **Create Go App CLI** is to read the [Official Documentation](https://create-go.app/) and explore [Discussions](https://github.com/create-go-app/cli/discussions).
-
-> 🔥 We've put together a subject index specifically for you, so you can find any answer you want in seconds!
-
-- [Detailed guides](https://create-go.app/detailed-guides/)
- - [CLI Installation](https://create-go.app/detailed-guides/installation/)
- - [Alternative installations](https://create-go.app/detailed-guides/installation/#alternative-installations)
- - [Understanding CLI commands and options](https://create-go.app/detailed-guides/commands-and-options/)
- - [`init`](https://create-go.app/detailed-guides/commands-and-options/#init)
- - [`create`](https://create-go.app/detailed-guides/commands-and-options/#create)
- - [`deploy`](https://create-go.app/detailed-guides/commands-and-options/#deploy)
- - [Working with the official Docker image](https://create-go.app/detailed-guides/official-docker-image/)
- - [Run project on your local machine](https://create-go.app/detailed-guides/run-on-local/)
- - [Deploy project to a production server](https://create-go.app/detailed-guides/deploy-to-server/)
- - [Make your own template](https://create-go.app/detailed-guides/make-custom-template/)
- - [Make your own container](https://create-go.app/detailed-guides/make-custom-container/)
-- [FAQ](https://create-go.app/faq/)
- - [Automation of a deploy process](https://create-go.app/automation/)
- - [How do I ask the right question?](https://create-go.app/ask-question/#how-do-i-ask-the-right-question)
-- [Official logo](https://create-go.app/logo/)
+The best way to better explore all the features of the **Create Go App CLI** is to read the project [Wiki](https://github.com/create-go-app/cli/wiki) and take part in [Discussions](https://github.com/create-go-app/cli/discussions) and/or [Issues](https://github.com/create-go-app/cli/issues). Yes, the most frequently asked questions (_FAQ_) are also [here](https://github.com/create-go-app/cli/wiki/FAQ).
## ⚙️ Commands & Options
-### `init`
-
-CLI command for generate a default `.cgapp.yml` config file in current folder:
-
-```bash
-cgapp init
-```
-
-- 📺 Preview: https://recordit.co/yvlnIu8Lyp
-- 📖 Docs: https://create-go.app/detailed-guides/commands-and-options/#init
-
-
-Generated config file
-
-
-
-```yaml
-# Project config.
-project:
- # Backend for your project.
- # (Required)
- # String:
- # - `net/http`
- # - `fiber`
- # User template: supported, set to URL (without protocol),
- # like `github.com/user/template`
- - backend: fiber
-
- # Frontend for your project.
- # (Optional, to skip set to `none`)
- # String:
- # - `react`
- # - `react:`
- # - `preact`
- # - `preact:`
- # - `vue`
- # - `vue:` (for preset from GitHub)
- # - `vue::` (for presets from others)
- # - `angular`
- # - `svelte`
- # - `sapper`
- # - `sapper:`
- # User template: supported, set to URL (without protocol),
- # like `github.com/user/template`
- - frontend: svelte
-
- # Web/Proxy server for your project.
- # (Optional, to skip set to `none`)
- # String: `nginx`
- # User template: supported, set to URL (without protocol),
- # like `github.com/user/template`
- - webserver: nginx
-
-# Automation config.
-roles:
- # Ansible roles for deploy your project.
- # (Optional, to skip set to empty or comment)
- # Objects list.
- - deploy:
- # Username of remote's server or local's user.
- # (Required)
- username: root
-
- # If you need to deploy (or run) a project asking for a password
- # for the user, set `become` to `true`. This is equivalent of
- # `--ask-become-pass`, a standard Ansible argument
- # to ask for a privilege escalation password.
- # (Optional)
- become: true
-
- # Host name from your inventory file (usually, at /etc/ansible/hosts).
- # (Required)
- host: localhost
-
- # Name of Docker network
- # (Required)
- network: cgapp_network
-
- # Port for backend Docker container (both in and out).
- # (Required)
- backend_port: 5000
-
- # Filename of Ansible playbook in the root of the Create Go App project.
- # If you want to rename it, do it, but not to change destination of file!
- # (Required)
- playbook: deploy-playbook.yml
-```
-
-
-
### `create`
-CLI command to create a new project with the selected configuration.
-
-There's two ways to create a new project:
-
-- [x] With an interactive console UI (or CUI).
-- [x] From configuration file (by default, in `$PWD/.cgapp.yml`).
-
-#### Create with the interactive console UI
-
-Run `create` command **without** any arguments:
+CLI command for create a new project with the interactive console UI.
```bash
cgapp create
```
-- 📺 Preview: https://recordit.co/LTxFQloedn
-- 📖 Docs: https://create-go.app/detailed-guides/commands-and-options/#create
-
-#### Create from the config file
+![cgapp_create](https://user-images.githubusercontent.com/11155743/116796937-38160080-aae9-11eb-8e21-fb1be2750aa4.gif)
-Run `create` command **with** `--use-config` (or `-c`) argument:
-
-```bash
-cgapp create --use-config
-```
+- 📺 Full demo video: https://recordit.co/OQAwkZBrjN
+- 📖 Docs: https://github.com/create-go-app/cli/wiki/Command-create
### `deploy`
-CLI command for deploy Docker containers with your project to a remote server.
-
-> ☝️ You should only run this command from the **root folder** of your project, which created with the `cgapp create` command! It's a necessary condition for everything to work perfectly.
-
-There's, also, two ways to deploy your project:
-
-- [x] With an interactive console UI (or CUI).
-- [x] From configuration file (by default, in `$PWD/.cgapp.yml`).
-
-#### Deploy with the interactive console UI
-
-Run `deploy` command **without** any arguments:
-
-```bash
-cgapp deploy
-```
-
-- 📺 Preview: https://recordit.co/ewjG9dgMPX
-- 📖 Docs: https://create-go.app/detailed-guides/commands-and-options/#deploy
-
-#### Deploy from the config file
+CLI command for deploy Docker containers with your project via Ansible to the remote server.
-Run `deploy` command **with** `--use-config` (or `-c`) argument:
+> 🔔 Make sure that you have [Python 3.8+](https://www.python.org/downloads/) and [Ansible 2.9+](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-specific-operating-systems) installed on your computer.
```bash
-cgapp deploy --use-config
+cgapp deploy [OPTION]
```
-## 🤔 Why another CLI?
+| Option | Description | Type | Default | Required? |
+| ------ | ------------------------------------------------------------------------------------------------------ | --------- | ------- | --------- |
+| `-K` | Prompt you to provide the remote user sudo password (_a standard Ansible `--ask-become-pass` option_). | `boolean` | `false` | No |
-When we started this project, we asked ourselves this question too and... came to the conclusion, that approximately 8 out of 10 routine operations at the start of a new project and/or the deployment of an existing one **can be automated**. And it would be better to have all the necessary functions inside one CLI. That's why we transferred all our experience to the Create Go App CLI, which we use ourselves!
+![cgapp_deploy](https://user-images.githubusercontent.com/11155743/116796941-3c421e00-aae9-11eb-9575-d72550814d7a.gif)
-So, yes, this CLI gives you the ability to prepare everything you need to **start a new project** (as `create-react-app` for the React.js ecosystem does) and **deploy an existing project** to a remote server in configured and fully isolated Docker containers.
+- 📺 Full demo video: https://recordit.co/ishTf0Au1x
+- 📖 Docs: https://github.com/create-go-app/cli/wiki/Command-deploy
## 📝 Production-ready project templates
-**Backend:**
+### Backend
-- [x] [`net/http`](https://create-go.app/production-templates/net-http-go/) — Backend template with Golang built-in [net/http](https://golang.org/pkg/net/http/) package.
-- [x] [`fiber`](https://create-go.app/production-templates/fiber-go/) — Backend template with [Fiber](https://github.com/gofiber/fiber).
-- [ ] [`echo`](https://create-go.app/production-templates/echo-go/) (_WIP_) — Backend template with [Echo](https://github.com/labstack/echo).
-- [ ] [`gin`](https://create-go.app/production-templates/gin-go/) (_WIP_) — Backend template with [Gin](https://github.com/gin-gonic/gin).
+- Backend template with Golang built-in [net/http](https://golang.org/pkg/net/http/) package:
+ - [`net/http`](https://github.com/create-go-app/net_http-go-template) — simple REST API with CRUD and JWT auth.
+- Backend template with [Fiber](https://github.com/gofiber/fiber):
+ - [`fiber`](https://github.com/create-go-app/fiber-go-template) — complex REST API with CRUD, JWT auth with renew token, DB and cache.
-**Frontend:**
+### Frontend
-- [x] `react` — [React](https://reactjs.org/) frontend app.
- - `react:` — CRA generated template for React app.
-- [x] `preact` — [Preact](https://preactjs.com/) frontend app.
- - `preact:` — Preact CLI generated template for Preact app.
-- [x] `vue` — [Vue.js](https://vuejs.org/) frontend app.
- - `vue:` — Preset for generating Vue.js app from GitHub.
- - `vue::` — Preset for generating Vue.js app from GitLab/BitBucket/etc.
-- [x] `angular` — [Angular](https://angular.io/) frontend app.
-- [x] `svelte` — [Svelte](https://svelte.dev/) frontend app.
-- [x] `sapper` — [Sapper](https://sapper.svelte.dev/) frontend app for static websites.
- - `sapper:` — Preset for generating Sapper with Webpack bundler.
+- Pure JavaScript frontend template:
+ - `vanilla` — generated template with pure JavaScript app.
+ - `vanilla-ts` — generated template with pure TypeScript app.
+- Frontend template with [React](https://reactjs.org/):
+ - `react` — generated template with a common React app.
+ - `react-ts` — generated template with a TypeScript version of the React app.
+- Frontend template with [Preact](https://preactjs.com/):
+ - `preact` — generated template with a common Preact app.
+ - `preact-ts` — generated template with a TypeScript version of the Preact app.
+- Frontend template with [Vue.js](https://vuejs.org/):
+ - `vue` — generated template with a common Vue.js app.
+ - `vue-ts` — generated template with a TypeScript version of the Vue.js app.
+- Frontend template with [Svelte](https://svelte.dev/):
+ - `svelte` — generated template with a common Svelte app.
+ - `svelte-ts` — generated template with a TypeScript version of the Svelte app.
+- Frontend template with [Lit](https://lit.dev/) web components:
+ - `lit-element` — generated template with a common Lit app.
+ - `lit-element-ts` — generated template a TypeScript version of the Lit app.
-> ☝️ Please note, that since version `v1.3.0`, frontend templates (_in the classical sense_) are **not** supported by the Create Go App CLI. Those templates, that we created ([`react-js`](https://github.com/create-go-app/react-js-template), [`react-ts`](https://github.com/create-go-app/react-ts-template) and [`preact-js`](https://github.com/create-go-app/preact-js-template)), are still available, but only for use as **user's custom templates**.
->
-> Now, the frontend part of your project will be generated **using official CLI** from the authors of each frontend UI library/framework (_under the hood_). So, you'll always get the latest version of `React`, `Preact`, `Vue.js`, `Angular`, `Svelte` or `Sapper` for your project from their authors!
+> ☝️ Frontend part will be generate using awesome tool [Vite.js](https://vitejs.dev/) under the hood. So, you'll always get the latest version of `React`, `Preact`, `Vue`, `Svelte`, `Lit` or pure JavaScript/TypeScript templates for your project!
-## 🐳 Configured Docker containers
+## 🚚 Pre-configured Ansible roles
-**Web/Proxy server:**
-
-- [x] [`nginx`](https://create-go.app/docker-containers/nginx/) — Docker container with [Nginx](https://nginx.org).
-
-## 👤 Custom templates & containers?
-
-Create Go App CLI provide works with **your own** custom templates, instead of those prepared by authors. Just specify backend, frontend and webserver with addresses to repositories in configuration file (`.cgapp.yml`):
-
-```yaml
-project:
- - backend: github.com/user1/my-template-1
- - frontend: gitlab.com/user2/my-template-2
- - webserver: bitbucket.org/user3/my-template-3
-# ...
-```
-
-> ☝️ The `https://` protocol will be added automatically!
-
-## 👵 How to install older version?
-
-You can do it by using a version suffix in `go install` command:
-
-```bash
-go install github.com/create-go-app/cli@1.6.0
-```
+### Web/Proxy server
-> ☝️ Don't forget to rename binary after installation, according to the version you have installed! This must be done to avoid confusion with the latest version.
->
-> For example: `mv $GOPATH/bin/cli $GOPATH/bin/cgapp_v1_6_0` and run it by `cgapp_v1_6_0`.
+- Roles for run Docker container with [Traefik Proxy](https://traefik.io/traefik/):
+ - `traefik` — configured Traefik container with a simple ACME challenge via CA server.
+ - `traefik-acme-dns` — configured Traefik container with a complex ACME challenge via DNS provider.
+- Roles for run Docker container with [Nginx](https://nginx.org):
+ - `nginx` — pure Nginx container with "the best practice" configuration.
-Found all available CLI versions on our [pkg.go.dev](https://pkg.go.dev/github.com/create-go-app/cli?tab=versions) page.
+> ✌️ Since Create Go App CLI `v2.0.0`, we're recommend to use **Traefik Proxy** as default proxy server for your projects. The main reason: this proxy provides _automatic_ SSL certificates from Let's Encrypt out of the box. Also, Traefik was built on the Docker ecosystem and has a _really good looking_ and _useful_ Web UI.
## ⭐️ Project assistance
If you want to say **thank you** or/and support active development of `Create Go App CLI`:
- Add a [GitHub Star](https://github.com/create-go-app/cli) to the project.
-- Twit about project [on your Twitter](https://twitter.com/intent/tweet?text=Create%20a%20new%20production-ready%20project%20with%20backend%20%28Golang%29%2C%20frontend%20%28JavaScript%2C%20TypeScript%29%20%26%20deploy%20automation%20%28Ansible%2C%20Docker%29%20by%20running%20one%20CLI%20command%21%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fcreate-go-app%2Fcli).
+- Twit about project [on your Twitter](https://twitter.com/intent/tweet?text=%E2%9C%A8%20Create%20a%20new%20production-ready%20project%20with%20%23Golang%20backend%2C%20%23JavaScript%20or%20%23TypeScript%20frontend%2C%20%23Docker%20and%20%23Ansible%20deploy%20automation%20by%20running%20one%20command.%20%0A%0AFocus%20on%20writing%20code%20and%20thinking%20of%20business-logic%21%0AThe%20CLI%20will%20take%20care%20of%20the%20rest.%0A%0Ahttps%3A%2F%2Fgithub.com%2Fcreate-go-app%2Fcli).
- Write interesting articles about project on [Dev.to](https://dev.to/), [Medium](https://medium.com/) or personal blog.
- Join DigitalOcean at our [referral link](https://m.do.co/c/b41859fa9b6e) (your profit is **$100** and we get $25).
@@ -319,4 +145,4 @@ Together, we can make this project **better** every day! 😘
## ⚠️ License
-`Create Go App CLI` is free and open-source software licensed under the [Apache 2.0 License](https://github.com/create-go-app/cli/blob/master/LICENSE). Official logo was created by [Vic Shóstak](https://shostak.dev/) and distributed under [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) license (CC BY-SA 4.0 International).
+`Create Go App CLI` is free and open-source software licensed under the [Apache 2.0 License](https://github.com/create-go-app/cli/blob/master/LICENSE). Official [logo](https://github.com/create-go-app/cli/wiki/Logo) was created by [Vic Shóstak](https://shostak.dev/) and distributed under [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) license (CC BY-SA 4.0 International).
diff --git a/main.go b/cmd/cgapp/main.go
similarity index 100%
rename from main.go
rename to cmd/cgapp/main.go
diff --git a/cmd/create.go b/cmd/create.go
index e62d2f0..bc01962 100644
--- a/cmd/create.go
+++ b/cmd/create.go
@@ -5,7 +5,7 @@
package cmd
import (
- "os"
+ "fmt"
"strings"
"time"
@@ -19,161 +19,195 @@ import (
var createCmd = &cobra.Command{
Use: "create",
Aliases: []string{"new"},
- Short: "Create a new project via interactive UI or configuration file",
- Long: "\nCreate a new project via interactive UI or configuration file.",
- Run: runCreateCmd,
+ Short: "Create a new project via interactive UI",
+ Long: "\nCreate a new project via interactive UI.",
+ RunE: runCreateCmd,
}
// runCreateCmd represents runner for the `create` command.
-var runCreateCmd = func(cmd *cobra.Command, args []string) {
+func runCreateCmd(cmd *cobra.Command, args []string) error {
// Start message.
- cgapp.SendMsg(true, "* * *", "Create a new project via Create Go App CLI v"+registry.CLIVersion+"...", "yellow", true)
-
- // If config is set and correct, skip survey and use it.
- if useConfigFile && projectConfig != nil {
- // Re-define variables from config file (default is $PWD/.cgapp.yml).
- backend = strings.ToLower(projectConfig["backend"].(string))
- frontend = strings.ToLower(projectConfig["frontend"].(string))
- webserver = strings.ToLower(projectConfig["webserver"].(string))
-
- // Check, if config file contains `roles` section
- if rolesConfig != nil {
- installAnsibleRoles = true
- }
- } else {
- // Start survey.
- if err := survey.Ask(
- registry.CreateQuestions, &createAnswers, survey.WithIcons(surveyIconsConfig),
- ); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
- }
-
- // If something went wrong, cancel and exit.
- if !createAnswers.AgreeCreation {
- cgapp.SendMsg(true, "[!]", "You're stopped creation of a new project.", "red", false)
- cgapp.SendMsg(false, "[!]", "Run `cgapp create` once again!", "red", true)
- os.Exit(1)
- }
-
- // Insert empty line.
- cgapp.SendMsg(false, "", "", "", false)
+ cgapp.ShowMessage(
+ "",
+ fmt.Sprintf("Create a new project via Create Go App CLI v%v...", registry.CLIVersion),
+ true, true,
+ )
- // Define variables for better display.
- backend = strings.ToLower(createAnswers.Backend)
- frontend = strings.ToLower(createAnswers.Frontend)
- webserver = strings.ToLower(createAnswers.Webserver)
- installAnsibleRoles = createAnswers.InstallAnsibleRoles
+ // Start survey.
+ if err := survey.Ask(
+ registry.CreateQuestions, &createAnswers, survey.WithIcons(surveyIconsConfig),
+ ); err != nil {
+ return cgapp.ShowError(err.Error())
}
+ // Define variables for better display.
+ backend = strings.Replace(createAnswers.Backend, "/", "_", -1)
+ frontend = createAnswers.Frontend
+ proxy = createAnswers.Proxy
+
// Start timer.
startTimer := time.Now()
- // Get current directory.
- currentDir, err := os.Getwd()
- if err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
+ /*
+ The project's backend part creation.
+ */
+
+ // Clone backend files from git repository.
+ if err := cgapp.GitClone(
+ "backend",
+ fmt.Sprintf("github.com/create-go-app/%v-go-template", backend),
+ ); err != nil {
+ return cgapp.ShowError(err.Error())
}
- // Create config files for your project.
- cgapp.SendMsg(false, "*", "Create config files for your project...", "cyan", true)
+ // Cleanup project.
+ cgapp.RemoveFolders("backend", []string{".git", ".github"})
- // Create configuration files.
- filesToMake := map[string][]byte{
- ".gitignore": registry.EmbedGitIgnore,
- ".gitattributes": registry.EmbedGitAttributes,
- ".editorconfig": registry.EmbedEditorConfig,
- "Makefile": registry.EmbedMakefile,
- }
- if err := cgapp.MakeFiles(currentDir, filesToMake); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
- }
+ // Show success report.
+ cgapp.ShowMessage(
+ "success",
+ fmt.Sprintf("Backend was created with template `%v`!", backend),
+ true, false,
+ )
- // Create Ansible playbook with tasks, if not skipped.
- if installAnsibleRoles {
- cgapp.SendMsg(true, "*", "Create Ansible playbook with tasks...", "cyan", true)
+ /*
+ The project's frontend part creation.
+ */
- // Create playbook.
- fileToMake := map[string][]byte{
- "deploy-playbook.yml": registry.EmbedDeployPlaybook,
- }
- if err := cgapp.MakeFiles(currentDir, fileToMake); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
+ if frontend != "none" {
+ // Create frontend files.
+ if err := cgapp.ExecCommand(
+ "npm",
+ []string{"init", "@vitejs/app", "frontend", "--", "--template", frontend},
+ true,
+ ); err != nil {
+ return cgapp.ShowError(err.Error())
}
- }
- // Create backend files.
- cgapp.SendMsg(true, "*", "Create project backend...", "cyan", true)
- if err := cgapp.CreateProjectFromRegistry(
- ®istry.Project{
- Type: "backend",
- Name: backend,
- RootFolder: currentDir,
- },
- registry.Repositories,
- registry.RegexpBackendPattern,
- ); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
+ // Show success report.
+ cgapp.ShowMessage(
+ "success",
+ fmt.Sprintf("Frontend was created with template `%v`!", frontend),
+ false, false,
+ )
}
- if frontend != "none" {
- // Create frontend files.
- cgapp.SendMsg(true, "*", "Create project frontend...", "cyan", false)
- if err := cgapp.CreateProjectFromCmd(
- ®istry.Project{
- Type: "frontend",
- Name: frontend,
- RootFolder: currentDir,
+ /*
+ The project's webserver part creation.
+ */
+
+ if proxy != "none" {
+ // Copy Ansible roles from embedded file system.
+ if err := cgapp.CopyFromEmbeddedFS(
+ &cgapp.EmbeddedFileSystem{
+ Name: registry.EmbedRoles,
+ RootFolder: "roles",
+ SkipDir: false,
},
- registry.Commands,
- registry.RegexpFrontendPattern,
); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
+ return cgapp.ShowError(err.Error())
}
- }
- // Docker containers.
- if webserver != "none" {
-
- cgapp.SendMsg(true, "* * *", "Configuring Docker containers...", "yellow", false)
-
- if webserver != "none" {
- // Create container with a web/proxy server.
- cgapp.SendMsg(true, "*", "Create container with web/proxy server...", "cyan", true)
- if err := cgapp.CreateProjectFromRegistry(
- ®istry.Project{
- Type: "webserver",
- Name: webserver,
- RootFolder: currentDir,
- },
- registry.Repositories,
- registry.RegexpWebServerPattern,
- ); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
- }
+ // Copy Ansible playbook, inventory and roles from embedded file system.
+ if err := cgapp.CopyFromEmbeddedFS(
+ &cgapp.EmbeddedFileSystem{
+ Name: registry.EmbedTemplates,
+ RootFolder: "templates",
+ SkipDir: true,
+ },
+ ); err != nil {
+ return cgapp.ShowError(err.Error())
+ }
+
+ // Set template variables for Ansible playbook and inventory files.
+ inventory = registry.AnsibleInventoryVariables[proxy].List
+ playbook = registry.AnsiblePlaybookVariables[proxy].List
+
+ // Generate Ansible inventory file.
+ if err := cgapp.GenerateFileFromTemplate("hosts.ini.tmpl", inventory); err != nil {
+ return cgapp.ShowError(err.Error())
+ }
+
+ // Generate Ansible playbook file.
+ if err := cgapp.GenerateFileFromTemplate("playbook.yml.tmpl", playbook); err != nil {
+ return cgapp.ShowError(err.Error())
+ }
+
+ // Set unused proxy roles.
+ if proxy == "traefik" || proxy == "traefik-acme-dns" {
+ proxyList = []string{"nginx"}
+ } else if proxy == "nginx" {
+ proxyList = []string{"traefik"}
}
+
+ // Delete unused proxy and/or frontend roles.
+ cgapp.RemoveFolders("roles", proxyList)
+
+ // Success messages.
+ cgapp.ShowMessage(
+ "success",
+ fmt.Sprintf("Web/Proxy server configuration for `%v` was created!", proxy),
+ false, false,
+ )
+ cgapp.ShowMessage(
+ "success",
+ "Ansible inventory, playbook and roles for deploying was created!",
+ false, false,
+ )
}
- // Stop timer
- stopTimer := time.Since(startTimer).String()
+ /*
+ The project's misc files part creation.
+ */
- // End message.
- cgapp.SendMsg(true, "* * *", "Completed in "+stopTimer+"!", "yellow", true)
- cgapp.SendMsg(false, "(i)", "A helpful documentation and next steps -> https://create-go.app/", "green", false)
- cgapp.SendMsg(false, "(i)", "Run `cgapp deploy` to deploy your project to a remote server or run on localhost.", "green", true)
+ // Copy from embedded file system.
+ if err := cgapp.CopyFromEmbeddedFS(
+ &cgapp.EmbeddedFileSystem{
+ Name: registry.EmbedMiscFiles,
+ RootFolder: "misc",
+ SkipDir: true,
+ },
+ ); err != nil {
+ return cgapp.ShowError(err.Error())
+ }
+
+ // Stop timer.
+ stopTimer := cgapp.CalculateDurationTime(startTimer)
+ cgapp.ShowMessage(
+ "info",
+ fmt.Sprintf("Completed in %v seconds!", stopTimer),
+ true, true,
+ )
+
+ // Ending messages.
+ if proxy != "none" {
+ cgapp.ShowMessage(
+ "",
+ "* Please put credentials into the Ansible inventory file (`hosts.ini`) before you start deploying a project!",
+ false, false,
+ )
+ }
+ if frontend != "none" {
+ cgapp.ShowMessage(
+ "",
+ fmt.Sprintf("* Visit https://vitejs.dev/guide/ for more info about using the `%v` frontend template!", frontend),
+ false, false,
+ )
+ }
+ cgapp.ShowMessage(
+ "",
+ "* A helpful documentation and next steps with your project is here https://create-go.app/",
+ false, true,
+ )
+ cgapp.ShowMessage(
+ "",
+ "Have a happy new project! :)",
+ false, true,
+ )
+
+ return nil
}
func init() {
rootCmd.AddCommand(createCmd)
- createCmd.PersistentFlags().BoolVarP(
- &useConfigFile,
- "use-config", "c", false,
- "use config file to create a new project or deploy to a remote server (by default, in $PWD/.cgapp.yml)",
- )
}
diff --git a/cmd/deploy.go b/cmd/deploy.go
index 98478bc..4d43d1e 100644
--- a/cmd/deploy.go
+++ b/cmd/deploy.go
@@ -5,11 +5,9 @@
package cmd
import (
- "os"
- "strings"
+ "fmt"
"time"
- "github.com/AlecAivazis/survey/v2"
"github.com/create-go-app/cli/pkg/cgapp"
"github.com/create-go-app/cli/pkg/registry"
"github.com/spf13/cobra"
@@ -18,96 +16,68 @@ import (
// deployCmd represents the `deploy` command.
var deployCmd = &cobra.Command{
Use: "deploy",
- Aliases: []string{"serve"},
- Short: "Deploy your project to a remote server via interactive UI or configuration file",
- Long: "\nDeploy your project to a remote server via interactive UI or configuration file.",
- Run: runDeployCmd,
+ Aliases: []string{"push"},
+ Short: "Deploy your project to the remote server via Ansible",
+ Long: "\nDeploy your project to the remote server by Ansible playbooks and roles.",
+ RunE: runDeployCmd,
}
// runDeployCmd represents runner for the `deploy` command.
-var runDeployCmd = func(cmd *cobra.Command, args []string) {
+func runDeployCmd(cmd *cobra.Command, args []string) error {
// Start message.
- cgapp.SendMsg(true, "* * *", "Deploying project via Create Go App CLI v"+registry.CLIVersion+"...", "yellow", true)
+ cgapp.ShowMessage(
+ "",
+ fmt.Sprintf("Deploying project via Create Go App CLI v%v...", registry.CLIVersion),
+ true, true,
+ )
- // If config is set and correct, skip survey and use it.
- if useConfigFile && rolesConfig != nil {
- // Re-define variables from config file (default is $PWD/.cgapp.yml).
- username = strings.ToLower(rolesConfig["username"].(string))
- host = strings.ToLower(rolesConfig["host"].(string))
- network = strings.ToLower(rolesConfig["network"].(string))
- port = strings.ToLower(rolesConfig["port"].(string))
- askBecomePass = rolesConfig["become"].(bool)
+ // Set Ansible playbook and inventory files.
+ if askBecomePass {
+ // With entering password.
+ options = []string{"playbook.yml", "-i", "hosts.ini", "-K"}
} else {
- // Start survey.
- if err := survey.Ask(
- registry.DeployQuestions, &deployAnswers, survey.WithIcons(surveyIconsConfig),
- ); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
- }
-
- // If something went wrong, cancel and exit.
- if !deployAnswers.AgreeDeployment {
- cgapp.SendMsg(true, "[!]", "You're stopped deployment process of your project.", "red", false)
- cgapp.SendMsg(false, "[!]", "Run `cgapp deploy` once again!", "red", true)
- os.Exit(1)
- }
-
- // Insert empty line.
- cgapp.SendMsg(false, "", "", "", false)
-
- // Define variables for better display.
- username = deployAnswers.Username
- host = deployAnswers.Host
- network = deployAnswers.Network
- port = deployAnswers.BackendPort
- askBecomePass = deployAnswers.AskBecomePass
+ // Without entering password.
+ options = []string{"playbook.yml", "-i", "hosts.ini"}
}
- // Start timer.
- startTimer := time.Now()
-
// Create config files for your project.
- cgapp.SendMsg(false, "*", "Run Ansible playbook `"+playbook+"`...", "cyan", true)
-
- // Define Ansible options.
- options := []string{
- playbook,
- "-u", username,
- "-e", "host=" + host + " network_name=" + network + " backend_port=" + port,
- }
+ cgapp.ShowMessage(
+ "info",
+ "Ansible playbook for deploy your project is running. Please wait for completion!",
+ false, false,
+ )
- // Check, if need to ask password for username.
- // See: https://docs.ansible.com/ansible/latest/user_guide/become.html#become-command-line-options
- if askBecomePass {
- options = []string{
- playbook,
- "-u", username,
- "-e", "host=" + host + " network_name=" + network + " backend_port=" + port,
- "--ask-become-pass",
- }
- }
+ // Start timer.
+ startTimer := time.Now()
// Run execution for Ansible playbook.
- if err := cgapp.ExecCommand("ansible-playbook", options); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
+ if err := cgapp.ExecCommand("ansible-playbook", options, false); err != nil {
+ return cgapp.ShowError(err.Error())
}
// Stop timer.
- stopTimer := time.Since(startTimer).String()
+ stopTimer := cgapp.CalculateDurationTime(startTimer)
+ cgapp.ShowMessage(
+ "info",
+ fmt.Sprintf("Completed in %v seconds!", stopTimer),
+ false, true,
+ )
+
+ // Ending message.
+ cgapp.ShowMessage(
+ "",
+ "Have a great project launch! :)",
+ false, true,
+ )
- // End message.
- cgapp.SendMsg(true, "* * *", "Completed in "+stopTimer+"!", "yellow", true)
- cgapp.SendMsg(false, "(i)", "A helpful documentation and next steps -> https://create-go.app/", "green", false)
- cgapp.SendMsg(false, "(i)", "Go to the `"+host+"` to see your deployed project! :)", "green", true)
+ return nil
}
func init() {
rootCmd.AddCommand(deployCmd)
deployCmd.PersistentFlags().BoolVarP(
- &useConfigFile,
- "use-config", "c", false,
- "use config file to create a new project or deploy to a remote server (default is $PWD/.cgapp.yml)",
+ &askBecomePass,
+ "", "K", false,
+ "prompt you to provide the remote user sudo password (standard Ansible `--ask-become-pass` option)",
)
}
diff --git a/cmd/init.go b/cmd/init.go
deleted file mode 100644
index d7d52f3..0000000
--- a/cmd/init.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019-present Vic Shóstak. All rights reserved.
-// Use of this source code is governed by Apache 2.0 license
-// that can be found in the LICENSE file.
-
-package cmd
-
-import (
- "os"
-
- "github.com/create-go-app/cli/pkg/cgapp"
- "github.com/create-go-app/cli/pkg/registry"
- "github.com/spf13/cobra"
-)
-
-// initCmd represents the init command.
-var initCmd = &cobra.Command{
- Use: "init",
- Short: "Init a configuration file for the Create Go App project",
- Long: "\nInit a configuration file for the Create Go App project.",
- Run: runInitCmd,
-}
-
-// runInitCmd represents runner for the `init` command.
-var runInitCmd = func(cmd *cobra.Command, args []string) {
- // Get current directory.
- currentDir, _ := os.Getwd()
-
- // Start message.
- cgapp.SendMsg(true, "* * *", "Init a configuration file in `"+currentDir+"` folder...", "yellow", true)
-
- // Create configuration file.
- fileToMake := map[string][]byte{
- ".cgapp.yml": registry.EmbedCGAPPConfig,
- }
- if err := cgapp.MakeFiles(currentDir, fileToMake); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
- }
-
- // End message.
- cgapp.SendMsg(true, "(i)", "A helpful documentation and next steps -> https://create-go.app/", "green", false)
- cgapp.SendMsg(false, "(i)", "Run `cgapp create --use-config` to create a new project by this configuration file.", "green", true)
-}
-
-func init() {
- rootCmd.AddCommand(initCmd)
-}
diff --git a/cmd/root.go b/cmd/root.go
index b884e62..7a0b594 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -5,25 +5,17 @@
package cmd
import (
- "os"
-
"github.com/AlecAivazis/survey/v2"
- "github.com/create-go-app/cli/pkg/cgapp"
"github.com/create-go-app/cli/pkg/registry"
"github.com/spf13/cobra"
- "github.com/spf13/viper"
)
var (
- useConfigFile bool // indicate using config (from $PWD/.cgapp.yml)
- projectConfig map[string]interface{} // parse project config
- rolesConfig map[string]interface{} // parse Ansible roles config
- backend, frontend, webserver string // define project variables
- installAnsibleRoles, askBecomePass bool // install Ansible roles, ask become pass
- username, host, network, port string // define deploy variables
- playbook string = "deploy-playbook.yml" // default Ansible playbook
- createAnswers registry.CreateAnswers // define answers variable for `create` command
- deployAnswers registry.DeployAnswers // define answers variable for `deploy` command
+ backend, frontend, proxy string // define project variables
+ inventory, playbook map[string]interface{} // define template variables
+ options, proxyList []string // define options, proxy list
+ askBecomePass bool // install Ansible roles, ask become pass
+ createAnswers registry.CreateAnswers // define answers variable for `create` command
// Config for survey icons and colors.
// See: https://github.com/mgutz/ansi#style-format
@@ -55,36 +47,8 @@ frontend (JavaScript, TypeScript) and deploy automation
A helpful documentation and next steps -> https://create-go.app/`,
}
-func init() {
- cobra.OnInitialize(initConfig)
-}
-
-// initConfig reads in config file, if set.
-func initConfig() {
- if useConfigFile {
- // Get current directory.
- currentDir, _ := os.Getwd()
-
- viper.AddConfigPath(currentDir) // add config path
- viper.SetConfigName(".cgapp") // set config name
-
- // If a config file is found, read it in.
- if err := viper.ReadInConfig(); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
- }
-
- // Parse configs.
- _ = viper.UnmarshalKey("project", &projectConfig)
- _ = viper.UnmarshalKey("roles", &rolesConfig)
- }
-}
-
// 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() {
- if err := rootCmd.Execute(); err != nil {
- cgapp.SendMsg(true, "[ERROR]", err.Error(), "red", true)
- os.Exit(1)
- }
+ _ = rootCmd.Execute()
}
diff --git a/go.mod b/go.mod
index 3006ea3..661e4f0 100644
--- a/go.mod
+++ b/go.mod
@@ -3,8 +3,7 @@ module github.com/create-go-app/cli
go 1.16
require (
- github.com/AlecAivazis/survey/v2 v2.2.9
+ github.com/AlecAivazis/survey/v2 v2.2.12
github.com/go-git/go-git/v5 v5.3.0
github.com/spf13/cobra v1.1.3
- github.com/spf13/viper v1.7.1
)
diff --git a/go.sum b/go.sum
index 6a2eee5..ea414cd 100644
--- a/go.sum
+++ b/go.sum
@@ -11,9 +11,8 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/AlecAivazis/survey/v2 v2.2.9 h1:LWvJtUswz/W9/zVVXELrmlvdwWcKE60ZAw0FWV9vssk=
-github.com/AlecAivazis/survey/v2 v2.2.9/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/AlecAivazis/survey/v2 v2.2.12 h1:5a07y93zA6SZ09gOa9wLVLznF5zTJMQ+pJ3cZK4IuO8=
+github.com/AlecAivazis/survey/v2 v2.2.12/go.mod h1:6d4saEvBsfSHXeN1a5OA5m2+HJ2LuVokllnC77pAIKI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
@@ -55,7 +54,6 @@ github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
@@ -95,7 +93,6 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -116,7 +113,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
@@ -134,7 +130,6 @@ github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
@@ -154,7 +149,6 @@ github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
@@ -174,7 +168,6 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@@ -182,7 +175,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -210,26 +202,19 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
-github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -237,7 +222,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
@@ -378,7 +362,6 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
diff --git a/pkg/cgapp/create.go b/pkg/cgapp/create.go
deleted file mode 100644
index 307b66a..0000000
--- a/pkg/cgapp/create.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2019-present Vic Shóstak. All rights reserved.
-// Use of this source code is governed by Apache 2.0 license
-// that can be found in the LICENSE file.
-
-package cgapp
-
-import (
- "path/filepath"
- "regexp"
- "strings"
-
- "github.com/create-go-app/cli/pkg/registry"
-)
-
-// CreateProjectFromRegistry function for create a new project from repository.
-func CreateProjectFromRegistry(p *registry.Project, r map[string]*registry.Repository, m string) error {
- // Define vars.
- var template string
-
- // Checking for nil.
- if p == nil || r == nil || m == "" {
- return throwError("Project template, registry or pattern not found!")
- }
-
- // Create path in project root folder.
- folder := filepath.Join(p.RootFolder, p.Type)
-
- // Create match expration.
- match, err := regexp.MatchString(m, p.Name)
- if err != nil {
- return throwError(err.Error())
- }
-
- // Check for regexp.
- if match {
- // Re-define vars.
- template = r[p.Type].List[p.Name]
-
- // If match, create from default template.
- if err := GitClone(folder, template); err != nil {
- return throwError(err.Error())
- }
- } else {
- // Re-define vars.
- template = p.Name
-
- // Else create from user template (from GitHub, etc).
- if err := GitClone(folder, template); err != nil {
- return throwError(err.Error())
- }
- }
-
- // Show success report.
- SendMsg(false, "[OK]", strings.Title(p.Type)+": created with the `"+template+"` template!", "cyan", false)
-
- // Cleanup project.
- foldersToRemove := []string{".git", ".github"}
- RemoveFolders(folder, foldersToRemove)
-
- return nil
-}
-
-// CreateProjectFromCmd function for create a new project from a comand line.
-func CreateProjectFromCmd(p *registry.Project, c map[string]*registry.Command, m string) error {
- // Define vars.
- var options []string
-
- // Checking for nil.
- if p == nil || c == nil || m == "" {
- return throwError("Project template, commands or pattern not found!")
- }
-
- // Create path in project root folder.
- folder := filepath.Join(p.RootFolder, p.Type)
-
- // Create match expration for name.
- match, err := regexp.MatchString(m, p.Name)
- if err != nil {
- return throwError(err.Error())
- }
-
- if match {
- // Split frontend library/framework name and template.
- project, err := stringSplit(":", p.Name)
- if err != nil {
- return throwError(err.Error())
- }
-
- // Re-define vars for more beauty view.
- runner := c[project[0]].Runner
- create := c[project[0]].Create
- args := c[project[0]].Args
-
- // Collect project runner and options.
- switch project[0] {
- case "react":
- // npx create-react-app [template]
- options = []string{create, folder}
- if len(project) > 1 {
- options = []string{create, folder, args["template"], "cra-template-" + project[1]}
- }
- case "preact":
- // preact create [template] [dest] [args...]
- options = []string{create, "default", p.Type, args["cwd"], p.RootFolder, args["name"], "cgapp"}
- if len(project) > 1 {
- options = []string{create, project[1], p.Type, args["cwd"], p.RootFolder, args["name"], "cgapp"}
- }
- case "vue":
- // vue create [options]
- options = []string{create, "--default", "--bare", p.Type}
- if len(project) == 2 {
- options = []string{create, "--preset", project[1], "--bare", p.Type}
- }
- if len(project) == 3 {
- options = []string{create, "--preset", project[1] + ":" + project[2], "--bare", "--clone", p.Type}
- }
- case "angular":
- // ng new [options]
- options = []string{create, "cgapp", "--defaults", "--routing", "--directory", p.Type}
- case "svelte":
- // npx degit [template] [dest]
- options = []string{create, args["template"], folder}
- case "sapper":
- // npx degit [template] [dest]
- options = []string{create, args["template"] + "#rollup", folder}
- if len(project) > 1 {
- options = []string{create, args["template"] + "#" + project[1], folder}
- }
- }
-
- // Run execution command.
- if err := ExecCommand(runner, options); err != nil {
- return throwError(err.Error())
- }
- } else {
- // Create frontend from given repository (GitHub, etc).
- if err := GitClone(folder, p.Name); err != nil {
- return throwError(err.Error())
- }
- }
-
- // Cleanup project.
- folderToRemove := []string{".git", ".github"}
- RemoveFolders(folder, folderToRemove)
-
- // Show success report.
- SendMsg(true, "[OK]", "Frontend: created with template `"+p.Name+"`!", "cyan", false)
-
- return nil
-}
diff --git a/pkg/cgapp/create_test.go b/pkg/cgapp/create_test.go
deleted file mode 100644
index ebe4d71..0000000
--- a/pkg/cgapp/create_test.go
+++ /dev/null
@@ -1,328 +0,0 @@
-package cgapp
-
-import (
- "os"
- "testing"
-
- "github.com/create-go-app/cli/pkg/registry"
-)
-
-func TestCreateProjectFromRegistry(t *testing.T) {
- type args struct {
- p *registry.Project
- r map[string]*registry.Repository
- m string
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- {
- "successfully created backend",
- args{
- p: ®istry.Project{
- Type: "backend",
- Name: "fiber",
- RootFolder: "../../tmp",
- },
- r: registry.Repositories,
- m: registry.RegexpBackendPattern,
- },
- false,
- },
- {
- "successfully created webserver",
- args{
- p: ®istry.Project{
- Type: "webserver",
- Name: "nginx",
- RootFolder: "../../tmp",
- },
- r: registry.Repositories,
- m: registry.RegexpWebServerPattern,
- },
- false,
- },
- {
- "failed to create (not valid repository)",
- args{
- p: ®istry.Project{
- Type: "backend",
- Name: "fiber",
- RootFolder: "../../tmp",
- },
- r: map[string]*registry.Repository{
- "backend": {
- List: map[string]string{},
- },
- },
- m: registry.RegexpAnsiblePattern,
- },
- true,
- },
- {
- "failed to create (not valid repository with wrong name)",
- args{
- p: ®istry.Project{
- Type: "backend",
- Name: "wrong-name",
- RootFolder: "../../tmp",
- },
- r: map[string]*registry.Repository{
- "backend": {
- List: map[string]string{},
- },
- },
- m: registry.RegexpAnsiblePattern,
- },
- true,
- },
- {
- "failed to create (repositories is nil)",
- args{
- p: ®istry.Project{
- Type: "backend",
- Name: "fiber",
- RootFolder: "../../tmp",
- },
- r: nil,
- m: registry.RegexpAnsiblePattern,
- },
- true,
- },
- {
- "failed to create (project registry is nil)",
- args{
- p: nil,
- r: registry.Repositories,
- m: registry.RegexpAnsiblePattern,
- },
- true,
- },
- {
- "failed to create (pattern is nil)",
- args{
- p: ®istry.Project{
- Type: "backend",
- Name: "fiber",
- RootFolder: "../../tmp",
- },
- r: registry.Repositories,
- m: "",
- },
- true,
- },
- {
- "failed to create (repositories, project registry & pattern are nil)",
- args{
- p: nil,
- r: nil,
- m: "",
- },
- true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := CreateProjectFromRegistry(tt.args.p, tt.args.r, tt.args.m); (err != nil) != tt.wantErr {
- t.Errorf("CreateProjectFromRegistry() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
-
- // Clean
- os.RemoveAll("../../tmp")
- }
-}
-
-func TestCreateProjectFromCmd(t *testing.T) {
- type args struct {
- p *registry.Project
- c map[string]*registry.Command
- m string
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- {
- "successfully create react:redux",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "react:redux",
- RootFolder: "../../tmp",
- },
- c: map[string]*registry.Command{
- "react": {
- Runner: "echo",
- Create: "react",
- Args: map[string]string{},
- },
- },
- m: registry.RegexpFrontendPattern,
- },
- false,
- },
- {
- "successfully create preact:simple",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "preact:simple",
- RootFolder: "../../tmp",
- },
- c: map[string]*registry.Command{
- "preact": {
- Runner: "echo",
- Create: "preact",
- Args: map[string]string{},
- },
- },
- m: registry.RegexpFrontendPattern,
- },
- false,
- },
- {
- "successfully create vue (with mock for GitHub)",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "vue:mock",
- RootFolder: "../../tmp",
- },
- c: map[string]*registry.Command{
- "vue": {
- Runner: "echo",
- Create: "vue",
- Args: map[string]string{},
- },
- },
- m: registry.RegexpFrontendPattern,
- },
- false,
- },
- {
- "successfully create vue (with mock for others)",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "vue:mock:mock",
- RootFolder: "../../tmp",
- },
- c: map[string]*registry.Command{
- "vue": {
- Runner: "echo",
- Create: "vue",
- Args: map[string]string{},
- },
- },
- m: registry.RegexpFrontendPattern,
- },
- false,
- },
- {
- "successfully create angular",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "angular",
- RootFolder: "../../tmp",
- },
- c: map[string]*registry.Command{
- "angular": {
- Runner: "echo",
- Create: "angular",
- Args: map[string]string{},
- },
- },
- m: registry.RegexpFrontendPattern,
- },
- false,
- },
- {
- "successfully create svelte",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "svelte",
- RootFolder: "../../tmp",
- },
- c: map[string]*registry.Command{
- "svelte": {
- Runner: "echo",
- Create: "svelte",
- Args: map[string]string{},
- },
- },
- m: registry.RegexpFrontendPattern,
- },
- false,
- },
- {
- "successfully create sapper:webpack",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "sapper:webpack",
- RootFolder: "../../tmp",
- },
- c: map[string]*registry.Command{
- "sapper": {
- Runner: "echo",
- Create: "sapper",
- Args: map[string]string{},
- },
- },
- m: registry.RegexpFrontendPattern,
- },
- false,
- },
- {
- "successfully create from user repository",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "github.com/create-go-app/gin-go-template",
- RootFolder: "../../tmp",
- },
- c: registry.Commands,
- m: registry.RegexpFrontendPattern,
- },
- false,
- },
- {
- "failed to create from user repository",
- args{
- p: ®istry.Project{
- Type: "frontend",
- Name: "unknown.com/user/repo",
- RootFolder: "../../tmp",
- },
- c: registry.Commands,
- m: registry.RegexpFrontendPattern,
- },
- true,
- },
- {
- "failed to create (project registry & commands are nil)",
- args{
- p: nil,
- c: nil,
- m: "",
- },
- true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := CreateProjectFromCmd(tt.args.p, tt.args.c, tt.args.m); (err != nil) != tt.wantErr {
- t.Errorf("CreateProjectFromCmd() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
-
- // Clean
- os.RemoveAll("../../tmp")
- }
-}
diff --git a/pkg/cgapp/exec.go b/pkg/cgapp/exec.go
index ecb562c..92fe457 100644
--- a/pkg/cgapp/exec.go
+++ b/pkg/cgapp/exec.go
@@ -7,47 +7,50 @@ package cgapp
import (
"bufio"
"bytes"
+ "fmt"
"os/exec"
)
// ExecCommand function to execute a given command.
-func ExecCommand(command string, options []string) error {
- //
- if command == "" {
- return throwError("No command to execute!")
+func ExecCommand(command string, options []string, silentMode bool) error {
+ // Checking for nil.
+ if command == "" || options == nil {
+ return fmt.Errorf("No command to execute!")
}
// Create buffer for stderr.
stderr := &bytes.Buffer{}
- // Collect command line
+ // Collect command line.
cmd := exec.Command(command, options...) // #nosec G204
- // Set buffer for stderr from cmd
+ // Set buffer for stderr from cmd.
cmd.Stderr = stderr
- // Create a new reader
- cmdReader, err := cmd.StdoutPipe()
- if err != nil {
- return throwError(err.Error())
+ // Create a new reader.
+ cmdReader, errStdoutPipe := cmd.StdoutPipe()
+ if errStdoutPipe != nil {
+ return ShowError(errStdoutPipe.Error())
}
// Start executing command.
- if err := cmd.Start(); err != nil {
- return throwError(stderr.String())
+ if errStart := cmd.Start(); errStart != nil {
+ return ShowError(stderr.String())
}
- // Create a new scanner and run goroutine func with output.
- scanner := bufio.NewScanner(cmdReader)
- go func() {
- for scanner.Scan() {
- SendMsg(false, "*", scanner.Text(), "cyan", false)
- }
- }()
+ // Create a new scanner and run goroutine func with output, if not in silent mode.
+ if !silentMode {
+ scanner := bufio.NewScanner(cmdReader)
+ go func() {
+ for scanner.Scan() {
+ ShowMessage("", scanner.Text(), false, false)
+ }
+ }()
+ }
// Wait for executing command.
- if err := cmd.Wait(); err != nil {
- return throwError(stderr.String())
+ if errWait := cmd.Wait(); errWait != nil {
+ return ShowError(stderr.String())
}
return nil
diff --git a/pkg/cgapp/exec_test.go b/pkg/cgapp/exec_test.go
index c928406..283946c 100644
--- a/pkg/cgapp/exec_test.go
+++ b/pkg/cgapp/exec_test.go
@@ -4,8 +4,9 @@ import "testing"
func TestExecCommand(t *testing.T) {
type args struct {
- command string
- options []string
+ command string
+ options []string
+ silentMode bool
}
tests := []struct {
name string
@@ -15,8 +16,18 @@ func TestExecCommand(t *testing.T) {
{
"successfully executing command",
args{
- command: "echo",
- options: []string{"ping"},
+ command: "echo",
+ options: []string{"ping"},
+ silentMode: false,
+ },
+ false,
+ },
+ {
+ "successfully executing command with silent mode",
+ args{
+ command: "echo",
+ options: []string{"ping"},
+ silentMode: true,
},
false,
},
@@ -28,7 +39,7 @@ func TestExecCommand(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if err := ExecCommand(tt.args.command, tt.args.options); (err != nil) != tt.wantErr {
+ if err := ExecCommand(tt.args.command, tt.args.options, tt.args.silentMode); (err != nil) != tt.wantErr {
t.Errorf("ExecCommand() error = %v, wantErr %v", err, tt.wantErr)
}
})
diff --git a/pkg/cgapp/files.go b/pkg/cgapp/files.go
index 86903b8..f366a41 100644
--- a/pkg/cgapp/files.go
+++ b/pkg/cgapp/files.go
@@ -5,42 +5,114 @@
package cgapp
import (
+ "embed"
+ "fmt"
+ "io/fs"
"io/ioutil"
"os"
"path/filepath"
+ "strings"
+ "text/template"
)
-// MakeFiles function for massively create folders.
-func MakeFiles(rootFolder string, filesToMake map[string][]byte) error {
- for file, data := range filesToMake {
- folder := filepath.Join(rootFolder, file)
+// EmbeddedFileSystems struct contains embedded file system fields.
+type EmbeddedFileSystem struct {
+ Name embed.FS
+ RootFolder string
+ SkipDir bool
+}
+
+// CopyFromEmbeddedFS function for copy files from embedded file system.
+func CopyFromEmbeddedFS(efs *EmbeddedFileSystem) error {
+ // Return copied folders and files.
+ if err := fs.WalkDir(efs.Name, efs.RootFolder, func(path string, entry fs.DirEntry, err error) error {
+ // Checking embed path.
+ if err != nil {
+ return ShowError(
+ fmt.Sprintf("Can't copy files from embedded path `%v`!", efs.RootFolder),
+ )
+ }
+
+ // Checking, if embedded file is a folder.
+ if entry.IsDir() && !efs.SkipDir {
+ // Create folders structure from embedded.
+ if err := MakeFolder(path); err != nil {
+ return err
+ }
+ }
+
+ // Checking, if embedded file is not a folder.
+ if !entry.IsDir() {
+ // Set file data.
+ fileData, errReadFile := fs.ReadFile(efs.Name, path)
+ if errReadFile != nil {
+ return errReadFile
+ }
- // Write to created file.
- if err := ioutil.WriteFile(folder, data, 0600); err != nil {
- return throwError("File `" + file + "` was not created!")
+ // Path to file, if skipped folders.
+ if efs.SkipDir {
+ path = entry.Name()
+ }
+
+ // Create file from embedded.
+ if errMakeFile := MakeFile(path, fileData); errMakeFile != nil {
+ return errMakeFile
+ }
}
- // Show report for file.
- SendMsg(false, "[OK]", "File `"+file+"` was created!", "cyan", false)
+ return nil
+ }); err != nil {
+ return err
}
return nil
}
-// MakeFolder function for create folder.
-func MakeFolder(folderName string, chmod os.FileMode) error {
- // Check if folder exists, fail if it does.
- if _, err := os.Stat(folderName); !os.IsNotExist(err) {
- return throwError("Folder `" + folderName + "` exists!")
+// GenerateFileFromTemplate func to generate files from templates.
+func GenerateFileFromTemplate(fileName string, variables map[string]interface{}) error {
+ // Parse template.
+ tmpl, errParseFiles := template.ParseFiles(fileName)
+ if errParseFiles != nil {
+ return ShowError(errParseFiles.Error())
+ }
+
+ // Create a new file with template data.
+ file, errCreate := os.Create(fileName)
+ if errCreate != nil {
+ return ShowError(errCreate.Error())
+ }
+
+ // Execute template with variables.
+ if errExecute := tmpl.Execute(file, variables); errExecute != nil {
+ return ShowError(errExecute.Error())
+ }
+ _ = file.Close()
+
+ // Rename output file.
+ newFileName := strings.Replace(fileName, ".tmpl", "", -1)
+ if errRename := os.Rename(fileName, newFileName); errRename != nil {
+ return ShowError(errRename.Error())
}
- // Create folder.
- if err := os.Mkdir(folderName, chmod); err != nil {
- return throwError("Folder `" + folderName + "` was not created!")
+ return nil
+}
+
+// MakeFile function for single file create.
+func MakeFile(fileName string, fileData []byte) error {
+ // Write to created file.
+ if err := ioutil.WriteFile(fileName, fileData, 0600); err != nil {
+ return err
}
- // Show report for folder.
- SendMsg(false, "OK", "Folder `"+folderName+"` was created!", "", false)
+ return nil
+}
+
+// MakeFolder function for create folder.
+func MakeFolder(folderName string) error {
+ // Check if folder exists, fail if it does.
+ if err := os.Mkdir(folderName, 0750); err != nil {
+ return err
+ }
return nil
}
diff --git a/pkg/cgapp/files_test.go b/pkg/cgapp/files_test.go
index 7b6136b..946b571 100644
--- a/pkg/cgapp/files_test.go
+++ b/pkg/cgapp/files_test.go
@@ -1,14 +1,24 @@
package cgapp
import (
+ "io/fs"
"os"
"testing"
+
+ "github.com/create-go-app/cli/pkg/registry"
)
-func TestMakeFiles(t *testing.T) {
+func TestMakeFile(t *testing.T) {
+
+ fileData, err := fs.ReadFile(registry.EmbedMiscFiles, "misc/Makefile")
+ if err != nil {
+ t.Error()
+ }
+
type args struct {
- rootFolder string
- filesToMake map[string][]byte
+ rootFolder string
+ file string
+ data []byte
}
tests := []struct {
name string
@@ -18,36 +28,78 @@ func TestMakeFiles(t *testing.T) {
{
"successfully created files",
args{
- rootFolder: "../../tmp",
- filesToMake: map[string][]byte{
- "test.txt": []byte("test"),
- },
+ rootFolder: "Makefile",
+ file: "Makefile",
+ data: fileData,
},
false,
},
{
"failed created files",
args{
- rootFolder: "./does/not-exists",
- filesToMake: map[string][]byte{
- "test.txt": []byte("test"),
- },
+ rootFolder: "",
+ file: "",
+ data: fileData,
},
true,
},
}
- _ = os.Mkdir("../../tmp", 0750)
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := MakeFile(tt.args.rootFolder, tt.args.data); (err != nil) != tt.wantErr {
+ t.Errorf("MakeFile() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+
+ // Clean
+ os.RemoveAll("Makefile")
+ }
+}
+
+func TestMakeFolder(t *testing.T) {
+ type args struct {
+ folderName string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ "successfully created folder",
+ args{
+ folderName: "../../tmp",
+ },
+ false,
+ },
+ {
+ "failed, folder is exists",
+ args{
+ folderName: "",
+ },
+ true,
+ },
+ {
+ "failed, folder is exists",
+ args{
+ folderName: "cgapp-project",
+ },
+ true,
+ },
+ }
+
+ _ = os.Mkdir("cgapp-project", 0750)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if err := MakeFiles(tt.args.rootFolder, tt.args.filesToMake); (err != nil) != tt.wantErr {
- t.Errorf("MakeFiles() error = %v, wantErr %v", err, tt.wantErr)
+ if err := MakeFolder(tt.args.folderName); (err != nil) != tt.wantErr {
+ t.Errorf("MakeFolder() error = %v, wantErr %v", err, tt.wantErr)
}
})
// Clean
- os.RemoveAll("../../tmp")
+ os.RemoveAll(tt.args.folderName)
}
}
@@ -77,3 +129,107 @@ func TestRemoveFolders(t *testing.T) {
})
}
}
+
+func TestCopyFromEmbeddedFS(t *testing.T) {
+ type args struct {
+ efs *EmbeddedFileSystem
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ "successfully copy from embedded fs",
+ args{
+ efs: &EmbeddedFileSystem{
+ Name: registry.EmbedTemplates,
+ RootFolder: "templates",
+ SkipDir: false,
+ },
+ },
+ false,
+ },
+ {
+ "successfully copy from embedded fs with skip dirs",
+ args{
+ efs: &EmbeddedFileSystem{
+ Name: registry.EmbedTemplates,
+ RootFolder: "templates",
+ SkipDir: true,
+ },
+ },
+ false,
+ },
+ {
+ "fail to copy from embedded fs",
+ args{
+ efs: &EmbeddedFileSystem{
+ Name: registry.EmbedTemplates,
+ RootFolder: "does-not-exist",
+ SkipDir: false,
+ },
+ },
+ true,
+ },
+ {
+ "fail (no args)",
+ args{
+ efs: &EmbeddedFileSystem{},
+ },
+ true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := CopyFromEmbeddedFS(tt.args.efs); (err != nil) != tt.wantErr {
+ t.Errorf("CopyFromEmbeddedFS() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+
+ // Clean
+ _ = os.Remove("hosts.ini.tmpl")
+ _ = os.Remove("playbook.yml.tmpl")
+ _ = os.RemoveAll(tt.args.efs.RootFolder)
+ }
+}
+
+func TestGenerateFileFromTemplate(t *testing.T) {
+ type args struct {
+ fileName string
+ variables map[string]interface{}
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ "successfully generate file",
+ args{
+ fileName: "../../tmp/test.txt",
+ variables: map[string]interface{}{},
+ },
+ false,
+ },
+ {
+ "failed to generate file",
+ args{},
+ true,
+ },
+ }
+
+ _ = os.Mkdir("../../tmp", 0750)
+ _, _ = os.Create("../../tmp/test.txt")
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := GenerateFileFromTemplate(tt.args.fileName, tt.args.variables); (err != nil) != tt.wantErr {
+ t.Errorf("GenerateFileFromTemplate() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+
+ // Clean
+ _ = os.RemoveAll("../../tmp")
+ }
+}
diff --git a/pkg/cgapp/git.go b/pkg/cgapp/git.go
index 2949689..91fa3e8 100644
--- a/pkg/cgapp/git.go
+++ b/pkg/cgapp/git.go
@@ -4,17 +4,43 @@
package cgapp
-import "github.com/go-git/go-git/v5"
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/go-git/go-git/v5"
+)
// GitClone function for `git clone` defined project template.
-func GitClone(rootFolder, templateName string) error {
+func GitClone(templateType, templateURL string) error {
+ // Checking for nil.
+ if templateType == "" || templateURL == "" {
+ return fmt.Errorf("Project template not found!")
+ }
+
+ // Get current directory.
+ currentDir, _ := os.Getwd()
+
+ // Set project folder.
+ folder := filepath.Join(currentDir, templateType)
+
// Clone project template.
- _, err := git.PlainClone(rootFolder, false, &git.CloneOptions{
- URL: "https://" + templateName,
- })
- if err != nil {
- return throwError("Repository was not cloned!")
+ _, errPlainClone := git.PlainClone(
+ folder,
+ false,
+ &git.CloneOptions{
+ URL: "https://" + templateURL,
+ },
+ )
+ if errPlainClone != nil {
+ return ShowError(
+ fmt.Sprintf("Repository `%v` was not cloned!", templateURL),
+ )
}
+ // Cleanup project.
+ RemoveFolders(folder, []string{".git", ".github"})
+
return nil
}
diff --git a/pkg/cgapp/git_test.go b/pkg/cgapp/git_test.go
index 6f42047..0192f5c 100644
--- a/pkg/cgapp/git_test.go
+++ b/pkg/cgapp/git_test.go
@@ -18,19 +18,27 @@ func TestGitClone(t *testing.T) {
{
"successfully cloned project",
args{
- rootFolder: "../../tmp",
- templateName: "github.com/create-go-app/fiber-go-template",
+ rootFolder: "../../tmp/test",
+ templateName: "github.com/koddr/koddr",
},
false,
},
{
"failed clone project (empty template)",
args{
- rootFolder: "../../tmp",
+ rootFolder: "../../tmp/test",
templateName: "",
},
true,
},
+ {
+ "failed clone project",
+ args{
+ rootFolder: "../../tmp/test",
+ templateName: "404.404/404/404",
+ },
+ true,
+ },
{
"failed clone project (empty args)",
args{},
diff --git a/pkg/cgapp/utils.go b/pkg/cgapp/utils.go
index ace48c0..3d967f1 100644
--- a/pkg/cgapp/utils.go
+++ b/pkg/cgapp/utils.go
@@ -6,83 +6,61 @@ package cgapp
import (
"fmt"
- "regexp"
+ "time"
)
-// BeautifyText function for send (colored or common) message to output.
-func BeautifyText(text, color string) string {
+// ShowMessage function for send message to output.
+func ShowMessage(level, text string, startWithNewLine, endWithNewLine bool) {
// Define variables.
- var (
- red string = "\033[0;31m"
- green string = "\033[0;32m"
- cyan string = "\033[0;36m"
- yellow string = "\033[1;33m"
- noColor string = "\033[0m"
- textColor string
- )
-
- // Switch color.
- switch color {
- case "":
- textColor = noColor
- case "green":
- textColor = green
- case "yellow":
- textColor = yellow
- case "red":
- textColor = red
- case "cyan":
- textColor = cyan
- }
-
- // Send common or colored text.
- return textColor + text + noColor
-}
-
-// SendMsg function forsend message to output.
-func SendMsg(startWithNewLine bool, caption, text, color string, endWithNewLine bool) {
- // Define variables.
- var startNewLine, endNewLine string
+ var startLine, endLine string
if startWithNewLine {
- startNewLine = "\n" // set new line
+ startLine = "\n" // set a new line
}
if endWithNewLine {
- endNewLine = "\n" // set new line
+ endLine = "\n" // set a new line
}
- if caption == "" {
- fmt.Println(startNewLine + text + endNewLine) // common text
- } else {
- fmt.Println(startNewLine + BeautifyText(caption, color) + " " + text + endNewLine) // colorized text
- }
+ fmt.Println(startLine + colorizeLevel(level) + text + endLine)
}
-// stringSplit function for split string by pattern.
-func stringSplit(pattern, match string) ([]string, error) {
- // Error, when empty or nil.
- if pattern == "" || match == "" {
- return nil, throwError("Frontend template not set!")
- }
+// ShowError function for send error message to output.
+func ShowError(text string) error {
+ return fmt.Errorf(colorizeLevel("error") + text)
+}
- // Define empty []string{} for splitted strings.
- splittedStrings := []string{}
+// CalculateDurationTime func to calculate duration time.
+func CalculateDurationTime(startTimer time.Time) string {
+ return fmt.Sprintf("%.0f", time.Since(startTimer).Seconds())
+}
- // Create regexp.
- re := regexp.MustCompile(pattern)
+// colorizeLevel function for send (colored or common) message to output.
+func colorizeLevel(level string) string {
+ // Define variables.
+ var (
+ red string = "\033[0;31m"
+ green string = "\033[0;32m"
+ yellow string = "\033[1;33m"
+ noColor string = "\033[0m"
+ color, icon string
+ )
- // Split match string.
- split := re.Split(match, -1)
- for str := range split {
- // Append all matched strings to set.
- splittedStrings = append(splittedStrings, split[str])
+ // Switch color.
+ switch level {
+ case "success":
+ color = green
+ icon = "[OK] "
+ case "error":
+ color = red
+ icon = "[ERROR] "
+ case "info":
+ color = yellow
+ icon = "[INFO] "
+ default:
+ color = noColor
}
- return splittedStrings, nil
-}
-
-// throwError function for throw an error.
-func throwError(text string) error {
- return fmt.Errorf(BeautifyText(text, "red"))
+ // Send common or colored caption.
+ return color + icon + noColor
}
diff --git a/pkg/cgapp/utils_test.go b/pkg/cgapp/utils_test.go
index e0907bf..69c0b56 100644
--- a/pkg/cgapp/utils_test.go
+++ b/pkg/cgapp/utils_test.go
@@ -1,15 +1,13 @@
package cgapp
import (
- "os"
- "reflect"
"testing"
+ "time"
)
-func TestBeautifyText(t *testing.T) {
+func Test_colorizeLevel(t *testing.T) {
type args struct {
- text string
- color string
+ level string
}
tests := []struct {
name string
@@ -17,53 +15,33 @@ func TestBeautifyText(t *testing.T) {
}{
{
"successfully send message",
- args{
- text: "Hello World!",
- color: "",
- },
+ args{level: ""},
},
{
- "successfully send colored message",
- args{
- text: "Hello World!",
- color: "green",
- },
+ "successfully send success message",
+ args{level: "success"},
},
{
- "successfully send colored message",
- args{
- text: "Hello World!",
- color: "yellow",
- },
+ "successfully send error message",
+ args{level: "error"},
},
{
- "successfully send colored message",
- args{
- text: "Hello World!",
- color: "cyan",
- },
- },
- {
- "successfully send colored message",
- args{
- text: "Hello World!",
- color: "red",
- },
+ "successfully send info message",
+ args{level: "info"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- _ = BeautifyText(tt.args.text, tt.args.color)
+ _ = colorizeLevel(tt.args.level)
})
}
}
-func TestSendMsg(t *testing.T) {
+func TestShowMessage(t *testing.T) {
type args struct {
- startWithNewLine bool
- caption string
+ level string
text string
- color string
+ startWithNewLine bool
endWithNewLine bool
}
tests := []struct {
@@ -76,111 +54,37 @@ func TestSendMsg(t *testing.T) {
},
{
"successfully send message with args",
- args{true, "!", "Test", "", true},
+ args{"success", "Test", true, true},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- SendMsg(tt.args.startWithNewLine, tt.args.caption, tt.args.text, tt.args.color, tt.args.endWithNewLine)
+ ShowMessage(tt.args.level, tt.args.text, tt.args.startWithNewLine, tt.args.endWithNewLine)
})
}
}
-func TestMakeFolder(t *testing.T) {
+func TestCalculateDurationTime(t *testing.T) {
type args struct {
- folderName string
- chmod os.FileMode
+ startTimer time.Time
}
tests := []struct {
- name string
- args args
- wantErr bool
- }{
- {
- "successfully created folder",
- args{
- folderName: "../../tmp",
- chmod: 0750,
- },
- false,
- },
- {
- "failed, folder is exists",
- args{
- folderName: "",
- chmod: 0750,
- },
- true,
- },
- {
- "failed, folder is exists",
- args{
- folderName: "cgapp-project",
- chmod: 0750,
- },
- true,
- },
- }
-
- _ = os.Mkdir("cgapp-project", 0750)
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := MakeFolder(tt.args.folderName, tt.args.chmod); (err != nil) != tt.wantErr {
- t.Errorf("MakeFolder() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
-
- // Clean
- os.RemoveAll(tt.args.folderName)
- }
-}
-
-func Test_stringSplit(t *testing.T) {
- type args struct {
- pattern string
- match string
- }
- tests := []struct {
- name string
- args args
- want []string
- wantErr bool
+ name string
+ args args
+ want string
}{
{
- "successfully matched",
- args{
- pattern: ":",
- match: "react:redux",
- },
- []string{"react", "redux"},
- false,
- },
- {
- "successfully not matched",
+ "successfully",
args{
- pattern: "=",
- match: "react:redux",
+ startTimer: time.Now(),
},
- []string{"react:redux"},
- false,
- },
- {
- "failed wrong pattern and match",
- args{},
- nil,
- true,
+ "0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- got, err := stringSplit(tt.args.pattern, tt.args.match)
- if (err != nil) != tt.wantErr {
- t.Errorf("StringSplit() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("StringSplit() = %v, want %v", got, tt.want)
+ if got := CalculateDurationTime(tt.args.startTimer); got != tt.want {
+ t.Errorf("CalculateDurationTime() = %v, want %v", got, tt.want)
}
})
}
diff --git a/pkg/registry/configs/.cgapp.yml b/pkg/registry/configs/.cgapp.yml
deleted file mode 100644
index b847f09..0000000
--- a/pkg/registry/configs/.cgapp.yml
+++ /dev/null
@@ -1,73 +0,0 @@
-# A default configuration for Create Go App project.
-# Author: Vic Shóstak (https://shostak.dev)
-# For more information, please visit https://create-go.app/
-
-# Project config.
-project:
- # Backend for your project.
- # (Required)
- # String:
- # - `net/http`
- # - `fiber`
- # User template: supported, set to URL (without protocol),
- # like `github.com/user/template`
- - backend: fiber
-
- # Frontend for your project.
- # (Optional, to skip set to `none`)
- # String:
- # - `react`
- # - `react:`
- # - `preact`
- # - `preact:`
- # - `vue`
- # - `vue:` (for preset from GitHub)
- # - `vue::` (for presets from others)
- # - `angular`
- # - `svelte`
- # - `sapper`
- # - `sapper:`
- # User template: supported, set to URL (without protocol),
- # like `github.com/user/template`
- - frontend: svelte
-
- # Web/Proxy server for your project.
- # (Optional, to skip set to `none`)
- # String: `nginx`
- # User template: supported, set to URL (without protocol),
- # like `github.com/user/template`
- - webserver: nginx
-
-# Automation config.
-roles:
- # Ansible roles for deploy your project.
- # (Optional, to skip set to empty or comment)
- # Objects list.
- - deploy:
- # Username of remote's server or local's user.
- # (Required)
- username: root
-
- # If you need to deploy (or run) a project asking for a password
- # for the user, set `become` to `true`. This is equivalent of
- # `--ask-become-pass`, a standard Ansible argument
- # to ask for a privilege escalation password.
- # (Optional)
- become: true
-
- # Host name from your inventory file (usually, at /etc/ansible/hosts).
- # (Required)
- host: localhost
-
- # Name of Docker network
- # (Required)
- network: cgapp_network
-
- # Port for backend Docker container (both in and out).
- # (Required)
- backend_port: 5000
-
- # Filename of Ansible playbook in the root of the Create Go App project.
- # If you want to rename it, do it, but not to change destination of file!
- # (Required)
- playbook: deploy-playbook.yml
diff --git a/pkg/registry/configs/deploy-playbook.yml b/pkg/registry/configs/deploy-playbook.yml
deleted file mode 100644
index f9e3b78..0000000
--- a/pkg/registry/configs/deploy-playbook.yml
+++ /dev/null
@@ -1,82 +0,0 @@
-# Ansible playbook for deploy the Create Go App project.
-# Author: Vic Shóstak (https://shostak.dev)
-# For more information, please visit https://create-go.app/
-
----
-- hosts: "{{ host }}"
- become: yes
-
- tasks:
- #
- # Create a new Docker network for connect all project elements into one network.
- #
- - name: Create a new Docker network ({{ network_name }})
- docker_network:
- name: "{{ network_name }}"
-
- #
- # Check all necessary project folders for future use.
- #
- - name: Check, if ./backend directory is exists
- stat:
- path: ./backend
- register: backend_folder
-
- - name: Check, if ./webserver directory is exists
- stat:
- path: ./webserver
- register: webserver_folder
-
- #
- # The block that builds and runs the backend part of the project.
- #
- - name: Backend block
- block:
- - name: Builds Docker image for backend
- docker_image:
- name: cgapp_backend # name of the backend image
- build:
- path: ./backend # folder with Dockerfile
- pull: yes
- source: build
-
- - name: Runs Docker container with backend
- docker_container:
- name: cgapp-backend # name of the backend container
- image: cgapp_backend:latest
- recreate: yes
- networks:
- - name: "{{ network_name }}"
- ports:
- - "{{ backend_port }}:{{ backend_port }}"
- state: started
- # Run block only if ./backend is a folder and exists.
- when: backend_folder.stat.exists and backend_folder.stat.isdir
-
- #
- # The block that builds and runs the webserver part of the project.
- #
- - name: Webserver block
- block:
- - name: Builds Docker image for webserver
- docker_image:
- name: cgapp_webserver # name of the webserver image
- build:
- path: ./webserver
- pull: yes
- source: build
-
- - name: Runs Docker container with webserver
- docker_container:
- name: cgapp-webserver # name of the webserver container
- image: cgapp_webserver:latest
- recreate: yes
- networks:
- - name: "{{ network_name }}"
- env:
- APP_DOMAIN: "{{ host }}"
- ports:
- - "80:80"
- state: started
- # Run block only if ./webserver is a folder and exists.
- when: webserver_folder.stat.exists and webserver_folder.stat.isdir
diff --git a/pkg/registry/defaults.go b/pkg/registry/defaults.go
index e0b399f..57cd372 100644
--- a/pkg/registry/defaults.go
+++ b/pkg/registry/defaults.go
@@ -5,147 +5,39 @@
package registry
import (
- _ "embed" // for embed config files
+ "embed"
"github.com/AlecAivazis/survey/v2"
)
-const (
- // CLIVersion version of Create Go App CLI.
- CLIVersion = "1.7.5"
- // RegexpAnsiblePattern pattern for Ansible roles.
- RegexpAnsiblePattern = "^(deploy)$"
- // RegexpBackendPattern pattern for backend.
- RegexpBackendPattern = "^(net/http|fiber)$"
- // RegexpFrontendPattern pattern for backend.
- RegexpFrontendPattern = "^(p?react:?|vue(:?[\\w]+)?(:?[\\w-_0-9\\/]+)?|angular|svelte|sapper:?)"
- // RegexpWebServerPattern pattern for web/proxy servers.
- RegexpWebServerPattern = "^(nginx)$"
-)
-
-// Project struct for describe project.
-type Project struct {
- Type string
- Name string
- RootFolder string
-}
+// CLIVersion version of Create Go App CLI.
+const CLIVersion string = "2.0.0"
-// Repository struct for describe repositories collection.
-type Repository struct {
- List map[string]string
-}
-
-// Command struct for describe commands collection.
-type Command struct {
- Runner string
- Create string
- Args map[string]string
+// Variables struct for Ansible variables (inventory, hosts).
+type Variables struct {
+ List map[string]interface{}
}
// CreateAnswers struct for a survey's answers for `create` command.
type CreateAnswers struct {
- Backend string
- Frontend string
- Webserver string
- InstallAnsibleRoles bool `survey:"roles"`
- AgreeCreation bool `survey:"agree"`
-}
-
-// DeployAnswers struct for a survey's answers for `deploy` command.
-type DeployAnswers struct {
- Username string
- Host string
- Network string
- BackendPort string
- AskBecomePass bool `survey:"become"`
- AgreeDeployment bool `survey:"agree"`
+ Backend string
+ Frontend string
+ Proxy string
+ AgreeCreation bool `survey:"agree"`
}
var (
- // EmbedCGAPPConfig main config file for Create Go App.
- //go:embed configs/.cgapp.yml
- EmbedCGAPPConfig []byte
+ // EmbedMiscFiles misc files and configs.
+ //go:embed misc/*
+ EmbedMiscFiles embed.FS
- // EmbedEditorConfig config for auto configuration IDE.
- //go:embed configs/.editorconfig
- EmbedEditorConfig []byte
-
- // EmbedGitAttributes config for git attributes.
- //go:embed configs/.gitattributes
- EmbedGitAttributes []byte
-
- // EmbedGitIgnore config for git ignore files.
- //go:embed configs/.gitignore
- EmbedGitIgnore []byte
-
- // EmbedMakefile file for rapid manipulation with a new app.
- //go:embed configs/Makefile
- EmbedMakefile []byte
-
- // EmbedDeployPlaybook Ansible playbook for deployment.
- //go:embed configs/deploy-playbook.yml
- EmbedDeployPlaybook []byte
-
- // Repositories collection.
- Repositories = map[string]*Repository{
- // Backend templates.
- "backend": {
- List: map[string]string{
- "net/http": "github.com/create-go-app/net_http-go-template",
- "fiber": "github.com/create-go-app/fiber-go-template",
- },
- },
+ // EmbedRoles Ansible roles.
+ //go:embed roles/*
+ EmbedRoles embed.FS
- // Docker containers with web/proxy servers.
- "webserver": {
- List: map[string]string{
- "nginx": "github.com/create-go-app/nginx-docker",
- },
- },
- }
-
- // Commands collection.
- Commands = map[string]*Command{
- "react": {
- Runner: "npx",
- Create: "create-react-app",
- Args: map[string]string{
- "template": "--template",
- },
- },
- "preact": {
- Runner: "preact",
- Create: "create",
- Args: map[string]string{
- "cwd": "--cwd",
- "name": "--name",
- },
- },
- "vue": {
- Runner: "vue",
- Create: "create",
- Args: map[string]string{},
- },
- "angular": {
- Runner: "ng",
- Create: "new",
- Args: map[string]string{},
- },
- "svelte": {
- Runner: "npx",
- Create: "degit",
- Args: map[string]string{
- "template": "sveltejs/template",
- },
- },
- "sapper": {
- Runner: "npx",
- Create: "degit",
- Args: map[string]string{
- "template": "sveltejs/sapper-template",
- },
- },
- }
+ // EmbedTemplates template files.
+ //go:embed templates/*
+ EmbedTemplates embed.FS
// CreateQuestions survey's questions for `create` command.
CreateQuestions = []*survey.Question{
@@ -153,89 +45,98 @@ var (
Name: "backend",
Prompt: &survey.Select{
Message: "Choose a backend framework:",
- Options: []string{"net/http", "Fiber"},
- Default: "Fiber",
+ Options: []string{
+ "net/http",
+ "fiber",
+ },
+ Default: "fiber",
+ PageSize: 2,
},
Validate: survey.Required,
},
{
Name: "frontend",
Prompt: &survey.Select{
- Message: "Choose a frontend UI library:",
- Options: []string{"none", "React", "Preact", "Vue", "Angular", "Svelte", "Sapper"},
- Default: "none",
+ Message: "Choose a frontend framework/library:",
+ Help: "Option with a `*-ts` tail will create a TypeScript template.",
+ Options: []string{
+ "none",
+ "vanilla",
+ "vanilla-ts",
+ "react",
+ "react-ts",
+ "preact",
+ "preact-ts",
+ "vue",
+ "vue-ts",
+ "svelte",
+ "svelte-ts",
+ "lit-element",
+ "lit-element-ts",
+ },
+ Default: "none",
+ PageSize: 13,
},
},
{
- Name: "webserver",
+ Name: "proxy",
Prompt: &survey.Select{
Message: "Choose a web/proxy server:",
- Options: []string{"none", "Nginx"},
- Default: "none",
- },
- },
- {
- Name: "roles",
- Prompt: &survey.Confirm{
- Message: "Do you want to create Ansible playbook for deploy your project?",
- Default: true,
+ Options: []string{
+ "none",
+ "traefik",
+ "traefik-acme-dns",
+ "nginx",
+ },
+ Default: "none",
+ PageSize: 4,
},
},
{
Name: "agree",
Prompt: &survey.Confirm{
- Message: "If everything is okay, can I create this project? ;)",
+ Message: "If everything is okay, can I create this project for you? ;)",
Default: true,
},
},
}
- // DeployQuestions survey's questions for `deploy` command.
- DeployQuestions = []*survey.Question{
- {
- Name: "username",
- Prompt: &survey.Input{
- Message: "Enter username:",
- Default: "root",
+ // AnsibleInventoryVariables list of variables for inventory.
+ AnsibleInventoryVariables = map[string]*Variables{
+ "traefik": {
+ List: map[string]interface{}{
+ "Proxy": "traefik",
+ "Wildcard": false,
},
- Validate: survey.Required,
},
- {
- Name: "become",
- Prompt: &survey.Confirm{
- Message: "Do you need to enter password for this username?",
- Default: true,
+ "traefik-acme-dns": {
+ List: map[string]interface{}{
+ "Proxy": "traefik",
+ "Wildcard": true,
},
},
- {
- Name: "host",
- Prompt: &survey.Input{
- Message: "Enter host name to deploy:",
- Default: "localhost",
+ "nginx": {
+ List: map[string]interface{}{
+ "Proxy": "nginx",
},
- Validate: survey.Required,
},
- {
- Name: "network",
- Prompt: &survey.Input{
- Message: "Enter name of Docker network:",
- Default: "cgapp_network",
+ }
+
+ // AnsiblePlaybookVariables list of variables for playbook.
+ AnsiblePlaybookVariables = map[string]*Variables{
+ "traefik": {
+ List: map[string]interface{}{
+ "Proxy": "traefik",
},
- Validate: survey.Required,
},
- {
- Name: "port",
- Prompt: &survey.Input{
- Message: "Enter port of backend Docker container:",
- Default: "5000",
+ "traefik-acme-dns": {
+ List: map[string]interface{}{
+ "Proxy": "traefik",
},
- Validate: survey.Required,
},
- {
- Name: "agree",
- Prompt: &survey.Confirm{
- Message: "If everything is okay, can I deploy this project? ;)",
- Default: true,
+ "nginx": {
+ List: map[string]interface{}{
+ "Proxy": "nginx",
},
},
}
diff --git a/pkg/registry/configs/.editorconfig b/pkg/registry/misc/.editorconfig
similarity index 91%
rename from pkg/registry/configs/.editorconfig
rename to pkg/registry/misc/.editorconfig
index 122c635..2a25d54 100644
--- a/pkg/registry/configs/.editorconfig
+++ b/pkg/registry/misc/.editorconfig
@@ -15,6 +15,6 @@ insert_final_newline = true
indent_style = tab
indent_size = 4
-[{Dockerfile,*.yml,*.yaml}]
+[{Dockerfile,Makefile,*.yml,*.yaml}]
indent_style = tab
indent_size = 2
diff --git a/pkg/registry/configs/.gitattributes b/pkg/registry/misc/.gitattributes
similarity index 100%
rename from pkg/registry/configs/.gitattributes
rename to pkg/registry/misc/.gitattributes
diff --git a/pkg/registry/configs/.gitignore b/pkg/registry/misc/.gitignore
similarity index 84%
rename from pkg/registry/configs/.gitignore
rename to pkg/registry/misc/.gitignore
index 856ef0e..79586ae 100644
--- a/pkg/registry/configs/.gitignore
+++ b/pkg/registry/misc/.gitignore
@@ -9,5 +9,12 @@
**/app/
**/build/
+# Temporary files
+tmp/
+
# Node.js dependencies
**/node_modules/
+
+# Files
+hosts.ini
+*.out
\ No newline at end of file
diff --git a/pkg/registry/configs/Makefile b/pkg/registry/misc/Makefile
similarity index 79%
rename from pkg/registry/configs/Makefile
rename to pkg/registry/misc/Makefile
index 3100798..1e97922 100644
--- a/pkg/registry/configs/Makefile
+++ b/pkg/registry/misc/Makefile
@@ -12,9 +12,9 @@ test:
@if [ -d "$(BACKEND_PATH)" ]; then cd $(BACKEND_PATH) && go test ./...; fi
run: test
- @if [ -d "$(FRONTEND_PATH)" ]; then cd $(FRONTEND_PATH) && $(MAKE) run; fi
+ @if [ -d "$(FRONTEND_PATH)" ]; then cd $(FRONTEND_PATH) && npm run dev; fi
@if [ -d "$(BACKEND_PATH)" ]; then cd $(BACKEND_PATH) && $(MAKE) run; fi
build: test
- @if [ -d "$(FRONTEND_PATH)" ]; then cd $(FRONTEND_PATH) && $(MAKE) build; fi
+ @if [ -d "$(FRONTEND_PATH)" ]; then cd $(FRONTEND_PATH) && npm run build; fi
@if [ -d "$(BACKEND_PATH)" ]; then cd $(BACKEND_PATH) && $(MAKE) build; fi
diff --git a/pkg/registry/roles/backend/tasks/main.yml b/pkg/registry/roles/backend/tasks/main.yml
new file mode 100644
index 0000000..be9e10f
--- /dev/null
+++ b/pkg/registry/roles/backend/tasks/main.yml
@@ -0,0 +1,67 @@
+# Ansible role for deploy a backend of the Create Go App project.
+# Author: Vic Shóstak (https://shostak.dev)
+# For more information, please visit https://create-go.app/
+
+---
+#
+# Delete backend files.
+#
+- name: Delete previous backend files
+ file:
+ state: absent
+ path: "{{ server_dir }}/backend"
+
+#
+# Copy `./backend` folder to the remote server.
+#
+- name: Copy a new backend files
+ synchronize:
+ src: ./backend
+ dest: "{{ server_dir }}"
+ rsync_opts:
+ - "--exclude=.git,.github,build,*.md"
+
+#
+# Build backend Docker container.
+#
+- name: Build Docker image for backend
+ docker_image:
+ name: cgapp_backend # name of the backend image
+ build:
+ path: "{{ server_dir }}/backend" # folder with Dockerfile
+ pull: yes
+ source: build
+
+#
+# Run backend container (for Traefik Proxy).
+#
+- name: Run Docker container with backend (for Traefik Proxy)
+ docker_container:
+ name: cgapp-backend # name of the backend container
+ image: cgapp_backend:latest
+ restart_policy: unless-stopped
+ recreate: true
+ networks:
+ - name: "{{ docker_network }}"
+ ports:
+ - "{{ backend_port }}:{{ backend_port }}"
+ labels:
+ traefik.enable: "true"
+ traefik.http.routers.backend.rule: "Host(`{{ project_domain }}`)"
+ traefik.http.routers.backend.entrypoints: "websecure"
+ when: traefik_version is defined
+
+#
+# Run backend container (for Nginx).
+#
+- name: Run Docker container with backend (for Nginx)
+ docker_container:
+ name: cgapp-backend # name of the backend container
+ image: cgapp_backend:latest
+ restart_policy: unless-stopped
+ recreate: true
+ networks:
+ - name: "{{ docker_network }}"
+ ports:
+ - "{{ backend_port }}:{{ backend_port }}"
+ when: nginx_version is defined
diff --git a/pkg/registry/roles/docker/tasks/main.yml b/pkg/registry/roles/docker/tasks/main.yml
new file mode 100755
index 0000000..9464e3a
--- /dev/null
+++ b/pkg/registry/roles/docker/tasks/main.yml
@@ -0,0 +1,57 @@
+# Ansible role for deploy the Create Go App project.
+# Author: Vic Shóstak (https://shostak.dev)
+# For more information, please visit https://create-go.app/
+
+---
+#
+# Make `apt update`.
+#
+- name: Update system packages cache
+ apt:
+ update_cache: yes
+ cache_valid_time: 3600
+
+#
+# Make `apt upgrade`.
+#
+- name: Update all system packages to their latest versions
+ apt:
+ name: "*"
+ state: latest
+
+#
+# Install `python3-pip` package for working with Docker from Ansible on the remote host.
+#
+- name: Install python3-pip package
+ apt:
+ pkg:
+ - python3-pip
+
+#
+# Install `docker` package for Python 3 from pip3.
+#
+- name: Add the Python 3 client for Docker
+ pip:
+ name: docker
+
+#
+# Create a new system group for Docker.
+#
+- name: Create a new Docker group
+ group:
+ name: "{{ server_group }}"
+
+#
+# Create a new system user for Docker.
+#
+- name: Add the default user to the Docker group
+ user:
+ name: "{{ server_user }}"
+ group: "{{ server_group }}"
+
+#
+# Create a new Docker network for connect all project elements into one network.
+#
+- name: Create a new Docker network
+ docker_network:
+ name: "{{ docker_network }}"
diff --git a/pkg/registry/roles/nginx/tasks/main.yml b/pkg/registry/roles/nginx/tasks/main.yml
new file mode 100755
index 0000000..4e5cd6a
--- /dev/null
+++ b/pkg/registry/roles/nginx/tasks/main.yml
@@ -0,0 +1,57 @@
+# Ansible role for deploy the Nginx for the Create Go App project.
+# Author: Vic Shóstak (https://shostak.dev)
+# For more information, please visit https://create-go.app/
+
+---
+#
+# Delete webserver files.
+#
+- name: Delete previous webserver files
+ file:
+ state: absent
+ path: "{{ server_dir }}/webserver"
+
+#
+# Create folder for Nginx.
+#
+- name: Ensures Nginx dir exists
+ file:
+ state: directory
+ path: "{{ server_dir }}/webserver"
+ owner: "{{ server_user }}"
+ group: "{{ server_group }}"
+
+#
+# Create Nginx config.
+#
+- name: Adding nginx.conf file
+ template:
+ src: nginx.conf.j2
+ dest: "{{ server_dir }}/webserver/nginx.conf"
+ mode: 0600
+
+#
+# Create default server config.
+#
+- name: Adding default.conf file
+ template:
+ src: "{{ 'default-https.conf.j2' if nginx_use_only_https == 'yes' else 'default-http.conf.j2' }}"
+ dest: "{{ server_dir }}/webserver/default.conf"
+ mode: 0600
+
+#
+# Run official Nginx Docker container with specified version.
+#
+- name: Run Nginx container
+ docker_container:
+ name: cgapp-nginx
+ image: "nginx:{{ nginx_version }}"
+ restart_policy: unless-stopped
+ recreate: true
+ networks:
+ - name: "{{ docker_network }}"
+ ports:
+ - ["80:80", "{{ '443:443' if nginx_use_only_https == 'yes' }}"]
+ volumes:
+ - "{{ server_dir }}/webserver/nginx.conf:/etc/nginx/nginx.conf:ro"
+ - "{{ server_dir }}/webserver/default.conf:/etc/nginx/conf.d/default.conf:ro"
diff --git a/pkg/registry/roles/nginx/templates/default-http.conf.j2 b/pkg/registry/roles/nginx/templates/default-http.conf.j2
new file mode 100755
index 0000000..fa49244
--- /dev/null
+++ b/pkg/registry/roles/nginx/templates/default-http.conf.j2
@@ -0,0 +1,33 @@
+#
+# Default server config without SSL certificate:
+#
+
+{% if nginx_redirect_to_non_www %}
+# WWW redirects
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server;
+ server_name www.{{ project_domain }};
+ return 301 https://{{ project_domain }}$request_uri;
+}
+{% endif %}
+
+# Main server
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server;
+{% if nginx_redirect_to_non_www %}
+ server_name {{ project_domain }};
+{% else %}
+ server_name {{ project_domain }} www.{{ project_domain }};
+{% endif %}
+ location / {
+ resolver 127.0.0.11;
+ proxy_pass http://cgapp-backend:{{ backend_port }}/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_redirect off;
+ }
+}
diff --git a/pkg/registry/roles/nginx/templates/default-https.conf.j2 b/pkg/registry/roles/nginx/templates/default-https.conf.j2
new file mode 100755
index 0000000..800c66b
--- /dev/null
+++ b/pkg/registry/roles/nginx/templates/default-https.conf.j2
@@ -0,0 +1,48 @@
+#
+# Default server config with SSL certificate (Let's Encrypt):
+#
+
+# HTTP redirects
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server;
+ server_name {{ project_domain }} www.{{ project_domain }};
+ return 301 https://{{ project_domain }}$request_uri;
+}
+
+# WWW redirects
+server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+{% if nginx_redirect_to_non_www %}
+ server_name www.{{ project_domain }};
+ return 301 https://{{ project_domain }}$request_uri;
+{% else %}
+ server_name {{ project_domain }};
+ return 301 https://www.{{ project_domain }}$request_uri;
+{% endif %}
+}
+
+# Main server
+server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+{% if nginx_redirect_to_non_www %}
+ server_name {{ project_domain }};
+{% else %}
+ server_name {{ project_domain }} www.{{ project_domain }};
+{% endif %}
+ ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
+ ssl_certificate /etc/letsencrypt/live/{{ project_domain }}/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/{{ project_domain }}/privkey.pem;
+ ssl_trusted_certificate /etc/letsencrypt/live/{{ project_domain }}/chain.pem;
+ location / {
+ resolver 127.0.0.11;
+ proxy_pass http://cgapp-backend:{{ backend_port }}/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_redirect off;
+ }
+}
diff --git a/pkg/registry/roles/nginx/templates/nginx.conf.j2 b/pkg/registry/roles/nginx/templates/nginx.conf.j2
new file mode 100755
index 0000000..bd953d6
--- /dev/null
+++ b/pkg/registry/roles/nginx/templates/nginx.conf.j2
@@ -0,0 +1,82 @@
+#
+# Nginx config
+#
+
+worker_processes auto;
+
+events {
+ use epoll;
+ multi_accept on;
+ worker_connections 1024;
+}
+
+http {
+ charset utf-8;
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ server_tokens off;
+ log_not_found off;
+
+ # Bucket size for server names hash
+ server_names_hash_bucket_size 64;
+
+ # MIME
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ # Logging
+ error_log /var/log/nginx/error.log crit;
+ access_log off;
+
+ # Timeouts
+ send_timeout 2;
+ keepalive_timeout 30;
+ keepalive_requests 100;
+ client_body_timeout 10;
+ reset_timedout_connection on;
+
+ # Max body size
+ client_max_body_size 4m;
+
+ # Cache
+ open_file_cache max=200000 inactive=20s;
+ open_file_cache_valid 30s;
+ open_file_cache_errors on;
+ open_file_cache_min_uses 2;
+
+ # Gzip
+ gzip on;
+ gzip_vary on;
+ gzip_disable "msie6";
+ gzip_proxied any;
+ gzip_comp_level 6;
+
+ # File types for compress via gzip
+ gzip_types text/plain text/css application/json application/x-javascript text/xml
+ application/xml application/xml+rss text/javascript application/javascript
+ image/svg+xml image/gif image/png image/jpeg image/x-icon image/webp;
+
+ # SSL
+ ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
+ ssl_stapling on;
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_session_cache shared:le_nginx_SSL:1m;
+ ssl_stapling_verify on;
+ ssl_session_timeout 1d;
+ ssl_session_tickets off;
+ ssl_prefer_server_ciphers off;
+
+ # Security headers
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header Referrer-Policy "no-referrer-when-downgrade" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
+
+ # Load configs
+ include /usr/share/nginx/modules/*.conf;
+ include /etc/nginx/conf.d/*.conf;
+ include /etc/nginx/sites-enabled/*;
+}
\ No newline at end of file
diff --git a/pkg/registry/roles/traefik/tasks/main.yml b/pkg/registry/roles/traefik/tasks/main.yml
new file mode 100755
index 0000000..590c7d5
--- /dev/null
+++ b/pkg/registry/roles/traefik/tasks/main.yml
@@ -0,0 +1,116 @@
+# Ansible role for deploy the Traefik Proxy for the Create Go App project.
+# Author: Vic Shóstak (https://shostak.dev)
+# For more information, please visit https://create-go.app/
+
+---
+#
+# Delete webserver files.
+#
+- name: Delete previous webserver files
+ file:
+ state: absent
+ path: "{{ server_dir }}/webserver"
+
+#
+# Create folder for Traefik.
+#
+- name: Ensures Traefik dir exists
+ file:
+ state: directory
+ path: "{{ server_dir }}/webserver"
+ owner: "{{ server_user }}"
+ group: "{{ server_group }}"
+
+#
+# Create file for ACME challenge.
+#
+- name: Ensures acme.json file exists
+ file:
+ state: touch
+ path: "{{ server_dir }}/webserver/acme.json"
+ mode: 0600
+
+#
+# Create Traefik config.
+#
+- name: Adding traefik.yml file
+ template:
+ src: traefik.yml.j2
+ dest: "{{ server_dir }}/webserver/traefik.yml"
+ mode: 0600
+
+#
+# Create Traefik log file.
+#
+- name: Ensures traefik.log file exists
+ file:
+ state: touch
+ path: "{{ server_dir }}/webserver/traefik.log"
+ mode: 0600
+
+#
+# Run official Traefik Docker container
+# WITHOUT dashboard (Traefik Web UI) and specified version.
+#
+- name: Run Traefik container without dashboard (Traefik Web UI)
+ docker_container:
+ name: cgapp-traefik
+ image: "traefik:{{ traefik_version }}"
+ restart_policy: unless-stopped
+ recreate: true
+ networks:
+ - name: "{{ docker_network }}"
+ ports:
+ - "80:80"
+ - "443:443"
+ volumes:
+ - "/var/run/docker.sock:/var/run/docker.sock"
+ - "{{ server_dir }}/webserver/traefik.yml:/etc/traefik/traefik.yml:ro"
+ - "{{ server_dir }}/webserver/acme.json:/acme.json:rw"
+ - "{{ server_dir }}/webserver/traefik.log:/traefik.log:rw"
+ #
+ # Don't forget to set environment variables, if your choose DNS way to getting SSL cert.
+ # See: https://doc.traefik.io/traefik/https/acme/#providers
+ #
+ # env:
+ # # For example, auth token for DigitalOcean DNS provider:
+ # DO_AUTH_TOKEN: "{{ do_auth_token }}"
+ #
+ when: (traefik_dashboard_url is not defined) or (traefik_dashboard_url|length == 0)
+
+#
+# Run official Traefik Docker container
+# WITH dashboard (Traefik Web UI) and specified version.
+#
+- name: Run Traefik container with dashboard (Traefik Web UI)
+ docker_container:
+ name: cgapp-traefik
+ image: "traefik:{{ traefik_version }}"
+ restart_policy: unless-stopped
+ recreate: true
+ networks:
+ - name: "{{ docker_network }}"
+ ports:
+ - "80:80"
+ - "443:443"
+ volumes:
+ - "/var/run/docker.sock:/var/run/docker.sock"
+ - "{{ server_dir }}/webserver/traefik.yml:/etc/traefik/traefik.yml:ro"
+ - "{{ server_dir }}/webserver/acme.json:/acme.json:rw"
+ - "{{ server_dir }}/webserver/traefik.log:/traefik.log:rw"
+ labels:
+ traefik.enable: "true"
+ traefik.http.routers.traefik.rule: "Host(`{{ traefik_dashboard_url }}`)"
+ traefik.http.routers.traefik.entrypoints: "websecure"
+ traefik.http.routers.traefik.service: "api@internal"
+ traefik.http.routers.traefik.middlewares: "auth"
+ traefik.http.middlewares.auth.basicauth.users: "{{ traefik_dashboard_user }}:{{ traefik_dashboard_password }}"
+ #
+ # Don't forget to set environment variables, if your choose DNS way to getting SSL cert.
+ # See: https://doc.traefik.io/traefik/https/acme/#providers
+ #
+ # env:
+ # # For example, auth token for DigitalOcean DNS provider:
+ # DO_AUTH_TOKEN: "{{ do_auth_token }}"
+ #
+ when: (traefik_dashboard_url is defined) or (traefik_dashboard_url|length > 0)
diff --git a/pkg/registry/roles/traefik/templates/traefik.yml.j2 b/pkg/registry/roles/traefik/templates/traefik.yml.j2
new file mode 100755
index 0000000..6579abc
--- /dev/null
+++ b/pkg/registry/roles/traefik/templates/traefik.yml.j2
@@ -0,0 +1,52 @@
+global:
+ checkNewVersion: false
+ sendAnonymousUsage: false
+
+log:
+ level: "{{ traefik_log_level }}"
+ format: "{{ traefik_log_format }}"
+ filePath: traefik.log
+{% if traefik_dashboard_url|length > 0 %}
+api:
+ dashboard: true
+{% endif %}
+entryPoints:
+ web:
+ address: :80
+ http:
+ redirections:
+ entryPoint:
+ to: websecure
+ scheme: https
+ websecure:
+ address: :443
+ http:
+ tls:
+ certResolver: le
+ domains:
+ - main: "{{ project_domain }}"
+{% if acme_dns_provider %}
+ sans:
+ - "*.{{ project_domain }}"
+{% endif %}
+
+certificatesResolvers:
+ le:
+ acme:
+ email: "{{ acme_email }}"
+ storage: acme.json
+{% if acme_dns_provider %}
+ dnsChallenge:
+ provider: "{{ acme_dns_provider }}"
+ delayBeforeCheck: 5
+ resolvers:
+ - 1.1.1.1:53
+ - 8.8.8.8:53
+{% else %}
+ caServer: https://acme-{% if acme_staging == 'yes' %}staging-{% endif %}v02.api.letsencrypt.org/directory
+{% endif %}
+
+providers:
+ docker:
+ network: "{{ docker_network }}"
+ exposedByDefault: false
diff --git a/pkg/registry/templates/hosts.ini.tmpl b/pkg/registry/templates/hosts.ini.tmpl
new file mode 100644
index 0000000..47d494e
--- /dev/null
+++ b/pkg/registry/templates/hosts.ini.tmpl
@@ -0,0 +1,103 @@
+# Ansible inventory for deploy the Create Go App project.
+# Author: Vic Shóstak (https://shostak.dev)
+# For more information, please visit https://create-go.app/
+
+[cgapp_project]
+127.0.0.1 # CHANGE THIS TO YOUR REMOTE SERVER IP!
+
+[cgapp_project:vars]
+#
+# Ansible default variables to start playbook:
+#
+
+# Set remote sudo username
+ansible_user=root
+# Ask become password for remote sudo user
+ansible_become=yes
+# Set connection type to remote server (usually, 'ssh')
+ansible_connection=ssh
+# Set Python 3 default path
+ansible_python_interpreter=/usr/bin/python3
+
+#
+# Docker configuration:
+#
+
+# Set Docker network name
+docker_network=cgapp_network
+
+#
+# Remote server configuration:
+#
+
+# Set directory on your remote server for store project files
+server_dir=/var/www/cgapp
+# Set user (owner of files/folders) name
+server_user=root
+# Set group name
+server_group=docker
+
+#
+# Project configuration:
+#
+
+# Set your project domain
+project_domain=example.com
+
+#
+# Backend configuration:
+#
+
+# Set backend port number,
+# MUST BE MATCH to the port that is listed in your `./backend/.env` file!
+backend_port=5000
+
+{{ if eq .Proxy "traefik" }}#
+# Traefik Proxy configuration:
+#
+
+# Set Traefik version (for example, v2.4)
+traefik_version=latest
+# Set log level in capital letters (DEBUG, PANIC, FATAL, ERROR, WARN, INFO)
+traefik_log_level=ERROR
+# Set log format between json and common
+traefik_log_format=json
+# Set URL for Traefik admin dashboard,
+# if you DO NOT NEED dashboard (Web UI), just leave it blank
+traefik_dashboard_url=cp.example.com
+# Set username for Traefik admin dashboard,
+# DO NOT FORGET to set this setting, if `traefik_dashboard_url` variable IS NOT empty!
+traefik_dashboard_user=admin
+# Set hashed password for Traefik admin dashboard,
+# DO NOT FORGET to set this setting, if `traefik_dashboard_url` variable IS NOT empty!
+# See: https://doc.traefik.io/traefik/middlewares/basicauth/#users
+traefik_dashboard_password=admin:$$apr1$$WpxRpfMZ$$TMTfGB37C9xAHiPIDiFiB1
+
+#
+# ACME configuration (Let's Encrypt):
+#
+
+# Set your email address for SSL cert
+acme_email=mail@example.com
+{{ if .Wildcard }}
+# You choose a DNS way to getting SSL cert,
+# DO NOT FORGET to set this variable here as well as in `./roles/traefik/tasks/main.yml` file!
+
+# Set DNS provider
+# See: https://doc.traefik.io/traefik/https/acme/#providers
+acme_dns_provider=digitalocean
+# Set environment variable(s) for a DNS provider,
+# for example, auth token for DigitalOcean
+do_auth_token=XXXXXXXXXXX
+{{ else }}# Change to 'no' for getting a real SSL cert
+acme_staging=yes{{ end }}
+{{ else if eq .Proxy "nginx" }}#
+# Nginx configuration:
+#
+
+# Set Nginx version (for example, 1.20-alpine)
+nginx_version=alpine
+# Set redirect from HTTP to HTTPS for default server
+nginx_use_only_https=yes
+# Set redirect from WWW to non-WWW domain for default server
+nginx_redirect_to_non_www=yes{{ end }}
\ No newline at end of file
diff --git a/pkg/registry/templates/playbook.yml.tmpl b/pkg/registry/templates/playbook.yml.tmpl
new file mode 100755
index 0000000..20b3889
--- /dev/null
+++ b/pkg/registry/templates/playbook.yml.tmpl
@@ -0,0 +1,18 @@
+# Ansible playbook for deploy the Create Go App project.
+# Author: Vic Shóstak (https://shostak.dev)
+# For more information, please visit https://create-go.app/
+
+---
+#
+# Playbook for deploy.
+#
+- name: Deploy the Create Go App project
+ hosts: cgapp_project
+
+ #
+ # List of all roles.
+ #
+ roles:
+ - { role: docker, tags: [docker] }
+ - { role: {{.Proxy}}, tags: [{{.Proxy}}] }
+ - { role: backend, tags: [backend] }
\ No newline at end of file