143 | description: |
144 | Create a new production-ready project with backend (Golang), frontend (JavaScript, TypeScript) and deploy automation (Ansible, Docker) by running one CLI command.
145 | Focus on writing code and thinking of business-logic! The CLI will take care of the rest.
146 | homepage: "https://github.com/create-go-app/cli"
147 | license: Apache 2.0
148 | formats: [deb, rpm, apk, archlinux]
149 | dependencies: [git, npm]
150 |
151 | archives:
152 | - format_overrides:
153 | - goos: windows
154 | format: zip
155 | files: [LICENSE, README.md]
156 |
157 | checksum:
158 | name_template: "checksums.txt"
159 |
160 | changelog:
161 | # use: github
162 | sort: asc
163 | abbrev: -1
164 | filters:
165 | exclude: ["^*.md", "^*.ya?ml"]
166 | groups:
167 | - title: Features
168 | regexp: ^.*?(F|f)eature.*?$
169 | order: 0
170 | - title: Bug fixes
171 | regexp: ^.*?((B|b)ug)|((F|f)ix).*?$
172 | order: 1
173 | - title: Improvements
174 | regexp: ^.*?(I|i)mprove.*?$
175 | order: 2
176 | - title: Updates
177 | regexp: ^.*?(U|u)pdate.*?$
178 | order: 3
179 | - title: Security issues
180 | regexp: ^.*?(S|s)ecurity.*?$
181 | order: 4
182 | - title: Delete unused or stale
183 | regexp: ^.*?((D|d)elete)|((U|u)nused)|((S|s)tale).*?$
184 | order: 5
185 | - title: Others
186 | order: 999
187 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM scratch
2 | COPY cgapp /
3 | ENTRYPOINT ["/cgapp"]
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2024 Vic Shóstak and Create Go App Contributors
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: clean security critic test install build release build-and-push-images delete-tag update-pkg-cache
2 |
3 | clean:
4 | rm -rf ./tmp ./tests
5 |
6 | security:
7 | go run github.com/securego/gosec/v2/cmd/gosec@latest -quiet ./...
8 |
9 | critic:
10 | go run github.com/go-critic/go-critic/cmd/gocritic@latest check -enableAll ./...
11 |
12 | test: clean security critic
13 | mkdir ./tests
14 | go test -coverprofile=./tests/coverage.out ./...
15 | go tool cover -func=./tests/coverage.out
16 | rm -rf ./tests
17 |
18 | install: test
19 | CGO_ENABLED=0 go build -ldflags="-s -w" -o $(GOPATH)/bin/cgapp ./cmd/cgapp/main.go
20 |
21 | build: test
22 | goreleaser --snapshot
23 |
24 | release: test
25 | git tag -a v$(VERSION) -m "$(VERSION)"
26 | goreleaser --snapshot
27 |
28 | build-and-push-images: test
29 | docker build -t docker.io/koddr/cgapp:latest .
30 | docker push docker.io/koddr/cgapp:latest
31 | docker build -t docker.io/koddr/cgapp:$(VERSION) .
32 | docker push docker.io/koddr/cgapp:$(VERSION)
33 | docker image rm docker.io/koddr/cgapp:$(VERSION)
34 |
35 | update-pkg-cache:
36 | curl -i https://proxy.golang.org/github.com/create-go-app/cli/v4/@v/v$(VERSION).info
37 |
38 | delete-tag:
39 | git tag --delete v$(VERSION)
40 | docker image rm docker.io/koddr/cgapp:latest
41 | docker image rm docker.io/koddr/cgapp:$(VERSION)
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [![Create Go App][repo_logo_img]][repo_url]
4 |
5 | # Create Go App CLI
6 |
7 | [![Go version][go_version_img]][go_dev_url]
8 | [![Go report][go_report_img]][go_report_url]
9 | [![Code coverage][go_code_coverage_img]][repo_url]
10 | [![Wiki][repo_wiki_img]][repo_wiki_url]
11 | [![License][repo_license_img]][repo_license_url]
12 |
13 | Create a new production-ready project with **backend** (Golang),
14 | **frontend** (JavaScript, TypeScript) and **deploy automation** (Ansible, Docker) by running only one command. Focus on **writing your code** and **thinking of the business-logic**! The CLI will take care of the rest.
15 |
16 |
17 |
18 | ## ⚡️ Quick start
19 |
20 | First, [download][go_download_url] and install **Go**. Version `1.21` or
21 | higher is required.
22 |
23 | > 👆 You can also use **Create Go App CLI** via other Go
24 | > versions: [`1.16`][repo_v2_url], [`1.17`][repo_v3_url].
25 |
26 | Installation is done by using the [`go install`][go_install_url] command:
27 |
28 | ```bash
29 | go install github.com/create-go-app/cli/v4/cmd/cgapp@latest
30 | ```
31 |
32 | Or see the repository's [Release page][repo_releases_url], if you want to
33 | download a ready-made `deb`, `rpm`, `apk` or `Arch Linux` package.
34 |
35 | Also, GNU/Linux and macOS users available way to install via
36 | [Homebrew][brew_url]:
37 |
38 | ```bash
39 | # Tap a new formula:
40 | brew tap create-go-app/tap
41 |
42 | # Installation:
43 | brew install create-go-app/tap/cgapp
44 | ```
45 |
46 | Let's create a new project via **interactive console UI** (or **CUI** for
47 | short) in current folder:
48 |
49 | ```bash
50 | cgapp create
51 | ```
52 |
53 | Next, open the generated Ansible inventory file (called `hosts.ini`) and
54 | fill in the variables according to your server configuration. And you're
55 | ready to **automatically deploy** this project:
56 |
57 | ```bash
58 | cgapp deploy
59 | ```
60 |
61 | That's all you need to know to start! 🎉
62 |
63 | ### 🐳 Docker-way to quick start
64 |
65 | If you don't want to install Create Go App CLI to your system, you feel free
66 | to using our official [Docker image][docker_url] and run CLI from isolated
67 | container:
68 |
69 | ```bash
70 | docker run --rm -it -v ${PWD}:${PWD} -w ${PWD} koddr/cgapp:latest [COMMAND]
71 | ```
72 |
73 | > 🔔 Please note: the `deploy` command is currently **unavailable** in this
74 | > image.
75 |
76 | ## 📖 Project Wiki
77 |
78 | The best way to better explore all the features of the **Create Go App CLI**
79 | is to read the project [Wiki][repo_wiki_url] and take part in
80 | [Discussions][repo_discussions_url] and/or [Issues][repo_issues_url].
81 |
82 | Yes, the most frequently asked questions (FAQ) are also
83 | [here][repo_wiki_faq_url].
84 |
85 | ## ⚙️ Commands & Options
86 |
87 | ### `create`
88 |
89 | CLI command for create a new project with the interactive console UI.
90 |
91 | ```bash
92 | cgapp create [OPTION]
93 | ```
94 |
95 | | Option | Description | Type | Default | Required? |
96 | | ------ | -------------------------------------------------------- | ------ | ------- | --------- |
97 | | `-t` | Enables to define custom backend and frontend templates. | `bool` | `false` | No |
98 |
99 | ![cgapp_create][cgapp_create_gif]
100 |
101 | - 📺 Full demo video: https://recordit.co/OQAwkZBrjN
102 | - 📖 Docs: https://github.com/create-go-app/cli/wiki/Command-create
103 |
104 | ### `deploy`
105 |
106 | CLI command for deploy Docker containers with your project via Ansible to
107 | the remote server.
108 |
109 | > 🔔 Make sure that you have [Python 3.8+][python_url] and
110 | > [Ansible 2.9+][ansible_url] installed on your computer.
111 |
112 | ```bash
113 | cgapp deploy [OPTION]
114 | ```
115 |
116 | | Option | Description | Type | Default | Required? |
117 | | ------ | ------------------------------------------------------------------------------------------------------ | ------ | ------- | --------- |
118 | | `-k` | Prompt you to provide the remote user sudo password (_a standard Ansible `--ask-become-pass` option_). | `bool` | `false` | No |
119 |
120 | ![cgapp_deploy][cgapp_deploy_gif]
121 |
122 | - 📺 Full demo video: https://recordit.co/ishTf0Au1x
123 | - 📖 Docs: https://github.com/create-go-app/cli/wiki/Command-deploy
124 |
125 | ## 📝 Production-ready project templates
126 |
127 | ### Backend
128 |
129 | - Backend template with Golang built-in [net/http][net_http_url] package:
130 | - [`net/http`][cgapp_net-http-template_url] — simple REST API with CRUD
131 | and JWT auth.
132 | - Backend template with [Fiber][fiber_url]:
133 | - [`fiber`][cgapp_fiber-template_url] — complex REST API with CRUD, JWT auth
134 | with renew token, DB and cache.
135 | - Backend template with [go-chi][chi_url]:
136 | - [`chi`][cgapp_chi-template_url] — a basic application with health check.
137 |
138 | ### Frontend
139 |
140 | Frontend part will be generated using awesome tool [Vite.js][vitejs_url]
141 | under the hood.
142 |
143 | So, you'll always get the latest version of React, Preact, Vue, Svelte,
144 | Solid, Lit, Qwik, or pure JavaScript/TypeScript templates for your project.
145 |
146 | | Name | Description | JavaScript | Typescript |
147 | | --------------------------------- | -------------------------------- | ----------- | -------------- |
148 | | Pure | A pure JavaScript/Typescript app | `vanilla` | `vanilla-ts` |
149 | | [React][react_url] | A common React app | `react` | `react-ts` |
150 | | [React (with SWC)][react_swc_url] | A React app with SWC | `react-swc` | `react-swc-ts` |
151 | | [Preact][preact_url] | A common Preact app | `preact` | `preact-ts` |
152 | | [Vue.js][vuejs_url] | A common Vue.js app | `vue` | `vue-ts` |
153 | | [Svelte][svelte_url] | A common Svelte app | `svelte` | `svelte-ts` |
154 | | [Solid][solid_url] | A common Solid app | `solid` | `solid-ts` |
155 | | [Lit][lit_url] | A common Lit app | `lit` | `lit-ts` |
156 | | [Qwik][qwik_url] | A common Qwik app | `qwik` | `qwik-ts` |
157 |
158 | The `Next.js` and `Nuxt` frontend parts will be generated using the latest
159 | `create-next-app` and `nuxi` utilities.
160 |
161 | | Name | Description | JavaScript | Typescript |
162 | | -------------------------- | ---------------------- | ---------- | ----------- |
163 | | [Next.js][nextjs_url] | A common Next.js app | `next` | `next-ts` |
164 | | [Nuxt][nuxt_url] | A common Nuxt v3 app | - | `nuxt` |
165 | | [Sveltekit][sveltekit_url] | A common Sveltekit app | - | `sveltekit` |
166 |
167 | > ❗️ Please make sure that you have `npm` version `7` or higher installed to
168 | > create the frontend part of the project correctly. If you run the
169 | > `cgapp -create` command using our [Docker image][docker_url], `npm` of the
170 | > correct version is **already** included.
171 |
172 | ## 🚚 Pre-configured Ansible roles
173 |
174 | ### Web/Proxy server
175 |
176 | - Roles for run Docker container with [Traefik Proxy][traefik_url]:
177 | - `traefik` — configured Traefik container with a simple ACME challenge
178 | via CA server.
179 | - `traefik-acme-dns` — configured Traefik container with a complex ACME
180 | challenge via DNS provider.
181 | - Roles for run Docker container with [Nginx][nginx_url]:
182 | - `nginx` — pure Nginx container with "the best practice" configuration.
183 |
184 | > ✌️ Since Create Go App CLI `v2.0.0`, we're recommended to use **Traefik
185 | > Proxy** as default proxy server for your projects. The main reason: this
186 | > proxy provides _automatic_ SSL certificates from Let's Encrypt out of the
187 | > box. Also, Traefik was built on the Docker ecosystem and has a _really
188 | > good-looking_ and _useful_ Web UI.
189 |
190 | ### Database
191 |
192 | - Roles for run Docker container with [PostgreSQL][postgresql_url]:
193 | - `postgres` — configured PostgreSQL container with apply migrations for
194 | backend.
195 |
196 | ### Cache (key-value storage)
197 |
198 | - Roles for run Docker container with [Redis][redis_url]:
199 | - `redis` — configured Redis container for backend.
200 |
201 | ## ⭐️ Project assistance
202 |
203 | If you want to say **thank you** or/and support active development of
204 | `Create Go App CLI`:
205 |
206 | - Add a [GitHub Star][repo_url] to the project.
207 | - Write interesting articles about project on [Dev.to][dev_to_url], or
208 | personal blog.
209 | - Leave a review on our [ProductHunt][cgapp_product-hunt_url] page.
210 |
211 | ## ❗️ Support the author
212 |
213 | You can support the author on [Boosty][boosty_url], both on a _permanent_ and on a _one-time_ basis.
214 |
215 | All proceeds from this way will go to **support** my OSS projects and will energize me to **create** new products and articles for the community.
216 |
217 |
218 |
219 | ## 🏆 A win-win cooperation
220 |
221 | And now, I invite you to participate in this project! Let's work **together** to
222 | create the **most useful** tool for developers on the web today.
223 |
224 | - [Issues][repo_issues_url]: ask questions and submit your features.
225 | - [Pull requests][repo_pull_request_url]: send your improvements to the current.
226 |
227 | Together, we can make this project **better** every day! 😘
228 |
229 | ## 🔥 Other projects from the author
230 |
231 |
232 |
233 | ## ⚠️ License
234 |
235 | [`Create Go App CLI`][repo_url] is free and open-source software licensed under
236 | the [Apache 2.0 License][repo_license_url]. Official [logo][repo_logo_url] was
237 | created by [Vic Shóstak][author] and distributed under
238 | [Creative Commons][repo_cc_url] license (CC BY-SA 4.0 International).
239 |
240 |
241 |
242 | [go_download_url]: https://golang.org/dl/
243 | [go_install_url]: https://golang.org/cmd/go/#hdr-Compile_and_install_packages_and_dependencies
244 | [go_version_img]: https://img.shields.io/badge/Go-1.21+-00ADD8?style=for-the-badge&logo=go
245 | [go_report_img]: https://img.shields.io/badge/Go_report-A+-success?style=for-the-badge&logo=none
246 | [go_report_url]: https://goreportcard.com/report/github.com/create-go-app/cli
247 | [go_code_coverage_img]: https://img.shields.io/badge/code_coverage-88%25-success?style=for-the-badge&logo=none
248 | [go_dev_url]: https://pkg.go.dev/github.com/create-go-app/cli/v4
249 |
250 |
251 |
252 | [repo_url]: https://github.com/create-go-app/cli
253 | [repo_logo_url]: https://github.com/create-go-app/cli/wiki/Logo
254 | [repo_logo_img]: https://github.com/create-go-app/cli/assets/11155743/95024afc-5e3b-4d6f-8c9c-5daaa51d080d
255 | [repo_license_url]: https://github.com/create-go-app/cli/blob/main/LICENSE
256 | [repo_license_img]: https://img.shields.io/badge/license-Apache_2.0-red?style=for-the-badge&logo=none
257 | [repo_cc_url]: https://creativecommons.org/licenses/by-sa/4.0/
258 | [repo_v2_url]: https://github.com/create-go-app/cli/tree/v2
259 | [repo_v3_url]: https://github.com/create-go-app/cli/tree/v3
260 | [repo_issues_url]: https://github.com/create-go-app/cli/issues
261 | [repo_pull_request_url]: https://github.com/create-go-app/cli/pulls
262 | [repo_discussions_url]: https://github.com/create-go-app/cli/discussions
263 | [repo_releases_url]: https://github.com/create-go-app/cli/releases
264 | [repo_wiki_url]: https://github.com/create-go-app/cli/wiki
265 | [repo_wiki_img]: https://img.shields.io/badge/docs-wiki_page-blue?style=for-the-badge&logo=none
266 | [repo_wiki_faq_url]: https://github.com/create-go-app/cli/wiki/FAQ
267 |
268 |
269 |
270 | [cgapp_deploy_gif]: https://user-images.githubusercontent.com/11155743/116796941-3c421e00-aae9-11eb-9575-d72550814d7a.gif
271 | [cgapp_create_gif]: https://user-images.githubusercontent.com/11155743/116796937-38160080-aae9-11eb-8e21-fb1be2750aa4.gif
272 | [cgapp_product-hunt_url]: https://www.producthunt.com/posts/create-go-app?utm_source=badge-review&utm_medium=badge&utm_souce=badge-create-go-app#discussion-body
273 | [cgapp_product-hunt_img]: https://api.producthunt.com/widgets/embed-image/v1/review.svg?post_id=316086&theme=light
274 | [cgapp_chi-template_url]: https://github.com/create-go-app/chi-go-template
275 | [cgapp_fiber-template_url]: https://github.com/create-go-app/fiber-go-template
276 | [cgapp_net-http-template_url]: https://github.com/create-go-app/net_http-go-template
277 |
278 |
279 |
280 | [author]: https://github.com/koddr
281 | [author_do_ref_url]: https://m.do.co/c/b41859fa9b6e
282 |
283 |
284 |
285 | [twitter_url]: https://twitter.com/intent/tweet?text=Wow%21%20%F0%9F%8E%89%20Create%20a%20new%20production-ready%20project%20with%20backend%20%28Golang%29%2C%20frontend%20%28JavaScript%2C%20TypeScript%29%0Aand%20deploy%20automation%20%28Ansible%2C%20Docker%29%20by%20running%20one%20CLI%20command%20%F0%9F%91%89%20https%3A%2F%2Fgithub.com%2Fcreate-go-app%2Fcli
286 | [dev_to_url]: https://dev.to/
287 | [redis_url]: https://redis.io/
288 | [postgresql_url]: https://postgresql.org/
289 | [nginx_url]: https://nginx.org/
290 | [traefik_url]: https://traefik.io/traefik/
291 | [vitejs_url]: https://vitejs.dev/
292 | [vuejs_url]: https://vuejs.org/
293 | [react_url]: https://reactjs.org/
294 | [preact_url]: https://preactjs.com/
295 | [nextjs_url]: https://nextjs.org/
296 | [nuxt_url]: https://v3.nuxtjs.org/
297 | [sveltekit_url]: https://kit.svelte.dev/
298 | [svelte_url]: https://svelte.dev/
299 | [lit_url]: https://lit.dev/
300 | [chi_url]: https://github.com/go-chi/chi
301 | [fiber_url]: https://github.com/gofiber/fiber
302 | [net_http_url]: https://golang.org/pkg/net/http/
303 | [docker_url]: https://hub.docker.com/r/koddr/cgapp
304 | [python_url]: https://www.python.org/downloads/
305 | [ansible_url]: https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-specific-operating-systems
306 | [brew_url]: https://brew.sh/
307 | [qwik_url]: https://github.com/BuilderIO/qwik
308 | [solid_url]: https://github.com/solidjs/solid
309 | [react_swc_url]: https://swc.rs/
310 |
311 |
312 |
313 | [gowebly_url]: https://github.com/gowebly/gowebly
314 | [gowebly_img_url]: https://github.com/gowebly/.github/blob/a8ae889ed2c5baefcd307ad4505161f98f75669f/images/gowebly-banner-logo.png
315 | [boosty_url]: https://boosty.to/koddr
316 |
--------------------------------------------------------------------------------
/cmd/cgapp/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package main includes call for the Create Go App CLI.
3 |
4 | Create a new production-ready project with backend (Golang),
5 | frontend (JavaScript, TypeScript) and deploy automation
6 | (Ansible, Docker) by running one CLI command.
7 |
8 | Focus on writing your code and thinking of the business logic!
9 | The Create Go App CLI will take care of the rest.
10 |
11 | A helpful documentation and next steps -> https://github.com/create-go-app/cli
12 |
13 | # Copyright 2023 Vic Shóstak and Create Go App Contributors
14 |
15 | Licensed under the Apache License, Version 2.0 (the "License");
16 | you may not use this file except in compliance with the License.
17 | You may obtain a copy of the License at
18 |
19 | http://www.apache.org/licenses/LICENSE-2.0
20 |
21 | Unless required by applicable law or agreed to in writing, software
22 | distributed under the License is distributed on an "AS IS" BASIS,
23 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 | See the License for the specific language governing permissions and
25 | limitations under the License.
26 | */
27 | package main
28 |
29 | import "github.com/create-go-app/cli/v4/cmd"
30 |
31 | func main() {
32 | cmd.Execute()
33 | }
34 |
--------------------------------------------------------------------------------
/cmd/create.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Vic Shóstak and Create Go App Contributors. All rights reserved.
2 | // Use of this source code is governed by Apache 2.0 license
3 | // that can be found in the LICENSE file.
4 |
5 | package cmd
6 |
7 | import (
8 | "fmt"
9 | "strings"
10 | "time"
11 |
12 | "github.com/AlecAivazis/survey/v2"
13 | "github.com/spf13/cobra"
14 |
15 | "github.com/create-go-app/cli/v4/pkg/cgapp"
16 | "github.com/create-go-app/cli/v4/pkg/registry"
17 | )
18 |
19 | func init() {
20 | rootCmd.AddCommand(createCmd)
21 | createCmd.Flags().BoolVarP(
22 | &useCustomTemplate,
23 | "template", "t", false,
24 | "enables to use custom backend and frontend templates",
25 | )
26 | }
27 |
28 | // createCmd represents the `create` command.
29 | var createCmd = &cobra.Command{
30 | Use: "create",
31 | Aliases: []string{"new"},
32 | Short: "Create a new project via interactive UI",
33 | Long: "\nCreate a new project via interactive UI.",
34 | RunE: runCreateCmd,
35 | }
36 |
37 | // runCreateCmd represents runner for the `create` command.
38 | func runCreateCmd(cmd *cobra.Command, args []string) error {
39 | // Start message.
40 | cgapp.ShowMessage(
41 | "",
42 | fmt.Sprintf(
43 | "Create a new project via Create Go App CLI v%v...",
44 | registry.CLIVersion,
45 | ),
46 | true, true,
47 | )
48 |
49 | // Start survey.
50 | if useCustomTemplate {
51 | // Custom survey.
52 | if err := survey.Ask(
53 | registry.CustomCreateQuestions,
54 | &customCreateAnswers,
55 | survey.WithIcons(surveyIconsConfig),
56 | ); err != nil {
57 | return cgapp.ShowError(err.Error())
58 | }
59 |
60 | // Define variables for better display.
61 | backend = customCreateAnswers.Backend
62 | frontend = customCreateAnswers.Frontend
63 | proxy = customCreateAnswers.Proxy
64 | } else {
65 | // Default survey.
66 | if err := survey.Ask(
67 | registry.CreateQuestions,
68 | &createAnswers,
69 | survey.WithIcons(surveyIconsConfig),
70 | ); err != nil {
71 | return cgapp.ShowError(err.Error())
72 | }
73 |
74 | // Define variables for better display.
75 | backend = fmt.Sprintf(
76 | "github.com/create-go-app/%v-go-template",
77 | strings.ReplaceAll(createAnswers.Backend, "/", "_"),
78 | )
79 | frontend = createAnswers.Frontend
80 | proxy = createAnswers.Proxy
81 | }
82 |
83 | // Catch the cancel action (hit "n" in the last question).
84 | if (!createAnswers.AgreeCreation && !useCustomTemplate) || (!customCreateAnswers.AgreeCreation && useCustomTemplate) {
85 | cgapp.ShowMessage(
86 | "",
87 | "Oh no! You said \"no\", so I won't create anything. Hope to see you soon!",
88 | true, true,
89 | )
90 | return nil
91 | }
92 |
93 | // Start timer.
94 | startTimer := time.Now()
95 |
96 | /*
97 | The project's backend part creation.
98 | */
99 |
100 | // Clone backend files from git repository.
101 | if err := cgapp.GitClone("backend", backend); err != nil {
102 | return cgapp.ShowError(err.Error())
103 | }
104 |
105 | // Show success report.
106 | cgapp.ShowMessage(
107 | "success",
108 | fmt.Sprintf("Backend was created with template `%v`!", backend),
109 | true, false,
110 | )
111 |
112 | /*
113 | The project's frontend part creation.
114 | */
115 |
116 | if frontend != "none" {
117 | // Checking, if you use custom templates.
118 | if useCustomTemplate {
119 | // Clone frontend files from git repository.
120 | if err := cgapp.GitClone("frontend", frontend); err != nil {
121 | return cgapp.ShowError(err.Error())
122 | }
123 | } else {
124 | switch frontend {
125 | case "next":
126 | // Create a default frontend template with Next.js (React).
127 | if err := cgapp.ExecCommand(
128 | "npx",
129 | []string{
130 | "create-next-app@latest", "frontend",
131 | "--javascript",
132 | "--eslint",
133 | "--app",
134 | "--tailwind", "false",
135 | "--src-dir", "false",
136 | "--import-alias", "false",
137 | }, true,
138 | ); err != nil {
139 | return err
140 | }
141 | case "next-ts":
142 | // Create a default frontend template with Next.js (React, Typescript).
143 | if err := cgapp.ExecCommand(
144 | "npx",
145 | []string{
146 | "create-next-app@latest", "frontend",
147 | "--typescript",
148 | "--eslint",
149 | "--app",
150 | "--tailwind", "false",
151 | "--src-dir", "false",
152 | "--import-alias", "false",
153 | }, true,
154 | ); err != nil {
155 | return err
156 | }
157 | case "nuxt":
158 | // Create a default frontend template with Nuxt v3 (Vue.js v3, Typescript).
159 | if err := cgapp.ExecCommand(
160 | "npx",
161 | []string{
162 | "nuxi@latest", "init", "frontend",
163 | }, true,
164 | ); err != nil {
165 | return err
166 | }
167 | case "sveltekit":
168 | // Create a default frontend template with Sveltekit (Svelte, Typescript).
169 | if err := cgapp.ExecCommand(
170 | "npm",
171 | []string{
172 | "create", "@svelte-add/kit@latest", "frontend",
173 | "--",
174 | "--with", "typescript+eslint+prettier",
175 | }, true,
176 | ); err != nil {
177 | return err
178 | }
179 | default:
180 | // Create a default frontend template from Vite (Pure JS/TS, React, Preact, Vue, Svelte, Lit).
181 | if err := cgapp.ExecCommand(
182 | "npm",
183 | []string{
184 | "create", "vite@latest", "frontend",
185 | "--",
186 | "--template",
187 | frontend,
188 | }, true,
189 | ); err != nil {
190 | return err
191 | }
192 | }
193 | }
194 |
195 | // Show success report.
196 | cgapp.ShowMessage(
197 | "success",
198 | fmt.Sprintf("Frontend was created with template `%v`!", frontend),
199 | false, false,
200 | )
201 | }
202 |
203 | /*
204 | The project's webserver part creation.
205 | */
206 |
207 | // Copy Ansible playbook, inventory and roles from embedded file system.
208 | if err := cgapp.CopyFromEmbeddedFS(
209 | &cgapp.EmbeddedFileSystem{
210 | Name: registry.EmbedTemplates,
211 | RootFolder: "templates",
212 | SkipDir: true,
213 | },
214 | ); err != nil {
215 | return cgapp.ShowError(err.Error())
216 | }
217 |
218 | // Set template variables for Ansible playbook and inventory files.
219 | inventory = registry.AnsibleInventoryVariables[proxy].List
220 | playbook = registry.AnsiblePlaybookVariables[proxy].List
221 |
222 | // Generate Ansible inventory file.
223 | if err := cgapp.GenerateFileFromTemplate("hosts.ini.tmpl", inventory); err != nil {
224 | return cgapp.ShowError(err.Error())
225 | }
226 |
227 | // Generate Ansible playbook file.
228 | if err := cgapp.GenerateFileFromTemplate("playbook.yml.tmpl", playbook); err != nil {
229 | return cgapp.ShowError(err.Error())
230 | }
231 |
232 | // Show success report.
233 | if proxy != "none" {
234 | cgapp.ShowMessage(
235 | "success",
236 | fmt.Sprintf("Web/Proxy server configuration for `%v` was created!", proxy),
237 | false, false,
238 | )
239 | }
240 |
241 | /*
242 | The project's Ansible roles part creation.
243 | */
244 |
245 | // Copy Ansible roles from embedded file system.
246 | if err := cgapp.CopyFromEmbeddedFS(
247 | &cgapp.EmbeddedFileSystem{
248 | Name: registry.EmbedRoles,
249 | RootFolder: "roles",
250 | SkipDir: false,
251 | },
252 | ); err != nil {
253 | return cgapp.ShowError(err.Error())
254 | }
255 |
256 | // Show success report.
257 | cgapp.ShowMessage(
258 | "success",
259 | "Ansible inventory, playbook and roles for deploying your project was created!",
260 | false, false,
261 | )
262 |
263 | /*
264 | The project's misc files part creation.
265 | */
266 |
267 | // Copy from embedded file system.
268 | if err := cgapp.CopyFromEmbeddedFS(
269 | &cgapp.EmbeddedFileSystem{
270 | Name: registry.EmbedMiscFiles,
271 | RootFolder: "misc",
272 | SkipDir: true,
273 | },
274 | ); err != nil {
275 | return cgapp.ShowError(err.Error())
276 | }
277 |
278 | /*
279 | Cleanup project.
280 | */
281 |
282 | // Set unused proxy roles.
283 | switch proxy {
284 | case "traefik", "traefik-acme-dns":
285 | proxyList = []string{"nginx"}
286 | case "nginx":
287 | proxyList = []string{"traefik"}
288 | default:
289 | proxyList = []string{"traefik", "nginx"}
290 | }
291 |
292 | // Delete unused roles, backend and frontend files.
293 | cgapp.RemoveFolders("roles", proxyList)
294 | cgapp.RemoveFolders("backend", []string{".git", ".github"})
295 | cgapp.RemoveFolders("frontend", []string{".git", ".github"})
296 |
297 | // Stop timer.
298 | stopTimer := cgapp.CalculateDurationTime(startTimer)
299 | cgapp.ShowMessage(
300 | "info",
301 | fmt.Sprintf("Completed in %v seconds!", stopTimer),
302 | true, true,
303 | )
304 |
305 | // Ending messages.
306 | cgapp.ShowMessage(
307 | "",
308 | "* Please put credentials into the Ansible inventory file (`hosts.ini`) before you start deploying a project!",
309 | false, false,
310 | )
311 | if !useCustomTemplate && frontend != "none" {
312 | cgapp.ShowMessage(
313 | "",
314 | fmt.Sprintf("* Visit https://vitejs.dev/guide/ for more info about using the `%v` frontend template!", frontend),
315 | false, false,
316 | )
317 | }
318 | cgapp.ShowMessage(
319 | "",
320 | "* A helpful documentation and next steps with your project is here https://github.com/create-go-app/cli/wiki",
321 | false, true,
322 | )
323 | cgapp.ShowMessage(
324 | "",
325 | "Have a happy new project! :)",
326 | false, true,
327 | )
328 |
329 | return nil
330 | }
331 |
--------------------------------------------------------------------------------
/cmd/deploy.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Vic Shóstak and Create Go App Contributors. All rights reserved.
2 | // Use of this source code is governed by Apache 2.0 license
3 | // that can be found in the LICENSE file.
4 |
5 | package cmd
6 |
7 | import (
8 | "fmt"
9 | "time"
10 |
11 | "github.com/spf13/cobra"
12 |
13 | "github.com/create-go-app/cli/v4/pkg/cgapp"
14 | "github.com/create-go-app/cli/v4/pkg/registry"
15 | )
16 |
17 | func init() {
18 | rootCmd.AddCommand(deployCmd)
19 | deployCmd.Flags().BoolVarP(
20 | &askBecomePass,
21 | "ask-become-pass", "k", false,
22 | "prompt you to provide the remote user sudo password (standard Ansible `--ask-become-pass` option)",
23 | )
24 | }
25 |
26 | // deployCmd represents the `deploy` command.
27 | var deployCmd = &cobra.Command{
28 | Use: "deploy",
29 | Aliases: []string{"push"},
30 | Short: "Deploy your project to the remote server via Ansible",
31 | Long: "\nDeploy your project to the remote server by Ansible playbooks and roles.",
32 | RunE: runDeployCmd,
33 | }
34 |
35 | // runDeployCmd represents runner for the `deploy` command.
36 | func runDeployCmd(cmd *cobra.Command, args []string) error {
37 | // Start message.
38 | cgapp.ShowMessage(
39 | "",
40 | fmt.Sprintf("Deploying project via Create Go App CLI v%v...", registry.CLIVersion),
41 | true, true,
42 | )
43 |
44 | // Set Ansible playbook and inventory files.
45 | if askBecomePass {
46 | // With entering password.
47 | options = []string{"playbook.yml", "-i", "hosts.ini", "-K"}
48 | } else {
49 | // Without entering password.
50 | options = []string{"playbook.yml", "-i", "hosts.ini"}
51 | }
52 |
53 | // Create config files for your project.
54 | cgapp.ShowMessage(
55 | "info",
56 | "Ansible playbook for deploy your project is running. Please wait for completion!",
57 | false, false,
58 | )
59 |
60 | // Start timer.
61 | startTimer := time.Now()
62 |
63 | // Run execution for Ansible playbook.
64 | if err := cgapp.ExecCommand("ansible-playbook", options, false); err != nil {
65 | return cgapp.ShowError(err.Error())
66 | }
67 |
68 | // Stop timer.
69 | stopTimer := cgapp.CalculateDurationTime(startTimer)
70 | cgapp.ShowMessage(
71 | "info",
72 | fmt.Sprintf("Completed in %v seconds!", stopTimer),
73 | false, true,
74 | )
75 |
76 | // Ending message.
77 | cgapp.ShowMessage(
78 | "",
79 | "Have a great project launch! :)",
80 | false, true,
81 | )
82 |
83 | return nil
84 | }
85 |
--------------------------------------------------------------------------------
/cmd/root.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Vic Shóstak and Create Go App Contributors. All rights reserved.
2 | // Use of this source code is governed by Apache 2.0 license
3 | // that can be found in the LICENSE file.
4 |
5 | package cmd
6 |
7 | import (
8 | "github.com/AlecAivazis/survey/v2"
9 | "github.com/spf13/cobra"
10 |
11 | "github.com/create-go-app/cli/v4/pkg/cgapp"
12 | "github.com/create-go-app/cli/v4/pkg/registry"
13 | )
14 |
15 | var (
16 | backend, frontend, proxy string // define project variables
17 | inventory, playbook map[string]interface{} // define template variables
18 | options, proxyList []string // define options, proxy list
19 | useCustomTemplate bool // define custom templates
20 | askBecomePass bool // install Ansible roles, ask become pass
21 | createAnswers, customCreateAnswers registry.CreateAnswers // define answers variable for `create` command
22 |
23 | // Config for survey icons and colors.
24 | // See: https://github.com/mgutz/ansi#style-format
25 | surveyIconsConfig = func(icons *survey.IconSet) {
26 | icons.Question.Format = "cyan"
27 | icons.Question.Text = "[?]"
28 | icons.Help.Format = "blue"
29 | icons.Help.Text = "Help ->"
30 | icons.Error.Format = "yellow"
31 | icons.Error.Text = "Note ->"
32 | }
33 | )
34 |
35 | // rootCmd represents the base command when called without any subcommands.
36 | var rootCmd = &cobra.Command{
37 | Use: "cgapp",
38 | Version: registry.CLIVersion,
39 | Short: "A powerful CLI for the Create Go App project",
40 | Long: `
41 | A powerful CLI for the Create Go App project.
42 |
43 | Create a new production-ready project with backend (Golang),
44 | frontend (JavaScript, TypeScript) and deploy automation
45 | (Ansible, Docker) by running one CLI command.
46 |
47 | -> Focus on writing code and thinking of business logic!
48 | <- The Create Go App CLI will take care of the rest.
49 |
50 | A helpful documentation and next steps -> https://github.com/create-go-app/cli/wiki`,
51 | }
52 |
53 | func init() {
54 | rootCmd.SetOut(cgapp.Stdout)
55 | rootCmd.SetErr(cgapp.Stderr)
56 | }
57 |
58 | // Execute adds all child commands to the root command and sets flags appropriately.
59 | // This is called by main.main(). It only needs to happen once to the rootCmd.
60 | func Execute() {
61 | _ = rootCmd.Execute()
62 | }
63 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/create-go-app/cli/v4
2 |
3 | go 1.21
4 | toolchain go1.24.1
5 |
6 | require (
7 | github.com/AlecAivazis/survey/v2 v2.3.7
8 | github.com/go-git/go-git/v5 v5.16.0
9 | github.com/mattn/go-colorable v0.1.14
10 | github.com/spf13/cobra v1.9.1
11 | )
12 |
13 | require (
14 | dario.cat/mergo v1.0.1 // indirect
15 | github.com/Microsoft/go-winio v0.6.2 // indirect
16 | github.com/ProtonMail/go-crypto v1.1.6 // indirect
17 | github.com/cloudflare/circl v1.6.1 // indirect
18 | github.com/cyphar/filepath-securejoin v0.4.1 // indirect
19 | github.com/emirpasic/gods v1.18.1 // indirect
20 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
21 | github.com/go-git/go-billy/v5 v5.6.2 // indirect
22 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
23 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
24 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
25 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
26 | github.com/kevinburke/ssh_config v1.2.0 // indirect
27 | github.com/mattn/go-isatty v0.0.20 // indirect
28 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
29 | github.com/pjbgf/sha1cd v0.3.2 // indirect
30 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
31 | github.com/skeema/knownhosts v1.3.1 // indirect
32 | github.com/spf13/pflag v1.0.6 // indirect
33 | github.com/xanzy/ssh-agent v0.3.3 // indirect
34 | golang.org/x/crypto v0.37.0 // indirect
35 | golang.org/x/net v0.39.0 // indirect
36 | golang.org/x/sys v0.32.0 // indirect
37 | golang.org/x/term v0.31.0 // indirect
38 | golang.org/x/text v0.24.0 // indirect
39 | gopkg.in/warnings.v0 v0.1.2 // indirect
40 | )
41 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
2 | dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
3 | github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
4 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
5 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
6 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
7 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
8 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
9 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
10 | github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
11 | github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
12 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
13 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
14 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
15 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
16 | github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
17 | github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
18 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
19 | github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
20 | github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
21 | github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
22 | github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
23 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
24 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
25 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
26 | github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
27 | github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
28 | github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
29 | github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
30 | github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
31 | github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
32 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
33 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
34 | github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
35 | github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
36 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
37 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
38 | github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
39 | github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
40 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
41 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
42 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
43 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
44 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
45 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
46 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
47 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
48 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
49 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
50 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
51 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
52 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
53 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
54 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
55 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
56 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
57 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
58 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
59 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
60 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
61 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
62 | github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
63 | github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
64 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
65 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
66 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
67 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
68 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
69 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
70 | github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
71 | github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
72 | github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
73 | github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
74 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
75 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
76 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
77 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
78 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
79 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
80 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
81 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
82 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
83 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
84 | github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
85 | github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
86 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
87 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
88 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
89 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
90 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
91 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
92 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
93 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
94 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
95 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
96 | github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
97 | github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
98 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
99 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
100 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
101 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
102 | golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
103 | golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
104 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
105 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
106 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
107 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
108 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
109 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
110 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
111 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
112 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
113 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
114 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
115 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
116 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
117 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
118 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
119 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
120 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
121 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
122 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
123 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
124 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
125 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
126 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
127 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
128 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
129 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
130 | golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
131 | golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
132 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
133 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
134 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
135 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
136 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
137 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
138 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
139 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
140 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
141 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
142 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
143 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
144 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
145 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
146 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
147 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
148 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
149 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
150 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
151 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
152 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
153 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
154 |
--------------------------------------------------------------------------------
/pkg/cgapp/exec.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Vic Shóstak and Create Go App Contributors. All rights reserved.
2 | // Use of this source code is governed by Apache 2.0 license
3 | // that can be found in the LICENSE file.
4 |
5 | package cgapp
6 |
7 | import (
8 | "bufio"
9 | "bytes"
10 | "fmt"
11 | "os/exec"
12 | )
13 |
14 | // ExecCommand function to execute a given command.
15 | func ExecCommand(command string, options []string, silentMode bool) error {
16 | // Checking for nil.
17 | if command == "" || options == nil {
18 | return fmt.Errorf("no command to execute")
19 | }
20 |
21 | // Create buffer for stderr.
22 | stderr := &bytes.Buffer{}
23 |
24 | // Collect command line.
25 | cmd := exec.Command(command, options...) // #nosec G204
26 |
27 | // Set buffer for stderr from cmd.
28 | cmd.Stderr = stderr
29 |
30 | // Create a new reader.
31 | cmdReader, errStdoutPipe := cmd.StdoutPipe()
32 | if errStdoutPipe != nil {
33 | return ShowError(errStdoutPipe.Error())
34 | }
35 |
36 | // Start executing command.
37 | if errStart := cmd.Start(); errStart != nil {
38 | return ShowError(errStart.Error())
39 | }
40 |
41 | // Create a new scanner and run goroutine func with output, if not in silent mode.
42 | if !silentMode {
43 | scanner := bufio.NewScanner(cmdReader)
44 | go func() {
45 | for scanner.Scan() {
46 | ShowMessage("", scanner.Text(), false, false)
47 | }
48 | }()
49 | }
50 |
51 | // Wait for executing command.
52 | if errWait := cmd.Wait(); errWait != nil {
53 | return ShowError(errWait.Error())
54 | }
55 |
56 | return nil
57 | }
58 |
--------------------------------------------------------------------------------
/pkg/cgapp/exec_test.go:
--------------------------------------------------------------------------------
1 | package cgapp
2 |
3 | import "testing"
4 |
5 | func TestExecCommand(t *testing.T) {
6 | type args struct {
7 | command string
8 | options []string
9 | silentMode bool
10 | }
11 | tests := []struct {
12 | name string
13 | args args
14 | wantErr bool
15 | }{
16 | {
17 | "successfully executing command",
18 | args{
19 | command: "echo",
20 | options: []string{"ping"},
21 | silentMode: false,
22 | },
23 | false,
24 | },
25 | {
26 | "successfully executing command with silent mode",
27 | args{
28 | command: "echo",
29 | options: []string{"ping"},
30 | silentMode: true,
31 | },
32 | false,
33 | },
34 | {
35 | "failed executing command",
36 | args{},
37 | true,
38 | },
39 | }
40 | for _, tt := range tests {
41 | t.Run(tt.name, func(t *testing.T) {
42 | if err := ExecCommand(tt.args.command, tt.args.options, tt.args.silentMode); (err != nil) != tt.wantErr {
43 | t.Errorf("ExecCommand() error = %v, wantErr %v", err, tt.wantErr)
44 | }
45 | })
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/pkg/cgapp/files.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Vic Shóstak and Create Go App Contributors. All rights reserved.
2 | // Use of this source code is governed by Apache 2.0 license
3 | // that can be found in the LICENSE file.
4 |
5 | package cgapp
6 |
7 | import (
8 | "embed"
9 | "fmt"
10 | "io/fs"
11 | "os"
12 | "path/filepath"
13 | "strings"
14 | "text/template"
15 | )
16 |
17 | // EmbeddedFileSystem struct contains embedded file system fields.
18 | type EmbeddedFileSystem struct {
19 | Name embed.FS
20 | RootFolder string
21 | SkipDir bool
22 | }
23 |
24 | // CopyFromEmbeddedFS function for copy files from embedded file system.
25 | func CopyFromEmbeddedFS(efs *EmbeddedFileSystem) error {
26 | // Return copied folders and files.
27 | if err := fs.WalkDir(efs.Name, efs.RootFolder, func(path string, entry fs.DirEntry, err error) error {
28 | // Checking embed path.
29 | if err != nil {
30 | return ShowError(
31 | fmt.Sprintf("Can't copy files from embedded path `%v`!", efs.RootFolder),
32 | )
33 | }
34 |
35 | // Checking, if embedded file is a folder.
36 | if entry.IsDir() && !efs.SkipDir {
37 | // Create folders structure from embedded.
38 | if err := MakeFolder(path); err != nil {
39 | return err
40 | }
41 | }
42 |
43 | // Checking, if embedded file is not a folder.
44 | if !entry.IsDir() {
45 | // Set file data.
46 | fileData, errReadFile := fs.ReadFile(efs.Name, path)
47 | if errReadFile != nil {
48 | return errReadFile
49 | }
50 |
51 | // Path to file, if skipped folders.
52 | if efs.SkipDir {
53 | path = entry.Name()
54 | }
55 |
56 | // Create file from embedded.
57 | if errMakeFile := MakeFile(path, fileData); errMakeFile != nil {
58 | return errMakeFile
59 | }
60 | }
61 |
62 | return nil
63 | }); err != nil {
64 | return err
65 | }
66 |
67 | return nil
68 | }
69 |
70 | // GenerateFileFromTemplate func to generate files from templates.
71 | func GenerateFileFromTemplate(fileName string, variables map[string]interface{}) error {
72 | // Checking file name.
73 | if fileName == "" {
74 | return ShowError(
75 | fmt.Sprintf("Not correct or empty file name (given: `%s`)!", fileName),
76 | )
77 | }
78 |
79 | // Clean file name.
80 | cleanFileName := filepath.Clean(fileName)
81 |
82 | // Parse template.
83 | tmpl, errParseFiles := template.ParseFiles(cleanFileName)
84 | if errParseFiles != nil {
85 | return ShowError(errParseFiles.Error())
86 | }
87 |
88 | // Create a new file with template data.
89 | file, errCreate := os.Create(cleanFileName)
90 | if errCreate != nil {
91 | return ShowError(errCreate.Error())
92 | }
93 |
94 | // Execute template with variables.
95 | if errExecute := tmpl.Execute(file, variables); errExecute != nil {
96 | return ShowError(errExecute.Error())
97 | }
98 | _ = file.Close()
99 |
100 | // Rename output file.
101 | newFileName := strings.ReplaceAll(cleanFileName, ".tmpl", "")
102 | if errRename := os.Rename(cleanFileName, newFileName); errRename != nil {
103 | return ShowError(errRename.Error())
104 | }
105 |
106 | return nil
107 | }
108 |
109 | // MakeFile function for single file create.
110 | func MakeFile(fileName string, fileData []byte) error {
111 | // Write to created file.
112 | if err := os.WriteFile(fileName, fileData, 0o600); err != nil {
113 | return err
114 | }
115 |
116 | return nil
117 | }
118 |
119 | // MakeFolder function for create folder.
120 | func MakeFolder(folderName string) error {
121 | // Check if folder exists, fail if it does.
122 | if err := os.Mkdir(folderName, 0o750); err != nil {
123 | return err
124 | }
125 |
126 | return nil
127 | }
128 |
129 | // RemoveFolders function for massively remove folders.
130 | func RemoveFolders(rootFolder string, foldersToRemove []string) {
131 | for _, folder := range foldersToRemove {
132 | _ = os.RemoveAll(filepath.Join(rootFolder, folder))
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/pkg/cgapp/files_test.go:
--------------------------------------------------------------------------------
1 | package cgapp
2 |
3 | import (
4 | "io/fs"
5 | "os"
6 | "testing"
7 |
8 | "github.com/create-go-app/cli/v4/pkg/registry"
9 | )
10 |
11 | func TestMakeFile(t *testing.T) {
12 |
13 | fileData, err := fs.ReadFile(registry.EmbedMiscFiles, "misc/Makefile")
14 | if err != nil {
15 | t.Error()
16 | }
17 |
18 | type args struct {
19 | rootFolder string
20 | file string
21 | data []byte
22 | }
23 | tests := []struct {
24 | name string
25 | args args
26 | wantErr bool
27 | }{
28 | {
29 | "successfully created files",
30 | args{
31 | rootFolder: "Makefile",
32 | file: "Makefile",
33 | data: fileData,
34 | },
35 | false,
36 | },
37 | {
38 | "failed created files",
39 | args{
40 | rootFolder: "",
41 | file: "",
42 | data: fileData,
43 | },
44 | true,
45 | },
46 | }
47 |
48 | for _, tt := range tests {
49 | t.Run(tt.name, func(t *testing.T) {
50 | if err := MakeFile(tt.args.rootFolder, tt.args.data); (err != nil) != tt.wantErr {
51 | t.Errorf("MakeFile() error = %v, wantErr %v", err, tt.wantErr)
52 | }
53 | })
54 |
55 | // Clean
56 | err := os.RemoveAll("Makefile")
57 | if err != nil {
58 | return
59 | }
60 | }
61 | }
62 |
63 | func TestMakeFolder(t *testing.T) {
64 | type args struct {
65 | folderName string
66 | }
67 | tests := []struct {
68 | name string
69 | args args
70 | wantErr bool
71 | }{
72 | {
73 | "successfully created folder",
74 | args{
75 | folderName: "../../tmp",
76 | },
77 | false,
78 | },
79 | {
80 | "failed, folder is exists",
81 | args{
82 | folderName: "",
83 | },
84 | true,
85 | },
86 | {
87 | "failed, folder is exists",
88 | args{
89 | folderName: "cgapp-project",
90 | },
91 | true,
92 | },
93 | }
94 |
95 | _ = os.Mkdir("cgapp-project", 0o750)
96 |
97 | for _, tt := range tests {
98 | t.Run(tt.name, func(t *testing.T) {
99 | if err := MakeFolder(tt.args.folderName); (err != nil) != tt.wantErr {
100 | t.Errorf("MakeFolder() error = %v, wantErr %v", err, tt.wantErr)
101 | }
102 | })
103 |
104 | // Clean
105 | err := os.RemoveAll(tt.args.folderName)
106 | if err != nil {
107 | return
108 | }
109 | }
110 | }
111 |
112 | func TestRemoveFolders(t *testing.T) {
113 | type args struct {
114 | rootFolder string
115 | foldersToRemove []string
116 | }
117 | tests := []struct {
118 | name string
119 | args args
120 | }{
121 | {
122 | "successfully removed",
123 | args{
124 | rootFolder: "../../tmp",
125 | foldersToRemove: []string{"folder-1"},
126 | },
127 | },
128 | }
129 |
130 | _ = os.MkdirAll("../../tmp/folder-1", 0o750)
131 |
132 | for _, tt := range tests {
133 | t.Run(tt.name, func(t *testing.T) {
134 | RemoveFolders(tt.args.rootFolder, tt.args.foldersToRemove)
135 | })
136 | }
137 | }
138 |
139 | func TestCopyFromEmbeddedFS(t *testing.T) {
140 | type args struct {
141 | efs *EmbeddedFileSystem
142 | }
143 | tests := []struct {
144 | name string
145 | args args
146 | wantErr bool
147 | }{
148 | {
149 | "successfully copy from embedded fs",
150 | args{
151 | efs: &EmbeddedFileSystem{
152 | Name: registry.EmbedTemplates,
153 | RootFolder: "templates",
154 | SkipDir: false,
155 | },
156 | },
157 | false,
158 | },
159 | {
160 | "successfully copy from embedded fs with skip dirs",
161 | args{
162 | efs: &EmbeddedFileSystem{
163 | Name: registry.EmbedTemplates,
164 | RootFolder: "templates",
165 | SkipDir: true,
166 | },
167 | },
168 | false,
169 | },
170 | {
171 | "fail to copy from embedded fs",
172 | args{
173 | efs: &EmbeddedFileSystem{
174 | Name: registry.EmbedTemplates,
175 | RootFolder: "does-not-exist",
176 | SkipDir: false,
177 | },
178 | },
179 | true,
180 | },
181 | {
182 | "fail (no args)",
183 | args{
184 | efs: &EmbeddedFileSystem{},
185 | },
186 | true,
187 | },
188 | }
189 | for _, tt := range tests {
190 | t.Run(tt.name, func(t *testing.T) {
191 | if err := CopyFromEmbeddedFS(tt.args.efs); (err != nil) != tt.wantErr {
192 | t.Errorf("CopyFromEmbeddedFS() error = %v, wantErr %v", err, tt.wantErr)
193 | }
194 | })
195 |
196 | // Clean
197 | _ = os.Remove("hosts.ini.tmpl")
198 | _ = os.Remove("playbook.yml.tmpl")
199 | _ = os.RemoveAll(tt.args.efs.RootFolder)
200 | }
201 | }
202 |
203 | func TestGenerateFileFromTemplate(t *testing.T) {
204 | type args struct {
205 | fileName string
206 | variables map[string]interface{}
207 | }
208 | tests := []struct {
209 | name string
210 | args args
211 | wantErr bool
212 | }{
213 | {
214 | "successfully generate file",
215 | args{
216 | fileName: "../../tmp/test.txt",
217 | variables: map[string]interface{}{},
218 | },
219 | false,
220 | },
221 | {
222 | "failed to generate file",
223 | args{},
224 | true,
225 | },
226 | }
227 |
228 | _ = os.Mkdir("../../tmp", 0o750)
229 | _, _ = os.Create("../../tmp/test.txt")
230 |
231 | for _, tt := range tests {
232 | t.Run(tt.name, func(t *testing.T) {
233 | if err := GenerateFileFromTemplate(tt.args.fileName, tt.args.variables); (err != nil) != tt.wantErr {
234 | t.Errorf("GenerateFileFromTemplate() error = %v, wantErr %v", err, tt.wantErr)
235 | }
236 | })
237 |
238 | // Clean
239 | _ = os.RemoveAll("../../tmp")
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/pkg/cgapp/git.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Vic Shóstak and Create Go App Contributors. All rights reserved.
2 | // Use of this source code is governed by Apache 2.0 license
3 | // that can be found in the LICENSE file.
4 |
5 | package cgapp
6 |
7 | import (
8 | "fmt"
9 | "net/url"
10 | "os"
11 | "path/filepath"
12 | "strings"
13 |
14 | "github.com/go-git/go-git/v5"
15 | )
16 |
17 | // GitClone function for `git clone` defined project template.
18 | func GitClone(templateType, templateURL string) error {
19 | // Checking for nil.
20 | if templateType == "" || templateURL == "" {
21 | return fmt.Errorf("project template not found")
22 | }
23 |
24 | // Get current directory.
25 | currentDir, _ := os.Getwd()
26 |
27 | // Set project folder.
28 | folder := filepath.Join(currentDir, templateType)
29 |
30 | // Clone project template.
31 | _, errPlainClone := git.PlainClone(
32 | folder,
33 | false,
34 | &git.CloneOptions{
35 | URL: getAbsoluteURL(templateURL),
36 | },
37 | )
38 | if errPlainClone != nil {
39 | return ShowError(
40 | fmt.Sprintf("Repository `%v` was not cloned!", templateURL),
41 | )
42 | }
43 |
44 | // Cleanup project.
45 | RemoveFolders(folder, []string{".git", ".github"})
46 |
47 | return nil
48 | }
49 |
50 | // getAbsolutURL func for help define correct HTTP protocol.
51 | func getAbsoluteURL(templateURL string) string {
52 | templateURL = strings.TrimSpace(templateURL)
53 | u, _ := url.Parse(templateURL)
54 |
55 | if u.Scheme == "" {
56 | u.Scheme = "https"
57 | }
58 |
59 | return u.String()
60 | }
61 |
--------------------------------------------------------------------------------
/pkg/cgapp/git_test.go:
--------------------------------------------------------------------------------
1 | package cgapp
2 |
3 | import (
4 | "os"
5 | "testing"
6 | )
7 |
8 | func TestGitClone(t *testing.T) {
9 | type args struct {
10 | rootFolder string
11 | templateName string
12 | }
13 | tests := []struct {
14 | name string
15 | args args
16 | wantErr bool
17 | }{
18 | {
19 | "successfully cloned project",
20 | args{
21 | rootFolder: "../../tmp/test",
22 | templateName: "github.com/koddr/koddr",
23 | },
24 | false,
25 | },
26 | {
27 | "failed clone project (empty template)",
28 | args{
29 | rootFolder: "../../tmp/test",
30 | templateName: "",
31 | },
32 | true,
33 | },
34 | {
35 | "failed clone project",
36 | args{
37 | rootFolder: "../../tmp/test",
38 | templateName: "404.404/404/404",
39 | },
40 | true,
41 | },
42 | {
43 | "failed clone project (empty args)",
44 | args{},
45 | true,
46 | },
47 | }
48 | for _, tt := range tests {
49 | t.Run(tt.name, func(t *testing.T) {
50 | if err := GitClone(tt.args.rootFolder, tt.args.templateName); (err != nil) != tt.wantErr {
51 | t.Errorf("GitClone() error = %v, wantErr %v", err, tt.wantErr)
52 | }
53 | })
54 |
55 | // Clean
56 | err := os.RemoveAll("../../tmp")
57 | if err != nil {
58 | return
59 | }
60 | }
61 | }
62 |
63 | func Test_getAbsoluteURL(t *testing.T) {
64 | type args struct {
65 | templateURL string
66 | }
67 | tests := []struct {
68 | name string
69 | args args
70 | want string
71 | }{
72 | {
73 | "successfully get absolute url from url with scheme",
74 | args{
75 | templateURL: "https://github.com/create-go-app/net_http-go-template",
76 | },
77 | "https://github.com/create-go-app/net_http-go-template",
78 | },
79 | {
80 | "successfully get absolute url from url without scheme",
81 | args{
82 | templateURL: "github.com/create-go-app/net_http-go-template",
83 | },
84 | "https://github.com/create-go-app/net_http-go-template",
85 | },
86 | {
87 | "successfully get absolute url from url starting space",
88 | args{
89 | templateURL: " github.com/create-go-app/net_http-go-template",
90 | },
91 | "https://github.com/create-go-app/net_http-go-template",
92 | },
93 | }
94 |
95 | for _, tt := range tests {
96 | t.Run(tt.name, func(t *testing.T) {
97 | if got := getAbsoluteURL(tt.args.templateURL); got != tt.want {
98 | t.Errorf("getAbsoluteURL() = %v, want %v", got, tt.want)
99 | }
100 | })
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/pkg/cgapp/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Vic Shóstak and Create Go App Contributors. All rights reserved.
2 | // Use of this source code is governed by Apache 2.0 license
3 | // that can be found in the LICENSE file.
4 |
5 | package cgapp
6 |
7 | import (
8 | "fmt"
9 | "time"
10 |
11 | "github.com/mattn/go-colorable"
12 | )
13 |
14 | var (
15 | Stdout = colorable.NewColorableStdout() // add a colorable std out
16 | Stderr = colorable.NewColorableStderr() // add a colorable std err
17 | )
18 |
19 | // ShowMessage function for showing output messages.
20 | func ShowMessage(level, text string, startWithNewLine, endWithNewLine bool) {
21 | // Define variables.
22 | var startLine, endLine string
23 |
24 | if startWithNewLine {
25 | startLine = "\n" // set a new line
26 | }
27 |
28 | if endWithNewLine {
29 | endLine = "\n" // set a new line
30 | }
31 |
32 | // Formatting message.
33 | message := fmt.Sprintf("%s %s %s %s", startLine, colorizeLevel(level), text, endLine)
34 |
35 | // Return output.
36 | _, err := fmt.Fprintln(Stdout, message)
37 | if err != nil {
38 | return
39 | }
40 | }
41 |
42 | // ShowError function for send error message to output.
43 | func ShowError(text string) error {
44 | return fmt.Errorf("%s%s", colorizeLevel("error"), text)
45 | }
46 |
47 | // CalculateDurationTime func to calculate duration time.
48 | func CalculateDurationTime(startTimer time.Time) string {
49 | return fmt.Sprintf("%.0f", time.Since(startTimer).Seconds())
50 | }
51 |
52 | // colorizeLevel function for send (colored or common) message to output.
53 | func colorizeLevel(level string) string {
54 | // Define variables.
55 | var (
56 | red = "\033[0;31m"
57 | green = "\033[0;32m"
58 | yellow = "\033[1;33m"
59 | noColor = "\033[0m"
60 | color, icon string
61 | )
62 |
63 | // Switch color.
64 | switch level {
65 | case "success":
66 | color = green
67 | icon = "[OK]"
68 | case "error":
69 | color = red
70 | icon = "[ERROR]"
71 | case "info":
72 | color = yellow
73 | icon = "[INFO]"
74 | default:
75 | color = noColor
76 | }
77 |
78 | // Send common or colored caption.
79 | return fmt.Sprintf("%s%s%s", color, icon, noColor)
80 | }
81 |
--------------------------------------------------------------------------------
/pkg/cgapp/utils_test.go:
--------------------------------------------------------------------------------
1 | package cgapp
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func Test_colorizeLevel(t *testing.T) {
9 | type args struct {
10 | level string
11 | }
12 | tests := []struct {
13 | name string
14 | args args
15 | }{
16 | {
17 | "successfully send message",
18 | args{level: ""},
19 | },
20 | {
21 | "successfully send success message",
22 | args{level: "success"},
23 | },
24 | {
25 | "successfully send error message",
26 | args{level: "error"},
27 | },
28 | {
29 | "successfully send info message",
30 | args{level: "info"},
31 | },
32 | }
33 | for _, tt := range tests {
34 | t.Run(tt.name, func(t *testing.T) {
35 | _ = colorizeLevel(tt.args.level)
36 | })
37 | }
38 | }
39 |
40 | func TestShowMessage(t *testing.T) {
41 | type args struct {
42 | level string
43 | text string
44 | startWithNewLine bool
45 | endWithNewLine bool
46 | }
47 | tests := []struct {
48 | name string
49 | args args
50 | }{
51 | {
52 | "successfully send message without args",
53 | args{},
54 | },
55 | {
56 | "successfully send message with args",
57 | args{"success", "Test", true, true},
58 | },
59 | }
60 | for _, tt := range tests {
61 | t.Run(tt.name, func(t *testing.T) {
62 | ShowMessage(tt.args.level, tt.args.text, tt.args.startWithNewLine, tt.args.endWithNewLine)
63 | })
64 | }
65 | }
66 |
67 | func TestCalculateDurationTime(t *testing.T) {
68 | type args struct {
69 | startTimer time.Time
70 | }
71 | tests := []struct {
72 | name string
73 | args args
74 | want string
75 | }{
76 | {
77 | "successfully",
78 | args{
79 | startTimer: time.Now(),
80 | },
81 | "0",
82 | },
83 | }
84 | for _, tt := range tests {
85 | t.Run(tt.name, func(t *testing.T) {
86 | if got := CalculateDurationTime(tt.args.startTimer); got != tt.want {
87 | t.Errorf("CalculateDurationTime() = %v, want %v", got, tt.want)
88 | }
89 | })
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/pkg/registry/defaults.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Vic Shóstak and Create Go App Contributors. All rights reserved.
2 | // Use of this source code is governed by Apache 2.0 license
3 | // that can be found in the LICENSE file.
4 |
5 | package registry
6 |
7 | import (
8 | "embed"
9 |
10 | "github.com/AlecAivazis/survey/v2"
11 | )
12 |
13 | // CLIVersion version of Create Go App CLI.
14 | const CLIVersion string = "4.1.0"
15 |
16 | // Variables struct for Ansible variables (inventory, hosts).
17 | type Variables struct {
18 | List map[string]interface{}
19 | }
20 |
21 | // CreateAnswers struct for a survey's answers for `create` command.
22 | type CreateAnswers struct {
23 | Backend string
24 | Frontend string
25 | Proxy string
26 | AgreeCreation bool `survey:"agree"`
27 | }
28 |
29 | var (
30 | // EmbedMiscFiles misc files and configs.
31 | //go:embed misc/*
32 | EmbedMiscFiles embed.FS
33 |
34 | // EmbedRoles Ansible roles.
35 | //go:embed roles/*
36 | EmbedRoles embed.FS
37 |
38 | // EmbedTemplates template files.
39 | //go:embed templates/*
40 | EmbedTemplates embed.FS
41 |
42 | // CreateQuestions survey's questions for `create` command.
43 | CreateQuestions = []*survey.Question{
44 | {
45 | Name: "backend",
46 | Prompt: &survey.Select{
47 | Message: "Choose a backend framework:",
48 | Options: []string{
49 | "net/http",
50 | "fiber",
51 | "chi",
52 | },
53 | Default: "fiber",
54 | PageSize: 3,
55 | },
56 | Validate: survey.Required,
57 | },
58 | {
59 | Name: "frontend",
60 | Prompt: &survey.Select{
61 | Message: "Choose a frontend framework/library:",
62 | Help: "Option with a `*-ts` tail will create a TypeScript template.",
63 | Options: []string{
64 | "none",
65 | "vanilla",
66 | "vanilla-ts",
67 | "react",
68 | "react-ts",
69 | "react-swc",
70 | "react-swc-ts",
71 | "preact",
72 | "preact-ts",
73 | "next",
74 | "next-ts",
75 | "nuxt",
76 | "vue",
77 | "vue-ts",
78 | "sveltekit",
79 | "svelte",
80 | "svelte-ts",
81 | "solid",
82 | "solid-ts",
83 | "lit",
84 | "lit-ts",
85 | "qwik",
86 | "qwik-ts",
87 | },
88 | Default: "none",
89 | PageSize: 21,
90 | },
91 | },
92 | {
93 | Name: "proxy",
94 | Prompt: &survey.Select{
95 | Message: "Choose a web/proxy server:",
96 | Options: []string{
97 | "none",
98 | "traefik",
99 | "traefik-acme-dns",
100 | "nginx",
101 | },
102 | Default: "none",
103 | PageSize: 4,
104 | },
105 | },
106 | {
107 | Name: "agree",
108 | Prompt: &survey.Confirm{
109 | Message: "If everything is okay, can I create this project for you? ;)",
110 | Default: true,
111 | },
112 | },
113 | }
114 |
115 | // CustomCreateQuestions survey's questions for `create -c` command.
116 | CustomCreateQuestions = []*survey.Question{
117 | {
118 | Name: "backend",
119 | Prompt: &survey.Input{
120 | Message: "Enter URL to the custom backend repository:",
121 | },
122 | Validate: survey.Required,
123 | },
124 | {
125 | Name: "frontend",
126 | Prompt: &survey.Input{
127 | Message: "Enter URL to the custom frontend repository:",
128 | Default: "none",
129 | },
130 | },
131 | {
132 | Name: "proxy",
133 | Prompt: &survey.Select{
134 | Message: "Choose a web/proxy server:",
135 | Options: []string{
136 | "none",
137 | "traefik",
138 | "traefik-acme-dns",
139 | "nginx",
140 | },
141 | Default: "none",
142 | PageSize: 4,
143 | },
144 | },
145 | {
146 | Name: "agree",
147 | Prompt: &survey.Confirm{
148 | Message: "If everything is okay, can I create this project for you? ;)",
149 | Default: true,
150 | },
151 | },
152 | }
153 |
154 | // AnsibleInventoryVariables list of variables for inventory.
155 | AnsibleInventoryVariables = map[string]*Variables{
156 | "none": {
157 | List: map[string]interface{}{
158 | "Proxy": "none",
159 | },
160 | },
161 | "traefik": {
162 | List: map[string]interface{}{
163 | "Proxy": "traefik",
164 | "Wildcard": false,
165 | },
166 | },
167 | "traefik-acme-dns": {
168 | List: map[string]interface{}{
169 | "Proxy": "traefik",
170 | "Wildcard": true,
171 | },
172 | },
173 | "nginx": {
174 | List: map[string]interface{}{
175 | "Proxy": "nginx",
176 | },
177 | },
178 | }
179 |
180 | // AnsiblePlaybookVariables list of variables for playbook.
181 | AnsiblePlaybookVariables = map[string]*Variables{
182 | "none": {
183 | List: map[string]interface{}{
184 | "Proxy": "none",
185 | },
186 | },
187 | "traefik": {
188 | List: map[string]interface{}{
189 | "Proxy": "traefik",
190 | },
191 | },
192 | "traefik-acme-dns": {
193 | List: map[string]interface{}{
194 | "Proxy": "traefik",
195 | },
196 | },
197 | "nginx": {
198 | List: map[string]interface{}{
199 | "Proxy": "nginx",
200 | },
201 | },
202 | }
203 | )
204 |
--------------------------------------------------------------------------------
/pkg/registry/misc/.editorconfig:
--------------------------------------------------------------------------------
1 | # A default .editorconfig for Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | root = true
6 |
7 | [*]
8 | indent_style = space
9 | indent_size = 2
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 |
14 | [{go.mod,go.sum,*.go}]
15 | indent_style = tab
16 | indent_size = 4
17 |
18 | [{Dockerfile,Makefile,*.yml,*.yaml}]
19 | indent_style = tab
20 | indent_size = 2
21 |
--------------------------------------------------------------------------------
/pkg/registry/misc/.gitattributes:
--------------------------------------------------------------------------------
1 | # A default .gitattributes for Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | go.sum merge=union
6 | *.go text eol=lf
7 |
--------------------------------------------------------------------------------
/pkg/registry/misc/.gitignore:
--------------------------------------------------------------------------------
1 | # A default .gitignore for Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | # macOS
6 | **/.DS_store
7 |
8 | # Go builds
9 | **/app/
10 | **/build/
11 |
12 | # Temporary files
13 | tmp/
14 |
15 | # Node.js dependencies
16 | **/node_modules/
17 |
18 | # Files
19 | hosts.ini
20 | *.out
21 |
--------------------------------------------------------------------------------
/pkg/registry/misc/Makefile:
--------------------------------------------------------------------------------
1 | # A default Makefile for Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | .PHONY: test run build
6 |
7 | FRONTEND_PATH = $(PWD)/frontend
8 | BACKEND_PATH = $(PWD)/backend
9 |
10 | test:
11 | @if [ -d "$(FRONTEND_PATH)" ]; then cd $(FRONTEND_PATH) && npm run test; fi
12 | @if [ -d "$(BACKEND_PATH)" ]; then cd $(BACKEND_PATH) && go test ./...; fi
13 |
14 | run: test
15 | @if [ -d "$(FRONTEND_PATH)" ]; then cd $(FRONTEND_PATH) && npm run dev; fi
16 | @if [ -d "$(BACKEND_PATH)" ]; then cd $(BACKEND_PATH) && $(MAKE) run; fi
17 |
18 | build: test
19 | @if [ -d "$(FRONTEND_PATH)" ]; then cd $(FRONTEND_PATH) && npm run build; fi
20 | @if [ -d "$(BACKEND_PATH)" ]; then cd $(BACKEND_PATH) && $(MAKE) build; fi
21 |
--------------------------------------------------------------------------------
/pkg/registry/roles/backend/tasks/main.yml:
--------------------------------------------------------------------------------
1 | # Ansible role for deploy a backend of the Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | ---
6 | #
7 | # Delete backend files.
8 | #
9 | - name: Delete previous backend files
10 | file:
11 | state: absent
12 | path: "{{ server_dir }}/backend"
13 |
14 | #
15 | # Copy `./backend` folder to the remote server.
16 | #
17 | - name: Copy a new backend files
18 | synchronize:
19 | src: ./backend
20 | dest: "{{ server_dir }}"
21 | rsync_opts:
22 | - "--exclude=.git,.github,build,*.md"
23 |
24 | #
25 | # Build backend Docker container.
26 | #
27 | - name: Build Docker image for backend
28 | community.docker.docker_image:
29 | name: cgapp_backend # name of the backend image
30 | build:
31 | path: "{{ server_dir }}/backend" # folder with Dockerfile
32 | pull: yes
33 | source: build
34 |
35 | #
36 | # Run backend container (for Traefik Proxy).
37 | #
38 | - name: Run Docker container with backend (for Traefik Proxy)
39 | community.docker.docker_container:
40 | name: cgapp-backend # name of the backend container
41 | image: cgapp_backend:latest
42 | restart_policy: unless-stopped
43 | recreate: true
44 | networks:
45 | - name: "{{ docker_network }}"
46 | ports:
47 | - "{{ backend_port }}:{{ backend_port }}"
48 | labels:
49 | traefik.enable: "true"
50 | traefik.http.routers.backend.rule: "Host(`{{ project_domain }}`)"
51 | traefik.http.routers.backend.entrypoints: "websecure"
52 | when: traefik_version is defined
53 |
54 | #
55 | # Run backend container (for Nginx).
56 | #
57 | - name: Run Docker container with backend (for Nginx)
58 | community.docker.docker_container:
59 | name: cgapp-backend # name of the backend container
60 | image: cgapp_backend:latest
61 | restart_policy: unless-stopped
62 | recreate: true
63 | networks:
64 | - name: "{{ docker_network }}"
65 | ports:
66 | - "{{ backend_port }}:{{ backend_port }}"
67 | when: nginx_version is defined
68 |
--------------------------------------------------------------------------------
/pkg/registry/roles/docker/tasks/main.yml:
--------------------------------------------------------------------------------
1 | # Ansible role for deploy the Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | ---
6 | #
7 | # Make `apt update`.
8 | #
9 | - name: Update system packages cache
10 | apt:
11 | update_cache: yes
12 | cache_valid_time: 3600
13 |
14 | #
15 | # Make `apt upgrade`.
16 | #
17 | - name: Update all system packages to their latest versions
18 | apt:
19 | name: "*"
20 | state: latest
21 |
22 | #
23 | # Install `python3-pip` package for working with Docker from Ansible on the remote host.
24 | #
25 | - name: Install python3-pip package
26 | apt:
27 | pkg:
28 | - python3-pip
29 |
30 | #
31 | # Install `docker` package for Python 3 from pip3.
32 | #
33 | - name: Add the Python 3 client for Docker
34 | pip:
35 | name: docker
36 |
37 | #
38 | # Create a new system group for Docker.
39 | #
40 | - name: Create a new Docker group
41 | group:
42 | name: "{{ server_group }}"
43 |
44 | #
45 | # Create a new system user for Docker.
46 | #
47 | - name: Add the default user to the Docker group
48 | user:
49 | name: "{{ server_user }}"
50 | group: "{{ server_group }}"
51 |
52 | #
53 | # Create a new Docker network for connect all project elements into one network.
54 | #
55 | - name: Create a new Docker network
56 | community.docker.docker_network:
57 | name: "{{ docker_network }}"
58 |
59 | #
60 | # Create folder for project files.
61 | #
62 | - name: Create folder for project files
63 | file:
64 | state: directory
65 | path: "{{ server_dir }}"
66 | owner: "{{ server_user }}"
67 | group: "{{ server_group }}"
68 |
--------------------------------------------------------------------------------
/pkg/registry/roles/nginx/tasks/main.yml:
--------------------------------------------------------------------------------
1 | # Ansible role for deploy the Nginx for the Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | ---
6 | #
7 | # Delete webserver files.
8 | #
9 | - name: Delete previous webserver files
10 | file:
11 | state: absent
12 | path: "{{ server_dir }}/webserver"
13 |
14 | #
15 | # Create folder for Nginx.
16 | #
17 | - name: Ensures Nginx dir exists
18 | file:
19 | state: directory
20 | path: "{{ server_dir }}/webserver"
21 | owner: "{{ server_user }}"
22 | group: "{{ server_group }}"
23 |
24 | #
25 | # Create Nginx config.
26 | #
27 | - name: Adding nginx.conf file
28 | template:
29 | src: nginx.conf.j2
30 | dest: "{{ server_dir }}/webserver/nginx.conf"
31 | mode: 0600
32 |
33 | #
34 | # Create default server config.
35 | #
36 | - name: Adding default.conf file
37 | template:
38 | src: "{{ 'default-https.conf.j2' if nginx_use_only_https == 'yes' else 'default-http.conf.j2' }}"
39 | dest: "{{ server_dir }}/webserver/default.conf"
40 | mode: 0600
41 |
42 | #
43 | # Run official Nginx Docker container (for HTTP only) with specified version.
44 | #
45 | - name: Run Nginx container (for HTTP)
46 | community.docker.docker_container:
47 | name: cgapp-nginx
48 | image: "nginx:{{ nginx_version }}"
49 | restart_policy: unless-stopped
50 | recreate: true
51 | networks:
52 | - name: "{{ docker_network }}"
53 | ports:
54 | - "80:80"
55 | volumes:
56 | - "{{ server_dir }}/webserver/nginx.conf:/etc/nginx/nginx.conf:ro"
57 | - "{{ server_dir }}/webserver/default.conf:/etc/nginx/conf.d/default.conf"
58 | when: nginx_use_only_https == 'no'
59 |
60 | #
61 | # Run official Nginx Docker container (for HTTPS only) with specified version.
62 | #
63 | - name: Run Nginx container (for HTTPS only)
64 | community.docker.docker_container:
65 | name: cgapp-nginx
66 | image: "nginx:{{ nginx_version }}"
67 | restart_policy: unless-stopped
68 | recreate: true
69 | networks:
70 | - name: "{{ docker_network }}"
71 | ports:
72 | - "80:80"
73 | - "443:443"
74 | volumes:
75 | - "{{ server_dir }}/webserver/nginx.conf:/etc/nginx/nginx.conf:ro"
76 | - "{{ server_dir }}/webserver/default.conf:/etc/nginx/conf.d/default.conf"
77 | when: nginx_use_only_https == 'yes'
78 |
--------------------------------------------------------------------------------
/pkg/registry/roles/nginx/templates/default-http.conf.j2:
--------------------------------------------------------------------------------
1 | #
2 | # Default server config without SSL certificate:
3 | #
4 |
5 | {% if nginx_redirect_to_non_www %}
6 | # WWW redirects
7 | server {
8 | listen 80 default_server;
9 | listen [::]:80 default_server;
10 | server_name www.{{ project_domain }};
11 | return 301 https://{{ project_domain }}$request_uri;
12 | }
13 | {% endif %}
14 |
15 | # Main server
16 | server {
17 | listen 80;
18 | listen [::]:80;
19 | {% if nginx_redirect_to_non_www %}
20 | server_name {{ project_domain }};
21 | {% else %}
22 | server_name {{ project_domain }} www.{{ project_domain }};
23 | {% endif %}
24 | location / {
25 | resolver 127.0.0.11;
26 | proxy_pass http://cgapp-backend:{{ backend_port }}/;
27 | proxy_set_header Host $host;
28 | proxy_set_header X-Real-IP $remote_addr;
29 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
30 | proxy_set_header X-Forwarded-Proto $scheme;
31 | proxy_redirect off;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/pkg/registry/roles/nginx/templates/default-https.conf.j2:
--------------------------------------------------------------------------------
1 | #
2 | # Default server config with SSL certificate (Let's Encrypt):
3 | #
4 |
5 | # HTTP redirects
6 | server {
7 | listen 80 default_server;
8 | listen [::]:80 default_server;
9 | server_name {{ project_domain }} www.{{ project_domain }};
10 | return 301 https://{{ project_domain }}$request_uri;
11 | }
12 |
13 | # WWW redirects
14 | server {
15 | listen 443 ssl http2;
16 | listen [::]:443 ssl http2;
17 | {% if nginx_redirect_to_non_www %}
18 | server_name www.{{ project_domain }};
19 | return 301 https://{{ project_domain }}$request_uri;
20 | {% else %}
21 | server_name {{ project_domain }};
22 | return 301 https://www.{{ project_domain }}$request_uri;
23 | {% endif %}
24 | }
25 |
26 | # Main server
27 | server {
28 | listen 443 ssl http2;
29 | listen [::]:443 ssl http2;
30 | {% if nginx_redirect_to_non_www %}
31 | server_name {{ project_domain }};
32 | {% else %}
33 | server_name {{ project_domain }} www.{{ project_domain }};
34 | {% endif %}
35 | ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
36 | ssl_certificate /etc/letsencrypt/live/{{ project_domain }}/fullchain.pem;
37 | ssl_certificate_key /etc/letsencrypt/live/{{ project_domain }}/privkey.pem;
38 | ssl_trusted_certificate /etc/letsencrypt/live/{{ project_domain }}/chain.pem;
39 | location / {
40 | resolver 127.0.0.11;
41 | proxy_pass http://cgapp-backend:{{ backend_port }}/;
42 | proxy_set_header Host $host;
43 | proxy_set_header X-Real-IP $remote_addr;
44 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
45 | proxy_set_header X-Forwarded-Proto $scheme;
46 | proxy_redirect off;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/registry/roles/nginx/templates/nginx.conf.j2:
--------------------------------------------------------------------------------
1 | #
2 | # Nginx config
3 | #
4 |
5 | worker_processes auto;
6 |
7 | events {
8 | use epoll;
9 | multi_accept on;
10 | worker_connections 1024;
11 | }
12 |
13 | http {
14 | charset utf-8;
15 | sendfile on;
16 | tcp_nopush on;
17 | tcp_nodelay on;
18 | server_tokens off;
19 | log_not_found off;
20 |
21 | # Bucket size for server names hash
22 | server_names_hash_bucket_size 64;
23 |
24 | # MIME
25 | include /etc/nginx/mime.types;
26 | default_type application/octet-stream;
27 |
28 | # Logging
29 | error_log /var/log/nginx/error.log crit;
30 | access_log off;
31 |
32 | # Timeouts
33 | send_timeout 2;
34 | keepalive_timeout 30;
35 | keepalive_requests 100;
36 | client_body_timeout 10;
37 | reset_timedout_connection on;
38 |
39 | # Max body size
40 | client_max_body_size 4m;
41 |
42 | # Cache
43 | open_file_cache max=200000 inactive=20s;
44 | open_file_cache_valid 30s;
45 | open_file_cache_errors on;
46 | open_file_cache_min_uses 2;
47 |
48 | # Gzip
49 | gzip on;
50 | gzip_vary on;
51 | gzip_disable "msie6";
52 | gzip_proxied any;
53 | gzip_comp_level 6;
54 |
55 | # File types for compress via gzip
56 | gzip_types text/plain text/css application/json application/x-javascript text/xml
57 | application/xml application/xml+rss text/javascript application/javascript
58 | image/svg+xml image/gif image/png image/jpeg image/x-icon image/webp;
59 |
60 | # SSL
61 | 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;
62 | ssl_stapling on;
63 | ssl_protocols TLSv1.2 TLSv1.3;
64 | ssl_session_cache shared:le_nginx_SSL:1m;
65 | ssl_stapling_verify on;
66 | ssl_session_timeout 1d;
67 | ssl_session_tickets off;
68 | ssl_prefer_server_ciphers off;
69 |
70 | # Security headers
71 | add_header X-Frame-Options "SAMEORIGIN" always;
72 | add_header Referrer-Policy "no-referrer-when-downgrade" always;
73 | add_header X-XSS-Protection "1; mode=block" always;
74 | add_header X-Content-Type-Options "nosniff" always;
75 | add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
76 | add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
77 |
78 | # Load configs
79 | include /usr/share/nginx/modules/*.conf;
80 | include /etc/nginx/conf.d/*.conf;
81 | include /etc/nginx/sites-enabled/*;
82 | }
--------------------------------------------------------------------------------
/pkg/registry/roles/postgres/tasks/main.yml:
--------------------------------------------------------------------------------
1 | # Ansible role for deploy the PostgreSQL for the Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | ---
6 | #
7 | # Create folder for PostgreSQL database.
8 | #
9 | - name: Ensures PostgreSQL dir exists
10 | file:
11 | state: directory
12 | path: "{{ server_dir }}/database"
13 | owner: "{{ server_user }}"
14 | group: "{{ server_group }}"
15 |
16 | #
17 | # Run official PostgreSQL Docker container with specified version.
18 | #
19 | - name: Run PostgreSQL container
20 | community.docker.docker_container:
21 | name: "{{ postgres_container_name }}"
22 | image: "postgres:{{ postgres_version }}"
23 | restart_policy: unless-stopped
24 | recreate: true
25 | networks:
26 | - name: "{{ docker_network }}"
27 | ports:
28 | - "{{ postgres_port }}:{{ postgres_port }}"
29 | env:
30 | POSTGRES_USER: "{{ postgres_user }}"
31 | POSTGRES_PASSWORD: "{{ postgres_password }}"
32 | POSTGRES_DB: "{{ postgres_db }}"
33 | volumes:
34 | - "{{ server_dir }}/database/data/:/var/lib/postgresql/data"
35 |
36 | #
37 | # Make DB migration.
38 | #
39 | - name: Make DB migration
40 | community.docker.docker_container:
41 | name: cgapp-db-migration
42 | image: "migrate/migrate"
43 | recreate: true
44 | networks:
45 | - name: "{{ docker_network }}"
46 | command:
47 | [
48 | "-path",
49 | "/migrations",
50 | "-database",
51 | "postgres://{{ postgres_user }}:{{ postgres_password }}@{{ postgres_container_name }}:{{ postgres_port }}/{{ postgres_db }}?sslmode={{ postgres_ssl_mode }}",
52 | "up",
53 | "{{ migrate_number }}",
54 | ]
55 | volumes:
56 | - "{{ server_dir }}/backend/platform/migrations/:/migrations"
57 |
--------------------------------------------------------------------------------
/pkg/registry/roles/redis/tasks/main.yml:
--------------------------------------------------------------------------------
1 | # Ansible role for deploy the Redis for the Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | ---
6 | #
7 | # Create folder for Redis cache.
8 | #
9 | - name: Ensures Redis dir exists
10 | file:
11 | state: directory
12 | path: "{{ server_dir }}/cache"
13 | owner: "{{ server_user }}"
14 | group: "{{ server_group }}"
15 |
16 | #
17 | # Run official Redis Docker container with specified version.
18 | #
19 | - name: Run Redis container
20 | community.docker.docker_container:
21 | name: "{{ redis_container_name }}"
22 | image: "redis:{{ redis_version }}"
23 | restart_policy: unless-stopped
24 | recreate: true
25 | networks:
26 | - name: "{{ docker_network }}"
27 | ports:
28 | - "{{ redis_port }}:{{ redis_port }}"
29 | volumes:
30 | - "{{ server_dir }}/cache/data/:/data"
31 |
--------------------------------------------------------------------------------
/pkg/registry/roles/traefik/tasks/main.yml:
--------------------------------------------------------------------------------
1 | # Ansible role for deploy the Traefik Proxy for the Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | ---
6 | #
7 | # Delete webserver files.
8 | #
9 | - name: Delete previous webserver files
10 | file:
11 | state: absent
12 | path: "{{ server_dir }}/webserver"
13 |
14 | #
15 | # Create folder for Traefik.
16 | #
17 | - name: Ensures Traefik dir exists
18 | file:
19 | state: directory
20 | path: "{{ server_dir }}/webserver"
21 | owner: "{{ server_user }}"
22 | group: "{{ server_group }}"
23 |
24 | #
25 | # Create file for ACME challenge.
26 | #
27 | - name: Ensures acme.json file exists
28 | file:
29 | state: touch
30 | path: "{{ server_dir }}/webserver/acme.json"
31 | mode: 0600
32 |
33 | #
34 | # Create Traefik config.
35 | #
36 | - name: Adding traefik.yml file
37 | template:
38 | src: traefik.yml.j2
39 | dest: "{{ server_dir }}/webserver/traefik.yml"
40 | mode: 0600
41 |
42 | #
43 | # Create Traefik log file.
44 | #
45 | - name: Ensures traefik.log file exists
46 | file:
47 | state: touch
48 | path: "{{ server_dir }}/webserver/traefik.log"
49 | mode: 0600
50 |
51 | #
52 | # Run official Traefik Docker container
53 | # WITHOUT dashboard (Traefik Web UI) and specified version.
54 | #
55 | - name: Run Traefik container without dashboard (Traefik Web UI)
56 | community.docker.docker_container:
57 | name: cgapp-traefik
58 | image: "traefik:{{ traefik_version }}"
59 | restart_policy: unless-stopped
60 | recreate: true
61 | networks:
62 | - name: "{{ docker_network }}"
63 | ports:
64 | - "80:80"
65 | - "443:443"
66 | volumes:
67 | - "/var/run/docker.sock:/var/run/docker.sock"
68 | - "{{ server_dir }}/webserver/traefik.yml:/etc/traefik/traefik.yml:ro"
69 | - "{{ server_dir }}/webserver/acme.json:/acme.json:rw"
70 | - "{{ server_dir }}/webserver/traefik.log:/traefik.log:rw"
71 | #
72 | # Don't forget to set environment variables, if your choose DNS way to getting SSL cert.
73 | # See: https://doc.traefik.io/traefik/https/acme/#providers
74 | #
75 | # env:
76 | # # For example, auth token for DigitalOcean DNS provider:
77 | # DO_AUTH_TOKEN: "{{ do_auth_token }}"
78 | #
79 | when: (traefik_dashboard_url is not defined) or (traefik_dashboard_url|length == 0)
80 |
81 | #
82 | # Run official Traefik Docker container
83 | # WITH dashboard (Traefik Web UI) and specified version.
84 | #
85 | - name: Run Traefik container with dashboard (Traefik Web UI)
86 | community.docker.docker_container:
87 | name: cgapp-traefik
88 | image: "traefik:{{ traefik_version }}"
89 | restart_policy: unless-stopped
90 | recreate: true
91 | networks:
92 | - name: "{{ docker_network }}"
93 | ports:
94 | - "80:80"
95 | - "443:443"
96 | volumes:
97 | - "/var/run/docker.sock:/var/run/docker.sock"
98 | - "{{ server_dir }}/webserver/traefik.yml:/etc/traefik/traefik.yml:ro"
99 | - "{{ server_dir }}/webserver/acme.json:/acme.json:rw"
100 | - "{{ server_dir }}/webserver/traefik.log:/traefik.log:rw"
101 | labels:
102 | traefik.enable: "true"
103 | traefik.http.routers.traefik.rule: "Host(`{{ traefik_dashboard_url }}`)"
104 | traefik.http.routers.traefik.entrypoints: "websecure"
105 | traefik.http.routers.traefik.service: "api@internal"
106 | traefik.http.routers.traefik.middlewares: "auth"
107 | traefik.http.middlewares.auth.basicauth.users: "{{ traefik_dashboard_user }}:{{ traefik_dashboard_password }}"
108 | #
109 | # Don't forget to set environment variables, if your choose DNS way to getting SSL cert.
110 | # See: https://doc.traefik.io/traefik/https/acme/#providers
111 | #
112 | # env:
113 | # # For example, auth token for DigitalOcean DNS provider:
114 | # DO_AUTH_TOKEN: "{{ do_auth_token }}"
115 | #
116 | when: (traefik_dashboard_url is defined) or (traefik_dashboard_url|length > 0)
117 |
--------------------------------------------------------------------------------
/pkg/registry/roles/traefik/templates/traefik.yml.j2:
--------------------------------------------------------------------------------
1 | global:
2 | checkNewVersion: false
3 | sendAnonymousUsage: false
4 |
5 | log:
6 | level: "{{ traefik_log_level }}"
7 | format: "{{ traefik_log_format }}"
8 | filePath: traefik.log
9 | {% if traefik_dashboard_url|length > 0 %}
10 | api:
11 | dashboard: true
12 | {% endif %}
13 | entryPoints:
14 | web:
15 | address: :80
16 | http:
17 | redirections:
18 | entryPoint:
19 | to: websecure
20 | scheme: https
21 | websecure:
22 | address: :443
23 | http:
24 | tls:
25 | certResolver: le
26 | domains:
27 | - main: "{{ project_domain }}"
28 | {% if acme_dns_provider %}
29 | sans:
30 | - "*.{{ project_domain }}"
31 | {% endif %}
32 |
33 | certificatesResolvers:
34 | le:
35 | acme:
36 | email: "{{ acme_email }}"
37 | storage: acme.json
38 | {% if acme_dns_provider %}
39 | dnsChallenge:
40 | provider: "{{ acme_dns_provider }}"
41 | delayBeforeCheck: 5
42 | resolvers:
43 | - 1.1.1.1:53
44 | - 8.8.8.8:53
45 | {% else %}
46 | caServer: https://acme-{% if acme_staging == 'yes' %}staging-{% endif %}v02.api.letsencrypt.org/directory
47 | {% endif %}
48 |
49 | providers:
50 | docker:
51 | network: "{{ docker_network }}"
52 | exposedByDefault: false
53 |
--------------------------------------------------------------------------------
/pkg/registry/templates/hosts.ini.tmpl:
--------------------------------------------------------------------------------
1 | # Ansible inventory for deploy the Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | [cgapp_project]
6 | 127.0.0.1 # CHANGE THIS TO YOUR REMOTE SERVER IP!
7 |
8 | [cgapp_project:vars]
9 | #
10 | # Ansible default variables to start playbook:
11 | #
12 |
13 | # Set remote sudo username
14 | ansible_user=root
15 | # Ask become password for remote sudo user
16 | ansible_become=yes
17 | # Set connection type to remote server (usually, 'ssh')
18 | ansible_connection=ssh
19 | # Set Python 3 default path
20 | ansible_python_interpreter=/usr/bin/python3
21 |
22 | #
23 | # Remote server configuration:
24 | #
25 |
26 | # Set directory on your remote server for store project files
27 | server_dir=/var/www/cgapp
28 | # Set user (owner of files/folders) name
29 | server_user=root
30 | # Set group name
31 | server_group=docker
32 |
33 | #
34 | # Project configuration:
35 | #
36 |
37 | # Set your project domain
38 | project_domain=example.com
39 |
40 | #
41 | # Docker configuration:
42 | #
43 |
44 | # Set Docker network name
45 | docker_network=cgapp_network
46 |
47 | #
48 | # Backend configuration:
49 | #
50 |
51 | # Set backend port number,
52 | # MUST BE MATCH to the port that is listed in your `./backend/.env` file!
53 | backend_port=5000
54 |
55 | #
56 | # PostgreSQL configuration (backend DB):
57 | #
58 |
59 | # Set PostgreSQL container name
60 | # MUST BE MATCH to the port that is listed in your `./backend/.env` file!
61 | postgres_container_name=cgapp-postgres
62 | # Set PostgreSQL version (for example, 13.2)
63 | postgres_version=latest
64 | # Set PostgreSQL port number,
65 | # MUST BE MATCH to the port that is listed in your `./backend/.env` file!
66 | postgres_port=5432
67 | # Set PostgreSQL user name,
68 | # MUST BE MATCH to the user name that is listed in your `./backend/.env` file!
69 | postgres_user=postgres
70 | # Set PostgreSQL password,
71 | # MUST BE MATCH to the password that is listed in your `./backend/.env` file!
72 | postgres_password=password
73 | # Set PostgreSQL DB name,
74 | # MUST BE MATCH to the DB name that is listed in your `./backend/.env` file!
75 | postgres_db=postgres
76 | # Set PostgreSQL SSL mode state ('enabled' or 'disabled'),
77 | # MUST BE MATCH to the SSL mode state that is listed in your `./backend/.env` file!
78 | postgres_ssl_mode=disable
79 |
80 | #
81 | # Go-Migrate configuration (DB migrations):
82 | #
83 |
84 | # Set migration number,
85 | # if you WANT to migrate all of your migrations, just leave it blank,
86 | # for only initial migration set it to '1'
87 | migrate_number=1
88 |
89 | #
90 | # Redis configuration (backend cache):
91 | #
92 |
93 | # Set Redis container name
94 | # MUST BE MATCH to the port that is listed in your `./backend/.env` file!
95 | redis_container_name=cgapp-redis
96 | # Set Redis version (for example, 6.2)
97 | redis_version=latest
98 | # Set Redis port number,
99 | # MUST BE MATCH to the port that is listed in your `./backend/.env` file!
100 | redis_port=6379
101 |
102 | {{ if eq .Proxy "traefik" }}#
103 | # Traefik Proxy configuration:
104 | #
105 |
106 | # Set Traefik version (for example, v2.4)
107 | traefik_version=latest
108 | # Set log level in capital letters (DEBUG, PANIC, FATAL, ERROR, WARN, INFO)
109 | traefik_log_level=ERROR
110 | # Set log format between json and common
111 | traefik_log_format=json
112 | # Set URL for Traefik admin dashboard,
113 | # if you DO NOT NEED dashboard (Web UI), just leave it blank
114 | traefik_dashboard_url=cp.example.com
115 | # Set username for Traefik admin dashboard,
116 | # DO NOT FORGET to set this setting, if `traefik_dashboard_url` variable IS NOT empty!
117 | traefik_dashboard_user=admin
118 | # Set hashed password for Traefik admin dashboard,
119 | # DO NOT FORGET to set this setting, if `traefik_dashboard_url` variable IS NOT empty!
120 | # See: https://doc.traefik.io/traefik/middlewares/basicauth/#users
121 | traefik_dashboard_password=admin:$$apr1$$WpxRpfMZ$$TMTfGB37C9xAHiPIDiFiB1
122 |
123 | #
124 | # ACME configuration (Let's Encrypt):
125 | #
126 |
127 | # Set your email address for SSL cert
128 | acme_email=mail@example.com
129 | {{ if .Wildcard }}
130 | # You choose a DNS way to getting SSL cert,
131 | # DO NOT FORGET to set this variable here as well as in `./roles/traefik/tasks/main.yml` file!
132 |
133 | # Set DNS provider
134 | # See: https://doc.traefik.io/traefik/https/acme/#providers
135 | acme_dns_provider=digitalocean
136 | # Set environment variable(s) for a DNS provider,
137 | # for example, auth token for DigitalOcean
138 | do_auth_token=XXXXXXXXXXX
139 | {{ else }}# Change to 'no' for getting a real SSL cert
140 | acme_staging=yes{{ end }}
141 | {{ else if eq .Proxy "nginx" }}#
142 | # Nginx configuration:
143 | #
144 |
145 | # Set Nginx version (for example, 1.20-alpine)
146 | nginx_version=alpine
147 | # Set redirect from HTTP to HTTPS for default server
148 | nginx_use_only_https=yes
149 | # Set redirect from WWW to non-WWW domain for default server
150 | nginx_redirect_to_non_www=yes{{ end }}
151 |
--------------------------------------------------------------------------------
/pkg/registry/templates/playbook.yml.tmpl:
--------------------------------------------------------------------------------
1 | # Ansible playbook for deploy the Create Go App project.
2 | # Author: Vic Shóstak (https://github.com/koddr)
3 | # For more information, please visit https://github.com/create-go-app/cli
4 |
5 | ---
6 | #
7 | # Playbook for deploy.
8 | #
9 | - name: Deploy the Create Go App project
10 | hosts: cgapp_project
11 |
12 | #
13 | # List of all roles.
14 | #
15 | roles:
16 | - { role: docker, tags: [docker] }
17 | - { role: backend, tags: [backend] }
18 | - { role: redis, tags: [redis] }
19 | - { role: postgres, tags: [postgres] }
20 | {{ if ne .Proxy "none" }} - { role: {{.Proxy}}, tags: [{{.Proxy}}] }{{ end }}
21 |
--------------------------------------------------------------------------------