├── .github ├── labeler.yml └── workflows │ ├── build.yml │ ├── label.yml │ ├── linter.yml.disabled │ └── release.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── COMMANDS.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── VERSIONING.md ├── box ├── .gitignore ├── box.go ├── gen.go └── resources │ ├── VERSION │ ├── init │ ├── .gitignore │ ├── clencli │ │ ├── aws-well-architected.tmpl │ │ ├── aws-well-architected.yaml │ │ ├── hld.tmpl │ │ ├── hld.yaml │ │ ├── readme.tmpl │ │ └── readme.yaml │ └── project │ │ └── type │ │ ├── clouformation │ │ ├── skeleton.json │ │ └── skeleton.yaml │ │ └── terraform │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── environments │ │ ├── dev.tf │ │ └── prod.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── manual │ ├── configure.yaml │ ├── gitignore.yaml │ ├── init.yaml │ ├── render.yaml │ ├── root.yaml │ ├── unsplash.yaml │ └── version.yaml ├── clencli ├── logo.jpeg ├── readme.tmpl ├── readme.yaml └── unsplash.yaml ├── cobra ├── README.md ├── aid │ ├── configure.go │ ├── gitignore.go │ ├── init.go │ ├── render.go │ ├── root.go │ └── unsplash.go ├── cmd │ ├── configure.go │ ├── gitignore.go │ ├── init.go │ ├── render.go │ ├── root.go │ ├── unsplash.go │ └── version.go ├── controller │ ├── configure.go │ ├── gitignore.go │ ├── init.go │ ├── render.go │ ├── root.go │ ├── unsplash.go │ └── version.go ├── dao │ └── configure.go ├── model │ ├── configure.go │ ├── init.go │ ├── root.go │ └── unsplash.go └── view │ └── configure.go ├── go.mod ├── go.sum ├── helper ├── cobra.go ├── directories.go ├── files.go ├── manual.go └── strings.go ├── lib └── make │ ├── cobra │ └── Makefile │ ├── docker │ └── Makefile │ ├── github │ └── Makefile │ └── go │ └── Makefile ├── main.go ├── tests ├── cmd_gitignore_test.go ├── cmd_init_test.go ├── cmd_render_test.go ├── cmd_root_test.go ├── cmd_unsplash_test.go ├── cmd_version_test.go └── pkg_test.go └── unsplash.yaml /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | documentation: 2 | - docs/**/* 3 | - docs-src/**/* 4 | 5 | dependencies: 6 | - go.mod 7 | - go.sum 8 | - vendor/**/* 9 | 10 | build: 11 | - .github/workflows/* 12 | - Makefile 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push] 3 | jobs: 4 | linux-build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-go@v2 9 | with: 10 | go-version: '^1.16.4' 11 | - run: sudo apt-get install -y git 12 | - run: go version 13 | - run: go env 14 | - run: go get ./... 15 | - run: go generate ./... 16 | - name: go test 17 | run: cd tests && go test pkg_test.go cmd_root_test.go cmd_init_test.go cmd_render_test.go cmd_version_test.go 18 | - name: go build 19 | run: mkdir bin && go build -o bin/clencli 20 | windows-build: 21 | runs-on: windows-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: actions/setup-go@v2 25 | with: 26 | go-version: '^1.16.4' 27 | - run: go version 28 | - run: go env 29 | - run: go get ./... 30 | - run: go generate ./... 31 | - name: go test 32 | run: cd tests && go test pkg_test.go cmd_root_test.go cmd_init_test.go cmd_render_test.go cmd_version_test.go 33 | - name: go build 34 | run: New-Item -Path "." -Name "bin" -ItemType "directory" && go build -o bin/clencli -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | name: Labeler 2 | on: [pull_request] 3 | 4 | jobs: 5 | label: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/labeler@v2 9 | with: 10 | repo-token: "${{ secrets.GITHUB_TOKEN }}" -------------------------------------------------------------------------------- /.github/workflows/linter.yml.disabled: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | # Run this workflow every time a new commit pushed to the repository 4 | on: [pull_request] 5 | 6 | jobs: 7 | # Set the job key. The key is displayed as the job name when a job name is not provided 8 | super-lint: 9 | # Name the Job 10 | name: Lint Code Base 11 | # Set the type of machine to run on 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | # Checks out a copy of your repository on the ubuntu-latest machine 16 | - name: Checkout code 17 | uses: actions/checkout@v2 18 | 19 | # Runs the Super-Linter action 20 | - name: Lint Code Base 21 | uses: github/super-linter@v3 22 | env: 23 | DEFAULT_BRANCH: main 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | # Sequence of patterns matched against refs/tags 5 | tags: 6 | - '*' # Push events to matching v*, i.e. v1.0, v20.15.10 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-go@v2 13 | with: 14 | go-version: '^1.15.2' # The Go version to download (if necessary) and use. 15 | - run: go version 16 | - run: go env 17 | - run: go get ./... 18 | - run: go generate ./... 19 | - run: make clencli/compile 20 | - name: Create Release 21 | id: create_release 22 | uses: actions/create-release@v1 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 25 | with: 26 | tag_name: ${{ github.ref }} 27 | release_name: Release ${{ github.ref }} 28 | body_path: CHANGELOG.md 29 | draft: false # true to create a draft (unpublished) release, false to create a published one. Default: false 30 | prerelease: false # true to identify the release as a prerelease. false to identify the release as a full release. Default: false 31 | - name: Upload Release Asset (clencli-darwin-amd64) 32 | id: upload-release-asset-clencli-darwin-amd64 33 | uses: actions/upload-release-asset@v1 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | with: 37 | upload_url: ${{ steps.create_release.outputs.upload_url }} 38 | asset_path: ./dist/clencli-darwin-amd64 39 | asset_name: clencli-darwin-amd64 40 | asset_content_type: application/x-clencli 41 | - name: Upload Release Asset (clencli-solaris-amd64) 42 | id: upload-release-asset-clencli-solaris-amd64 43 | uses: actions/upload-release-asset@v1 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | with: 47 | upload_url: ${{ steps.create_release.outputs.upload_url }} 48 | asset_path: ./dist/clencli-solaris-amd64 49 | asset_name: clencli-solaris-amd64 50 | asset_content_type: application/x-clencli 51 | - name: Upload Release Asset (clencli-freebsd-386) 52 | id: upload-release-asset-clencli-freebsd-386 53 | uses: actions/upload-release-asset@v1 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | with: 57 | upload_url: ${{ steps.create_release.outputs.upload_url }} 58 | asset_path: ./dist/clencli-freebsd-386 59 | asset_name: clencli-freebsd-386 60 | asset_content_type: application/x-clencli 61 | - name: Upload Release Asset (clencli-freebsd-amd64) 62 | id: upload-release-asset-clencli-freebsd-amd64 63 | uses: actions/upload-release-asset@v1 64 | env: 65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 66 | with: 67 | upload_url: ${{ steps.create_release.outputs.upload_url }} 68 | asset_path: ./dist/clencli-freebsd-amd64 69 | asset_name: clencli-freebsd-amd64 70 | asset_content_type: application/x-clencli 71 | - name: Upload Release Asset (clencli-freebsd-arm) 72 | id: upload-release-asset-clencli-freebsd-arm 73 | uses: actions/upload-release-asset@v1 74 | env: 75 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 76 | with: 77 | upload_url: ${{ steps.create_release.outputs.upload_url }} 78 | asset_path: ./dist/clencli-freebsd-arm 79 | asset_name: clencli-freebsd-arm 80 | asset_content_type: application/x-clencli 81 | - name: Upload Release Asset (clencli-openbsd-386) 82 | id: upload-release-asset-clencli-openbsd-386 83 | uses: actions/upload-release-asset@v1 84 | env: 85 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 86 | with: 87 | upload_url: ${{ steps.create_release.outputs.upload_url }} 88 | asset_path: ./dist/clencli-openbsd-386 89 | asset_name: clencli-openbsd-386 90 | asset_content_type: application/x-clencli 91 | - name: Upload Release Asset (clencli-openbsd-amd64) 92 | id: upload-release-asset-clencli-openbsd-amd64 93 | uses: actions/upload-release-asset@v1 94 | env: 95 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 96 | with: 97 | upload_url: ${{ steps.create_release.outputs.upload_url }} 98 | asset_path: ./dist/clencli-openbsd-amd64 99 | asset_name: clencli-openbsd-amd64 100 | asset_content_type: application/x-clencli 101 | - name: Upload Release Asset (clencli-openbsd-arm) 102 | id: upload-release-asset-clencli-openbsd-arm 103 | uses: actions/upload-release-asset@v1 104 | env: 105 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 106 | with: 107 | upload_url: ${{ steps.create_release.outputs.upload_url }} 108 | asset_path: ./dist/clencli-openbsd-arm 109 | asset_name: clencli-openbsd-arm 110 | asset_content_type: application/x-clencli 111 | - name: Upload Release Asset (clencli-linux-386) 112 | id: upload-release-asset-clencli-linux-386 113 | uses: actions/upload-release-asset@v1 114 | env: 115 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 116 | with: 117 | upload_url: ${{ steps.create_release.outputs.upload_url }} 118 | asset_path: ./dist/clencli-linux-386 119 | asset_name: clencli-linux-386 120 | asset_content_type: application/x-clencli 121 | - name: Upload Release Asset (clencli-linux-amd64) 122 | id: upload-release-asset-clencli-linux-amd64 123 | uses: actions/upload-release-asset@v1 124 | env: 125 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 126 | with: 127 | upload_url: ${{ steps.create_release.outputs.upload_url }} 128 | asset_path: ./dist/clencli-linux-amd64 129 | asset_name: clencli-linux-amd64 130 | asset_content_type: application/x-clencli 131 | - name: Upload Release Asset (clencli-linux-arm) 132 | id: upload-release-asset-clencli-linux-arm 133 | uses: actions/upload-release-asset@v1 134 | env: 135 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 136 | with: 137 | upload_url: ${{ steps.create_release.outputs.upload_url }} 138 | asset_path: ./dist/clencli-linux-arm 139 | asset_name: clencli-linux-arm 140 | asset_content_type: application/x-clencli 141 | - name: Upload Release Asset (clencli-windows-386) 142 | id: upload-release-asset-clencli-windows-386 143 | uses: actions/upload-release-asset@v1 144 | env: 145 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 146 | with: 147 | upload_url: ${{ steps.create_release.outputs.upload_url }} 148 | asset_path: ./dist/clencli-windows-386.exe 149 | asset_name: clencli-windows-386.exe 150 | asset_content_type: application/x-clencli 151 | - name: Upload Release Asset (clencli-windows-amd64) 152 | id: upload-release-asset-clencli-windows-amd64 153 | uses: actions/upload-release-asset@v1 154 | env: 155 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 156 | with: 157 | upload_url: ${{ steps.create_release.outputs.upload_url }} 158 | asset_path: ./dist/clencli-windows-amd64.exe 159 | asset_name: clencli-windows-amd64.exe 160 | asset_content_type: application/x-clencli -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/go,java,linux,macos,python,windows,virtualenv,visualstudiocode 3 | # Edit at https://www.gitignore.io/?templates=go,java,linux,macos,python,windows,virtualenv,visualstudiocode 4 | 5 | ### Go ### 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | ### Go Patch ### 23 | /vendor/ 24 | /Godeps/ 25 | 26 | ### Java ### 27 | # Compiled class file 28 | *.class 29 | 30 | # Log file 31 | *.log 32 | 33 | # BlueJ files 34 | *.ctxt 35 | 36 | # Mobile Tools for Java (J2ME) 37 | .mtj.tmp/ 38 | 39 | # Package Files # 40 | *.jar 41 | *.war 42 | *.nar 43 | *.ear 44 | *.zip 45 | *.tar.gz 46 | *.rar 47 | 48 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 49 | hs_err_pid* 50 | 51 | ### Linux ### 52 | *~ 53 | 54 | # temporary files which can be created if a process still has a handle open of a deleted file 55 | .fuse_hidden* 56 | 57 | # KDE directory preferences 58 | .directory 59 | 60 | # Linux trash folder which might appear on any partition or disk 61 | .Trash-* 62 | 63 | # .nfs files are created when an open file is removed but is still being accessed 64 | .nfs* 65 | 66 | ### macOS ### 67 | # General 68 | .DS_Store 69 | .AppleDouble 70 | .LSOverride 71 | 72 | # Icon must end with two \r 73 | Icon 74 | 75 | # Thumbnails 76 | ._* 77 | 78 | # Files that might appear in the root of a volume 79 | .DocumentRevisions-V100 80 | .fseventsd 81 | .Spotlight-V100 82 | .TemporaryItems 83 | .Trashes 84 | .VolumeIcon.icns 85 | .com.apple.timemachine.donotpresent 86 | 87 | # Directories potentially created on remote AFP share 88 | .AppleDB 89 | .AppleDesktop 90 | Network Trash Folder 91 | Temporary Items 92 | .apdisk 93 | 94 | ### Python ### 95 | # Byte-compiled / optimized / DLL files 96 | __pycache__/ 97 | *.py[cod] 98 | *$py.class 99 | 100 | # C extensions 101 | 102 | # Distribution / packaging 103 | .Python 104 | build/ 105 | develop-eggs/ 106 | dist/ 107 | downloads/ 108 | eggs/ 109 | .eggs/ 110 | lib64/ 111 | parts/ 112 | sdist/ 113 | var/ 114 | wheels/ 115 | pip-wheel-metadata/ 116 | share/python-wheels/ 117 | *.egg-info/ 118 | .installed.cfg 119 | *.egg 120 | MANIFEST 121 | 122 | # PyInstaller 123 | # Usually these files are written by a python script from a template 124 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 125 | *.manifest 126 | *.spec 127 | 128 | # Installer logs 129 | pip-log.txt 130 | pip-delete-this-directory.txt 131 | 132 | # Unit test / coverage reports 133 | htmlcov/ 134 | .tox/ 135 | .nox/ 136 | .coverage 137 | .coverage.* 138 | .cache 139 | nosetests.xml 140 | coverage.xml 141 | *.cover 142 | .hypothesis/ 143 | .pytest_cache/ 144 | 145 | # Translations 146 | *.mo 147 | *.pot 148 | 149 | # Scrapy stuff: 150 | .scrapy 151 | 152 | # Sphinx documentation 153 | docs/_build/ 154 | 155 | # PyBuilder 156 | target/ 157 | 158 | # pyenv 159 | .python-version 160 | 161 | # pipenv 162 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 163 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 164 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 165 | # install all needed dependencies. 166 | #Pipfile.lock 167 | 168 | # celery beat schedule file 169 | celerybeat-schedule 170 | 171 | # SageMath parsed files 172 | *.sage.py 173 | 174 | # Spyder project settings 175 | .spyderproject 176 | .spyproject 177 | 178 | # Rope project settings 179 | .ropeproject 180 | 181 | # Mr Developer 182 | .mr.developer.cfg 183 | .project 184 | .pydevproject 185 | 186 | # mkdocs documentation 187 | /site 188 | 189 | # mypy 190 | .mypy_cache/ 191 | .dmypy.json 192 | dmypy.json 193 | 194 | # Pyre type checker 195 | .pyre/ 196 | 197 | ### VirtualEnv ### 198 | # Virtualenv 199 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 200 | pyvenv.cfg 201 | .env 202 | .venv 203 | env/ 204 | venv/ 205 | ENV/ 206 | env.bak/ 207 | venv.bak/ 208 | pip-selfcheck.json 209 | 210 | ### VisualStudioCode ### 211 | .vscode/* 212 | !.vscode/settings.json 213 | !.vscode/tasks.json 214 | !.vscode/launch.json 215 | !.vscode/extensions.json 216 | 217 | ### VisualStudioCode Patch ### 218 | # Ignore all local history of files 219 | .history 220 | 221 | ### Windows ### 222 | # Windows thumbnail cache files 223 | Thumbs.db 224 | Thumbs.db:encryptable 225 | ehthumbs.db 226 | ehthumbs_vista.db 227 | 228 | # Dump file 229 | *.stackdump 230 | 231 | # Folder config file 232 | [Dd]esktop.ini 233 | 234 | # Recycle Bin used on file shares 235 | $RECYCLE.BIN/ 236 | 237 | # Windows Installer files 238 | *.cab 239 | *.msi 240 | *.msix 241 | *.msm 242 | *.msp 243 | 244 | # Windows shortcuts 245 | *.lnk 246 | 247 | # End of https://www.gitignore.io/api/go,java,linux,macos,python,windows,virtualenv,visualstudiocode 248 | 249 | # Custom rules 250 | downloads/ 251 | generated-*/ 252 | bin/* 253 | dist/* 254 | # Ignore the examples folder (also used as a branch) 255 | examples/* 256 | test/* 257 | *debug_bin* 258 | clencli/log.json 259 | *.tmp* -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "d-configure", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "debug", 12 | "program": "${workspaceFolder}/main.go", 13 | "args": ["configure"] 14 | }, 15 | { 16 | "name": "d-configure-delete-profile", 17 | "type": "go", 18 | "request": "launch", 19 | "mode": "debug", 20 | "program": "${workspaceFolder}/main.go", 21 | "args": ["configure", "delete", "--profile", "dylan"] 22 | }, 23 | { 24 | "name": "d-init-help", 25 | "type": "go", 26 | "request": "launch", 27 | "mode": "debug", 28 | "program": "${workspaceFolder}/main.go", 29 | "args": ["init", "--help"] 30 | }, 31 | { 32 | "name": "d-init-project-basic", 33 | "type": "go", 34 | "request": "launch", 35 | "mode": "debug", 36 | "program": "${workspaceFolder}/main.go", 37 | "args": ["init", "project", "--name", "generated-basic-project"] 38 | }, 39 | { 40 | "name": "d-init-project-terraform", 41 | "type": "go", 42 | "request": "launch", 43 | "mode": "debug", 44 | "program": "${workspaceFolder}/main.go", 45 | "args": ["init", "project", "--name", "generated-terraform-project", "--type", "terraform"], 46 | }, 47 | { 48 | "name": "d-init-project-terraform-with-only-customized-structure", 49 | "type": "go", 50 | "request": "launch", 51 | "mode": "debug", 52 | "program": "${workspaceFolder}/main.go", 53 | "args": ["init", "project", "--name", "generated-terraform-project-only-customized-structure", "--type", "terraform", "-o"], 54 | }, 55 | { 56 | "name": "d-render-template", 57 | "type": "go", 58 | "request": "launch", 59 | "mode": "debug", 60 | "program": "${workspaceFolder}/main.go", 61 | "args": ["render", "template"], 62 | } 63 | ] 64 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build", 8 | "type": "shell", 9 | "command": "make clencli/build", 10 | "problemMatcher": [], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | } 15 | }, 16 | { 17 | "label": "render-template", 18 | "type": "shell", 19 | "command": [ 20 | "go", 21 | "run", 22 | "${workspaceFolder}/main.go", 23 | "render", 24 | "template" 25 | ], 26 | "problemMatcher": [], 27 | "group": "build" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## v0.3.6 4 | 5 | * Fixed render template command on Windows 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 2 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 3 | opensource-codeofconduct@amazon.com with any additional questions or comments. 4 | -------------------------------------------------------------------------------- /COMMANDS.md: -------------------------------------------------------------------------------- 1 | ## Commands 2 | ``` 3 | The Cloud Engineer CLI 4 | 5 | Usage: 6 | clencli [command] 7 | 8 | Available Commands: 9 | configure Configures CLENCLI global settings 10 | gitignore Download .gitignore based on the given input 11 | help Help about any command 12 | init Initialize a project 13 | render Render a template 14 | unsplash Downloads random photos from Unsplash.com 15 | version Displays the version of CLENCLI and all installed plugins 16 | 17 | Flags: 18 | -h, --help help for clencli 19 | --log Enable or disable logs (can be found at ./clencli/log.json). Log outputs will be redirected default output if disabled. (default true) 20 | --log-file-path string Log file path. Requires log=true, ignored otherwise. (default "clencli/log.json") 21 | -p, --profile string Use a specific profile from your credentials and configurations file. (default "default") 22 | -v, --verbosity string Valid log level:panic,fatal,error,warn,info,debug,trace). (default "error") 23 | 24 | Use "clencli [command] --help" for more information about a command. 25 | ``` 26 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ### Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ### Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ### Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ### Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ### Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ### Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | include lib/make/*/Makefile 4 | 5 | .PHONY: clencli/test 6 | clencli/test: clencli/build go/test 7 | 8 | .PHONY: clencli/build 9 | clencli/build: clencli/clean go/mod/tidy go/version go/fmt go/generate go/install clencli/update-readme ## Builds the app 10 | 11 | .PHONY: clencli/install 12 | clencli/install: go/get go/fmt go/generate go/install ## Builds the app and install all dependencies 13 | 14 | .PHONY: clencli/run 15 | clencli/run: go/fmt ## Run a command 16 | ifdef command 17 | make go/run command='$(command)' 18 | else 19 | make go/run 20 | endif 21 | 22 | .PHONY: clencli/compile 23 | clencli/compile: ## Compile to multiple architectures 24 | @mkdir -p dist 25 | @echo "Compiling for every OS and Platform" 26 | GOOS=darwin GOARCH=amd64 go build -o dist/clencli-darwin-amd64 main.go 27 | GOOS=solaris GOARCH=amd64 go build -o dist/clencli-solaris-amd64 main.go 28 | 29 | GOOS=freebsd GOARCH=386 go build -o dist/clencli-freebsd-386 main.go 30 | GOOS=freebsd GOARCH=amd64 go build -o dist/clencli-freebsd-amd64 main.go 31 | GOOS=freebsd GOARCH=arm go build -o dist/clencli-freebsd-arm main.go 32 | 33 | GOOS=openbsd GOARCH=386 go build -o dist/clencli-openbsd-386 main.go 34 | GOOS=openbsd GOARCH=amd64 go build -o dist/clencli-openbsd-amd64 main.go 35 | GOOS=openbsd GOARCH=arm go build -o dist/clencli-openbsd-arm main.go 36 | 37 | GOOS=linux GOARCH=386 go build -o dist/clencli-linux-386 main.go 38 | GOOS=linux GOARCH=amd64 go build -o dist/clencli-linux-amd64 main.go 39 | GOOS=linux GOARCH=arm go build -o dist/clencli-linux-arm main.go 40 | 41 | GOOS=windows GOARCH=386 go build -o dist/clencli-windows-386.exe main.go 42 | GOOS=windows GOARCH=amd64 go build -o dist/clencli-windows-amd64.exe main.go 43 | 44 | .PHONY: clencli/clean 45 | clencli/clean: ## Removes unnecessary files and directories 46 | rm -rf downloads/ 47 | rm -rf generated-*/ 48 | rm -rf dist/ 49 | rm -rf build/ 50 | rm -f box/blob.go 51 | 52 | #rm -f $$GOPATH/bin/clencli 53 | 54 | .PHONY: clencli/update-readme 55 | clencli/update-readme: ## Renders template readme.tmpl with additional documents 56 | @echo "Updating README.tmpl to the latest version" 57 | @cp box/resources/init/clencli/readme.tmpl clencli/readme.tmpl 58 | @echo "Generate COMMANDS.md" 59 | @echo "## Commands" > COMMANDS.md 60 | @echo '```' >> COMMANDS.md 61 | @clencli --help >> COMMANDS.md 62 | @echo '```' >> COMMANDS.md 63 | @echo "COMMANDS.md generated successfully" 64 | @clencli render template --name readme 65 | 66 | # .PHONY: clencli/test 67 | # clencli/test: go/test 68 | 69 | .DEFAULT_GOAL := help 70 | 71 | .PHONY: help 72 | help: ## This HELP message 73 | @fgrep -h ": ##" $(MAKEFILE_LIST) | sed -e 's/\(\:.*\#\#\)/\:\ /' | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//' 74 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | ![Photo by [Felipe Dias](https://unsplash.com/fdiascreator) on [Unsplash](https://unsplash.com)](clencli/logo.jpeg) 16 | 17 | > Photo by [Felipe Dias](https://unsplash.com/fdiascreator) on [Unsplash](https://unsplash.com) 18 | 19 | 20 | [![GitHub issues](https://img.shields.io/github/issues/awslabs/clencli)](https://github.com/awslabs/clencli/issues)[![GitHub forks](https://img.shields.io/github/forks/awslabs/clencli)](https://github.com/awslabs/clencli/network)[![GitHub stars](https://img.shields.io/github/stars/awslabs/clencli)](https://github.com/awslabs/clencli/stargazers)[![GitHub license](https://img.shields.io/github/license/awslabs/clencli)](https://github.com/awslabs/clencli/blob/master/LICENSE)[![Twitter](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fawslabs%2Fclencli)](https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fawslabs%2Fclencli) 21 | 22 | # Cloud Engineer CLI ( clencli ) 23 | 24 | A CLI built to assist Cloud Engineers. 25 | 26 | ## Table of Contents 27 | --- 28 | 29 | 30 | 31 | 32 | - [Usage](#usage) 33 | 34 | - [Installing](#installing) 35 | 36 | 37 | - [Acknowledgments](#acknowledgments) 38 | - [Contributors](#contributors) 39 | - [References](#references) 40 | - [License](#license) 41 | - [Copyright](#copyright) 42 | 43 | 44 | 45 | 46 | ## Usage 47 | --- 48 |
49 | Expand 50 | 51 | In a polyglot world where a team can choose it's programming language, often this flexibility can spill into chaos as every repo looks different. 52 | CLENCLI solves this issue by giving developers a quick and easy way to create a standardised repo structure and easily rendering documentation via a YAML file. 53 | 54 | ### Create a new project 55 | ``` 56 | $ clencli init project --project-name foo 57 | $ tree -a moon/ 58 | foo/ 59 | ├── clencli 60 | │   ├── readme.tmpl 61 | │   └── readme.yaml 62 | └── .gitignore 63 | ``` 64 | 65 | ### Create a new CloudFormation project 66 | ``` 67 | $ clencli init project --project-name foo --project-type cloudformation 68 | $ tree -a sun/ 69 | foo/ 70 | ├── clencli 71 | │   ├── hld.tmpl 72 | │   ├── hld.yaml 73 | │   ├── readme.tmpl 74 | │   └── readme.yaml 75 | ├── environments 76 | │   ├── dev 77 | │   └── prod 78 | ├── .gitignore 79 | ├── skeleton.json 80 | └── skeleton.yaml 81 | ``` 82 | 83 | ### Create a new Terraform project 84 | ``` 85 | $ clencli init project --project-name foo --project-type terraform 86 | $ tree -a foo/ 87 | foo/ 88 | ├── clencli 89 | │   ├── hld.tmpl 90 | │   ├── hld.yaml 91 | │   ├── readme.tmpl 92 | │   └── readme.yaml 93 | ├── environments 94 | │   ├── dev.tf 95 | │   └── prod.tf 96 | ├── .gitignore 97 | ├── LICENSE 98 | ├── main.tf 99 | ├── Makefile 100 | ├── outputs.tf 101 | └── variables.tf 102 | ``` 103 | 104 | ## Render a template 105 | ``` 106 | $ clencli init project --project-name foo 107 | foo was successfully initialized as a basic project 108 | $ cd foo/ 109 | $ clencli render template 110 | Template readme.tmpl rendered as README.md 111 | ``` 112 | 113 | The `README.md` you are reading right now was generated and it's maintained by `CLENCLI` itself. 114 | Please check [readme.yaml](clencli/readme.yaml) for more details. 115 | 116 | ## Download a .gitignore for your project 117 | ``` 118 | $ clencli gitignore --input="terraform,visualstudiocode" 119 | .gitignore created successfully 120 | $ less .gitignore 121 | 122 | # Created by https://www.toptal.com/developers/gitignore/api/terraform,visualstudiocode 123 | # Edit at https://www.toptal.com/developers/gitignore?templates=terraform,visualstudiocode 124 | 125 | ### Terraform ### 126 | # Local .terraform directories 127 | **/.terraform/* 128 | 129 | # .tfstate files 130 | *.tfstate 131 | *.tfstate.* 132 | 133 | # Crash log files 134 | crash.log 135 | 136 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 137 | # .tfvars files are managed as part of configuration and so should be included in 138 | # version control. 139 | # 140 | # example.tfvars 141 | 142 | # Ignore override files as they are usually used to override resources locally and so 143 | # are not checked in 144 | override.tf 145 | override.tf.json 146 | *_override.tf 147 | *_override.tf.json 148 | 149 | # Include override files you do wish to add to version control using negated pattern 150 | # !example_override.tf 151 | 152 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 153 | # example: *tfplan* 154 | 155 | ### VisualStudioCode ### 156 | .vscode/* 157 | !.vscode/settings.json 158 | !.vscode/tasks.json 159 | !.vscode/launch.json 160 | !.vscode/extensions.json 161 | *.code-workspace 162 | 163 | ### VisualStudioCode Patch ### 164 | # Ignore all local history of files 165 | .history 166 | .ionide 167 | 168 | # End of https://www.toptal.com/developers/gitignore/api/terraform,visualstudiocode 169 | 170 | ``` 171 | Additionally, you can also *customize the initialization* of your projects (scaffolding) and download photos for your projects from [unsplash](https://unsplash.com), please read more [here](https://github.com/awslabs/clencli/wiki/Configuration). 172 |
173 | 174 | 175 | 176 | 177 | 178 | ## Installing 179 | --- 180 |
181 | Expand 182 | 183 | Download the latest version [released](https://github.com/awslabs/clencli/releases) according to your platform and execute it directly. I recommend placing the binary into your `$PATH`, so it's easily accessible. 184 |
185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | ## Commands 195 | ``` 196 | The Cloud Engineer CLI 197 | 198 | Usage: 199 | clencli [command] 200 | 201 | Available Commands: 202 | configure Configures CLENCLI global settings 203 | gitignore Download .gitignore based on the given input 204 | help Help about any command 205 | init Initialize a project 206 | render Render a template 207 | unsplash Downloads random photos from Unsplash.com 208 | version Displays the version of CLENCLI and all installed plugins 209 | 210 | Flags: 211 | -h, --help help for clencli 212 | --log Enable or disable logs (can be found at ./clencli/log.json). Log outputs will be redirected default output if disabled. (default true) 213 | --log-file-path string Log file path. Requires log=true, ignored otherwise. (default "clencli/log.json") 214 | -p, --profile string Use a specific profile from your credentials and configurations file. (default "default") 215 | -v, --verbosity string Valid log level:panic,fatal,error,warn,info,debug,trace). (default "error") 216 | 217 | Use "clencli [command] --help" for more information about a command. 218 | ``` 219 | 220 | 221 | 222 | 223 | ## Contributors 224 | --- 225 |
226 | Expand 227 | 228 | | Name | Email | Role | 229 | |:------------:|:--------------------:|:---------------:| 230 | | Silva, Valter | valterh@amazon.com | AWS Professional Services - Cloud Architect | 231 | 232 |
233 | 234 | 235 | 236 | ## Acknowledgments 237 | --- 238 |
239 | Expand 240 | 241 | Gratitude for assistance: 242 | * Sia, William - AWS Professional Service - Senior Cloud Architect 243 | * Dhingra, Prashit - AWS Professional Service - Cloud Architect 244 | 245 | 246 |
247 | 248 | 249 | 250 | ## References 251 | --- 252 |
253 | Expand 254 | 255 | * [cobra](https://github.com/spf13/cobra) - Cobra is both a library for creating powerful modern CLI applications as well as a program to generate applications and command files. 256 | * [viper](https://github.com/spf13/viper) - Viper is a complete configuration solution for Go applications including 12-Factor apps. 257 | * [twelve-factor-app](https://12factor.net) - The Twelve-Factor App 258 | * [gomplate](https://github.com/hairyhenderson/gomplate) - gomplate is a template renderer which supports a growing list of datasources, such as JSON (including EJSON - encrypted JSON), YAML, AWS EC2 metadata, BoltDB, Hashicorp Consul and Hashicorp Vault secrets. 259 | * [unsplash](https://unsplash.com) - The most powerful photo engine in the world. 260 | * [placeholder](https://placeholder.com) - The Free Image Placeholder Service Favoured By Designers 261 | * [pirate-ipsum](https://pirateipsum.me) - The best Lorem Ipsum Generator in all the sea 262 | * [recordit](https://recordit.co) - Record Fast Screencasts 263 | * [ttystudio](https://github.com/chjj/ttystudio) - A terminal-to-gif recorder minus the headaches. 264 | * [gihub-super-linter](https://github.com/github/super-linter) - GitHub Super Linter 265 | * [github-actions](https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/introduction-to-github-actions) - GitHub Actions 266 | * [gitignore.io](https://www.toptal.com/developers/gitignore) - Create useful .gitignore files for your project 267 | 268 | 269 |
270 | 271 | 272 | 273 | ## License 274 | --- 275 | This project is licensed under the Apache License 2.0. 276 | 277 | For more information please read [LICENSE](LICENSE). 278 | 279 | 280 | 281 | ## Copyright 282 | --- 283 | ``` 284 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 285 | ``` 286 | 287 | -------------------------------------------------------------------------------- /VERSIONING.md: -------------------------------------------------------------------------------- 1 | ## Branching strategy 2 | This project has adopted [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) for its branching strategy model. 3 | 4 | ## Release process 5 | This project has adopted [Nebula Release Plugin]() for the release process. 6 | 7 | ### Explanation 8 | 9 | `final` - Sets the version to the appropriate `..`, creates tag `v..` 10 | 11 | `candidate` - Sets the version to the appropriate `..-rc.#`, creates tag `v..-rc.#` where `#` is the number of release candidates for this version produced so far. 1st 1.0.0 will be 1.0.0-rc.1, 2nd 1.0.0-rc.2 and so on. 12 | 13 | `devSnapshot` - Sets the version to the appropriate `..-dev.#+`, does not create a tag. Where `#` is the number of commits since the last release and hash is the git hash of the current commit. If releasing a `devSnapshot` from a branch not listed in the releaseBranchPatterns and not excluded by excludeBranchPatterns the version will be `..-dev.#+.` 14 | You can use nebula.release.features.replaceDevWithImmutableSnapshot=true in your gradle.properties file to change pattern of version to ..-snapshot.+. Where timestamp is UTC time in YYYYMMddHHmm format, ex. 201907052105 and hash is the git hash of the current commit. If releasing a immutableSnapshot from a branch not listed in the releaseBranchPatterns and not excluded by excludeBranchPatterns the version will be `..-snapshot.+.` 15 | -------------------------------------------------------------------------------- /box/.gitignore: -------------------------------------------------------------------------------- 1 | blob.go -------------------------------------------------------------------------------- /box/box.go: -------------------------------------------------------------------------------- 1 | //go:generate go run gen.go 2 | 3 | package box 4 | 5 | type resourceBox struct { 6 | storage map[string][]byte 7 | } 8 | 9 | func newResourceBox() *resourceBox { 10 | return &resourceBox{storage: make(map[string][]byte)} 11 | } 12 | 13 | // Find for a file 14 | func (r *resourceBox) Has(file string) bool { 15 | if _, ok := r.storage[file]; ok { 16 | return true 17 | } 18 | return false 19 | } 20 | 21 | // Get file's content 22 | // Always use / for looking up 23 | // For example: /init/README.md is actually resources/init/README.md 24 | func (r *resourceBox) Get(file string) ([]byte, bool) { 25 | if f, ok := r.storage[file]; ok { 26 | return f, ok 27 | } 28 | return nil, false 29 | } 30 | 31 | // Add a file to box 32 | func (r *resourceBox) Add(file string, content []byte) { 33 | r.storage[file] = content 34 | } 35 | 36 | // Resource expose 37 | var resources = newResourceBox() 38 | 39 | // Get a file from box 40 | func Get(file string) ([]byte, bool) { 41 | return resources.Get(file) 42 | } 43 | 44 | // Add a file content to box 45 | func Add(file string, content []byte) { 46 | resources.Add(file, content) 47 | } 48 | 49 | // Has a file in box 50 | func Has(file string) bool { 51 | return resources.Has(file) 52 | } 53 | -------------------------------------------------------------------------------- /box/gen.go: -------------------------------------------------------------------------------- 1 | //+build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "go/format" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | "text/template" 15 | ) 16 | 17 | const blob = "blob.go" 18 | 19 | var packageTemplate = template.Must(template.New("").Funcs(map[string]interface{}{"conv": FormatByteSlice}).Parse(`// Code generated by go generate; DO NOT EDIT. 20 | // generated using files from resources directory 21 | // DO NOT COMMIT this file 22 | package box 23 | 24 | func init(){ 25 | {{- range $name, $file := . }} 26 | resources.Add("{{ $name }}", []byte{ {{ conv $file }} }) 27 | {{- end }} 28 | } 29 | `)) 30 | 31 | func FormatByteSlice(sl []byte) string { 32 | builder := strings.Builder{} 33 | for _, v := range sl { 34 | builder.WriteString(fmt.Sprintf("%d,", int(v))) 35 | } 36 | return builder.String() 37 | } 38 | 39 | func main() { 40 | log.Println("Baking resources... \U0001F4E6") 41 | 42 | if _, err := os.Stat("resources"); os.IsNotExist(err) { 43 | log.Fatal("Resources directory does not exists") 44 | } 45 | 46 | resources := make(map[string][]byte) 47 | err := filepath.Walk("resources", func(path string, info os.FileInfo, err error) error { 48 | if err != nil { 49 | log.Println("Error :", err) 50 | return err 51 | } 52 | relativePath := filepath.ToSlash(strings.TrimPrefix(path, "resources")) 53 | if info.IsDir() { 54 | log.Println(path, "is a directory, skipping... \U0001F47B") 55 | return nil 56 | } else { 57 | log.Println(path, "is a file, baking in... \U0001F31F") 58 | b, err := ioutil.ReadFile(path) 59 | if err != nil { 60 | log.Printf("Error reading %s: %s", path, err) 61 | return err 62 | } 63 | resources[relativePath] = b 64 | } 65 | return nil 66 | }) 67 | 68 | if err != nil { 69 | log.Fatal("Error walking through resources directory:", err) 70 | } 71 | 72 | f, err := os.Create(blob) 73 | if err != nil { 74 | log.Fatal("Error creating blob file:", err) 75 | } 76 | defer f.Close() 77 | 78 | builder := &bytes.Buffer{} 79 | 80 | err = packageTemplate.Execute(builder, resources) 81 | if err != nil { 82 | log.Fatal("Error executing template", err) 83 | } 84 | 85 | data, err := format.Source(builder.Bytes()) 86 | if err != nil { 87 | log.Fatal("Error formatting generated code", err) 88 | } 89 | err = ioutil.WriteFile(blob, data, os.ModePerm) 90 | if err != nil { 91 | log.Fatal("Error writing blob file", err) 92 | } 93 | 94 | log.Println("Baking resources done... \U0001F680") 95 | log.Println("DO NOT COMMIT box/blob.go \U0001F47B") 96 | } 97 | -------------------------------------------------------------------------------- /box/resources/VERSION: -------------------------------------------------------------------------------- 1 | 0.3.6 -------------------------------------------------------------------------------- /box/resources/init/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/go,java,linux,macos,python,windows,virtualenv,visualstudiocode,terraform,intellij 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=go,java,linux,macos,python,windows,virtualenv,visualstudiocode,terraform,intellij 4 | 5 | ### Go ### 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | ### Go Patch ### 23 | /vendor/ 24 | /Godeps/ 25 | 26 | ### Intellij ### 27 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 28 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 29 | 30 | # User-specific stuff 31 | .idea/**/workspace.xml 32 | .idea/**/tasks.xml 33 | .idea/**/usage.statistics.xml 34 | .idea/**/dictionaries 35 | .idea/**/shelf 36 | 37 | # Generated files 38 | .idea/**/contentModel.xml 39 | 40 | # Sensitive or high-churn files 41 | .idea/**/dataSources/ 42 | .idea/**/dataSources.ids 43 | .idea/**/dataSources.local.xml 44 | .idea/**/sqlDataSources.xml 45 | .idea/**/dynamic.xml 46 | .idea/**/uiDesigner.xml 47 | .idea/**/dbnavigator.xml 48 | 49 | # Gradle 50 | .idea/**/gradle.xml 51 | .idea/**/libraries 52 | 53 | # Gradle and Maven with auto-import 54 | # When using Gradle or Maven with auto-import, you should exclude module files, 55 | # since they will be recreated, and may cause churn. Uncomment if using 56 | # auto-import. 57 | # .idea/artifacts 58 | # .idea/compiler.xml 59 | # .idea/jarRepositories.xml 60 | # .idea/modules.xml 61 | # .idea/*.iml 62 | # .idea/modules 63 | # *.iml 64 | # *.ipr 65 | 66 | # CMake 67 | cmake-build-*/ 68 | 69 | # Mongo Explorer plugin 70 | .idea/**/mongoSettings.xml 71 | 72 | # File-based project format 73 | *.iws 74 | 75 | # IntelliJ 76 | out/ 77 | 78 | # mpeltonen/sbt-idea plugin 79 | .idea_modules/ 80 | 81 | # JIRA plugin 82 | atlassian-ide-plugin.xml 83 | 84 | # Cursive Clojure plugin 85 | .idea/replstate.xml 86 | 87 | # Crashlytics plugin (for Android Studio and IntelliJ) 88 | com_crashlytics_export_strings.xml 89 | crashlytics.properties 90 | crashlytics-build.properties 91 | fabric.properties 92 | 93 | # Editor-based Rest Client 94 | .idea/httpRequests 95 | 96 | # Android studio 3.1+ serialized cache file 97 | .idea/caches/build_file_checksums.ser 98 | 99 | ### Intellij Patch ### 100 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 101 | 102 | # *.iml 103 | # modules.xml 104 | # .idea/misc.xml 105 | # *.ipr 106 | 107 | # Sonarlint plugin 108 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 109 | .idea/**/sonarlint/ 110 | 111 | # SonarQube Plugin 112 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 113 | .idea/**/sonarIssues.xml 114 | 115 | # Markdown Navigator plugin 116 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 117 | .idea/**/markdown-navigator.xml 118 | .idea/**/markdown-navigator-enh.xml 119 | .idea/**/markdown-navigator/ 120 | 121 | # Cache file creation bug 122 | # See https://youtrack.jetbrains.com/issue/JBR-2257 123 | .idea/$CACHE_FILE$ 124 | 125 | # CodeStream plugin 126 | # https://plugins.jetbrains.com/plugin/12206-codestream 127 | .idea/codestream.xml 128 | 129 | ### Java ### 130 | # Compiled class file 131 | *.class 132 | 133 | # Log file 134 | *.log 135 | 136 | # BlueJ files 137 | *.ctxt 138 | 139 | # Mobile Tools for Java (J2ME) 140 | .mtj.tmp/ 141 | 142 | # Package Files # 143 | *.jar 144 | *.war 145 | *.nar 146 | *.ear 147 | *.zip 148 | *.tar.gz 149 | *.rar 150 | 151 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 152 | hs_err_pid* 153 | 154 | ### Linux ### 155 | *~ 156 | 157 | # temporary files which can be created if a process still has a handle open of a deleted file 158 | .fuse_hidden* 159 | 160 | # KDE directory preferences 161 | .directory 162 | 163 | # Linux trash folder which might appear on any partition or disk 164 | .Trash-* 165 | 166 | # .nfs files are created when an open file is removed but is still being accessed 167 | .nfs* 168 | 169 | ### macOS ### 170 | # General 171 | .DS_Store 172 | .AppleDouble 173 | .LSOverride 174 | 175 | # Icon must end with two \r 176 | Icon 177 | 178 | 179 | # Thumbnails 180 | ._* 181 | 182 | # Files that might appear in the root of a volume 183 | .DocumentRevisions-V100 184 | .fseventsd 185 | .Spotlight-V100 186 | .TemporaryItems 187 | .Trashes 188 | .VolumeIcon.icns 189 | .com.apple.timemachine.donotpresent 190 | 191 | # Directories potentially created on remote AFP share 192 | .AppleDB 193 | .AppleDesktop 194 | Network Trash Folder 195 | Temporary Items 196 | .apdisk 197 | 198 | ### Python ### 199 | # Byte-compiled / optimized / DLL files 200 | __pycache__/ 201 | *.py[cod] 202 | *$py.class 203 | 204 | # C extensions 205 | 206 | # Distribution / packaging 207 | .Python 208 | build/ 209 | develop-eggs/ 210 | dist/ 211 | downloads/ 212 | eggs/ 213 | .eggs/ 214 | lib/ 215 | lib64/ 216 | parts/ 217 | sdist/ 218 | var/ 219 | wheels/ 220 | pip-wheel-metadata/ 221 | share/python-wheels/ 222 | *.egg-info/ 223 | .installed.cfg 224 | *.egg 225 | MANIFEST 226 | 227 | # PyInstaller 228 | # Usually these files are written by a python script from a template 229 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 230 | *.manifest 231 | *.spec 232 | 233 | # Installer logs 234 | pip-log.txt 235 | pip-delete-this-directory.txt 236 | 237 | # Unit test / coverage reports 238 | htmlcov/ 239 | .tox/ 240 | .nox/ 241 | .coverage 242 | .coverage.* 243 | .cache 244 | nosetests.xml 245 | coverage.xml 246 | *.cover 247 | *.py,cover 248 | .hypothesis/ 249 | .pytest_cache/ 250 | pytestdebug.log 251 | 252 | # Translations 253 | *.mo 254 | *.pot 255 | 256 | # Django stuff: 257 | local_settings.py 258 | db.sqlite3 259 | db.sqlite3-journal 260 | 261 | # Flask stuff: 262 | instance/ 263 | .webassets-cache 264 | 265 | # Scrapy stuff: 266 | .scrapy 267 | 268 | # Sphinx documentation 269 | docs/_build/ 270 | doc/_build/ 271 | 272 | # PyBuilder 273 | target/ 274 | 275 | # Jupyter Notebook 276 | .ipynb_checkpoints 277 | 278 | # IPython 279 | profile_default/ 280 | ipython_config.py 281 | 282 | # pyenv 283 | .python-version 284 | 285 | # pipenv 286 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 287 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 288 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 289 | # install all needed dependencies. 290 | #Pipfile.lock 291 | 292 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 293 | __pypackages__/ 294 | 295 | # Celery stuff 296 | celerybeat-schedule 297 | celerybeat.pid 298 | 299 | # SageMath parsed files 300 | *.sage.py 301 | 302 | # Environments 303 | .env 304 | .venv 305 | env/ 306 | venv/ 307 | ENV/ 308 | env.bak/ 309 | venv.bak/ 310 | pythonenv* 311 | 312 | # Spyder project settings 313 | .spyderproject 314 | .spyproject 315 | 316 | # Rope project settings 317 | .ropeproject 318 | 319 | # mkdocs documentation 320 | /site 321 | 322 | # mypy 323 | .mypy_cache/ 324 | .dmypy.json 325 | dmypy.json 326 | 327 | # Pyre type checker 328 | .pyre/ 329 | 330 | # pytype static type analyzer 331 | .pytype/ 332 | 333 | # profiling data 334 | .prof 335 | 336 | ### Terraform ### 337 | # Local .terraform directories 338 | **/.terraform/* 339 | 340 | # .tfstate files 341 | *.tfstate 342 | *.tfstate.* 343 | 344 | # Crash log files 345 | crash.log 346 | 347 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 348 | # .tfvars files are managed as part of configuration and so should be included in 349 | # version control. 350 | # 351 | # example.tfvars 352 | 353 | # Ignore override files as they are usually used to override resources locally and so 354 | # are not checked in 355 | override.tf 356 | override.tf.json 357 | *_override.tf 358 | *_override.tf.json 359 | 360 | # Include override files you do wish to add to version control using negated pattern 361 | # !example_override.tf 362 | 363 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 364 | # example: *tfplan* 365 | 366 | ### VirtualEnv ### 367 | # Virtualenv 368 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 369 | [Bb]in 370 | [Ii]nclude 371 | [Ll]ib 372 | [Ll]ib64 373 | [Ll]ocal 374 | [Ss]cripts 375 | pyvenv.cfg 376 | pip-selfcheck.json 377 | 378 | ### VisualStudioCode ### 379 | .vscode/* 380 | !.vscode/tasks.json 381 | !.vscode/launch.json 382 | *.code-workspace 383 | 384 | ### VisualStudioCode Patch ### 385 | # Ignore all local history of files 386 | .history 387 | .ionide 388 | 389 | ### Windows ### 390 | # Windows thumbnail cache files 391 | Thumbs.db 392 | Thumbs.db:encryptable 393 | ehthumbs.db 394 | ehthumbs_vista.db 395 | 396 | # Dump file 397 | *.stackdump 398 | 399 | # Folder config file 400 | [Dd]esktop.ini 401 | 402 | # Recycle Bin used on file shares 403 | $RECYCLE.BIN/ 404 | 405 | # Windows Installer files 406 | *.cab 407 | *.msi 408 | *.msix 409 | *.msm 410 | *.msp 411 | 412 | # Windows shortcuts 413 | *.lnk 414 | 415 | # End of https://www.toptal.com/developers/gitignore/api/go,java,linux,macos,python,windows,virtualenv,visualstudiocode,terraform,intellij 416 | 417 | # CLENCLI Files 418 | clencli/log.json -------------------------------------------------------------------------------- /box/resources/init/clencli/aws-well-architected.tmpl: -------------------------------------------------------------------------------- 1 | # AWS Well-Architected 2 | 3 | AWS Well-Architected helps cloud architects build secure, high-performing, resilient, and efficient infrastructure for their applications and workloads. Based on five pillars — operational excellence, security, reliability, performance efficiency, and cost optimization — AWS Well-Architected provides a consistent approach for customers and partners to evaluate architectures, and implement designs that can scale over time. 4 | 5 | The AWS Well-Architected Framework started as a single whitepaper but has expanded to include domain-specific lenses, hands-on labs, and the AWS Well-Architected Tool. The AWS WA Tool, available at no cost in the AWS Management Console, provides a mechanism for regularly evaluating your workloads, identifying high risk issues, and recording your improvements. 6 | 7 | AWS has an ecosystem of hundreds of members of the Well-Architected Partner Program. Engage a partner in your area to help you analyze and review your applications. 8 | 9 | ## AWS Well-Architected and the Five Pillars 10 | ### Framework Overview 11 | 12 | The AWS Well-Architected Framework describes the key concepts, design principles, and architectural best practices for designing and running workloads in the cloud. By answering a set of foundational questions, you learn how well your architecture aligns with cloud best practices and are provided guidance for making improvements. 13 | 14 | ### Operational Excellence Pillar 15 | 16 | The operational excellence pillar focuses on running and monitoring systems to deliver business value, and continually improving processes and procedures. Key topics include automating changes, responding to events, and defining standards to manage daily operations. 17 | 18 | ### Security Pillar 19 | 20 | The security pillar focuses on protecting information and systems. Key topics include confidentiality and integrity of data, identifying and managing who can do what with privilege management, protecting systems, and establishing controls to detect security events. 21 | 22 | ### Reliability Pillar 23 | 24 | The reliability pillar focuses on ensuring a workload performs its intended function correctly and consistently when it’s expected to. A resilient workload quickly recovers from failures to meet business and customer demand. Key topics include distributed system design, recovery planning, and how to handle change. 25 | 26 | ### Performance Efficiency Pillar 27 | 28 | The performance efficiency pillar focuses on using IT and computing resources efficiently. Key topics include selecting the right resource types and sizes based on workload requirements, monitoring performance, and making informed decisions to maintain efficiency as business needs evolve 29 | 30 | ### Cost Optimization Pillar 31 | 32 | The cost optimization pillar focuses on avoiding unnecessary costs. Key topics include understanding and controlling where money is being spent, selecting the most appropriate and right number of resource types, analyzing spend over time, and scaling to meet business needs without overspending. -------------------------------------------------------------------------------- /box/resources/init/clencli/aws-well-architected.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/clencli/7ecf17182a209ce8ea2de534263b58850cfa2bd7/box/resources/init/clencli/aws-well-architected.yaml -------------------------------------------------------------------------------- /box/resources/init/clencli/hld.tmpl: -------------------------------------------------------------------------------- 1 | # High Level Design 2 | 3 | ## Table of contents 4 | 5 | {{ if (file.Exists "clencli/hld.yaml") }} 6 | 7 | {{ if (file.Exists "INPUTS.md") }} - [Inputs](#inputs) {{end}} 8 | {{ if (file.Exists "OUTPUTS.md") }} - [Outputs](#outputs) {{end}} 9 | 10 | {{ if has (ds "db") "architecture" }} - [Architecture](#architecture) {{end}} 11 | {{ if has (ds "db") "automation" }} - [Automation](#automation) {{end}} 12 | {{ if has (ds "db") "availability" }} - [Availability](#availability) {{end}} 13 | {{ if has (ds "db") "compliance" }} - [Compliance](#compliance) {{end}} 14 | {{ if has (ds "db") "criticality" }} - [Criticality](#criticality) {{end}} 15 | {{ if has (ds "db") "data" }} - [Data](#data) {{end}} 16 | {{ if has (ds "db") "decisions" }} - [Decisions](#decisions) {{end}} 17 | {{ if has (ds "db") "devops" }} - [Devops](#devops) {{end}} 18 | {{ if has (ds "db") "environments" }} - [Environments](#environments) {{end}} 19 | {{ if has (ds "db") "integration" }} - [integration](#integration) {{end}} 20 | {{ if has (ds "db") "reliability" }} - [Reliability](#reliability) {{end}} 21 | {{ if has (ds "db") "reusable" }} - [Reusable](#Reusable) {{end}} 22 | {{ if has (ds "db") "scalability" }} - [Scalability](#scalability) {{end}} 23 | {{ if has (ds "db") "target" }} - [Target](#target) {{end}} 24 | {{ if has (ds "db") "team" }} - [Team](#team) {{end}} 25 | 26 | {{end}} 27 | 28 | {{ if (file.Exists "clencli/hld.yaml") }} 29 | 30 | {{ if has (ds "db") "architecture" }} 31 | ## Architecture 32 | --- 33 |
34 | Expand 35 | 36 | {{ (ds "db").architecture.description }} 37 | 38 | ### Current state 39 | --- 40 | {{ (ds "db").architecture.currentstate.description }} 41 | 42 | | ![{{ (ds "db").architecture.currentstate.diagram.label }}]({{ (ds "db").architecture.currentstate.diagram.url }}) | 43 | |:--:| 44 | | *{{ (ds "db").architecture.currentstate.diagram.label }}* | 45 | 46 | ### Target state 47 | --- 48 | {{ (ds "db").architecture.targetstate.description }} 49 | 50 | | ![{{ (ds "db").architecture.targetstate.diagram.label }}]({{ (ds "db").architecture.targetstate.diagram.url }}) | 51 | |:--:| 52 | | *{{ (ds "db").architecture.targetstate.diagram.label }}* | 53 | 54 |
55 | {{end}} 56 | 57 | {{ if has (ds "db") "automation" }} 58 | ## Automation 59 | --- 60 |
61 | Expand 62 | 63 | {{ (ds "db").automation }} 64 |
65 | {{end}} 66 | 67 | {{ if has (ds "db") "availability" }} 68 | ## Availability 69 | --- 70 |
71 | Expand 72 | 73 | {{ (ds "db").availability }} 74 |
75 | {{end}} 76 | 77 | {{ if has (ds "db").compliance "requirements" }} 78 | ## Compliance 79 | --- 80 |
81 | Expand 82 | 83 | {{ (ds "db").compliance.requirements }} 84 |
85 | {{end}} 86 | 87 | {{ if has (ds "db").criticality "category" }} 88 | ## Criticality 89 | --- 90 |
91 | Expand 92 | 93 | {{ (ds "db").criticality.category }} 94 |
95 | {{end}} 96 | 97 | {{ if has (ds "db").data "classification" }} 98 | ## Data 99 | --- 100 |
101 | Expand 102 | 103 | {{ (ds "db").data.classification }} 104 |
105 | {{end}} 106 | 107 | {{ if has (ds "db") "decisions" }} 108 | ## Decisions 109 | --- 110 |
111 | Expand 112 | 113 | All known decisions: 114 | {{ range $decision := (ds "db").decisions }} 115 | {{ printf " * %s - *%s* - %s\n ```\n%s\n```" $decision.date $decision.status $decision.context $decision.description }} 116 | 117 | {{end}} 118 |
119 | {{end}} 120 | 121 | {{ if has (ds "db") "devops" }} 122 | ## Devops 123 | --- 124 |
125 | Expand 126 | 127 | {{ range $devop := (ds "db").devops }} 128 | {{ printf " * [%s](%s)" $devop.label $devop.url }}{{end}} 129 |
130 | {{end}} 131 | 132 | {{ if has (ds "db") "environments" }} 133 | ## Environments 134 | --- 135 |
136 | Expand 137 | 138 | | Name | Identifier | 139 | |:------------:|:--------------------:| 140 | {{ range $env := (ds "db").environments }}{{ printf "| %s | %s |\n" $env.name $env.identifier }}{{end}} 141 |
142 | {{end}} 143 | 144 | {{ if has (ds "db") "integration" }} 145 | ## Integration 146 | --- 147 |
148 | Expand 149 | 150 | {{ (ds "db").integration }} 151 |
152 | {{end}} 153 | 154 | {{ if has (ds "db") "reliability" }} 155 | ## Reliability 156 | --- 157 |
158 | Expand 159 | 160 | {{ (ds "db").reliability }} 161 |
162 | {{end}} 163 | 164 | {{ if has (ds "db") "reusable" }} 165 | ## Reusable 166 | --- 167 |
168 | Expand 169 | 170 | {{ (ds "db").reusable }} 171 |
172 | {{end}} 173 | 174 | {{ if has (ds "db") "scalability" }} 175 | ## Scalability 176 | --- 177 |
178 | Expand 179 | 180 | {{ (ds "db").scalability }} 181 |
182 | {{end}} 183 | 184 | {{ if has (ds "db").target "golive" }} 185 | ## Target 186 | --- 187 |
188 | Expand 189 | 190 | Go-Live Date: {{ (ds "db").target.golive }} 191 |
192 | {{end}} 193 | 194 | {{end}} -------------------------------------------------------------------------------- /box/resources/init/clencli/hld.yaml: -------------------------------------------------------------------------------- 1 | # Show at a high level how: 2 | # * Users interact with the app as a whole 3 | # * Data lifecycle is managed 4 | # * Integrations with and dependencies on other systems 5 | # * Use numbers/letters on diagram to populate table showing what each object does." 6 | # * Do not include deployment mechanism in same diagram as high-level application design. 7 | architecture: 8 | description: Blandit massa enim nec dui nunc mattis. 9 | currentstate: 10 | description: Lorem ipsum dolor sit amet. 11 | diagram: 12 | label: Dui ut ornare lectus sit amet est. 13 | url: https://via.placeholder.com/512x512.png 14 | targetstate: 15 | description: Senectus et netus et malesuada fames ac turpis. 16 | diagram: 17 | label: Elementum curabitur vitae nunc sed velit. 18 | url: https://via.placeholder.com/512x512.png 19 | 20 | # List applicable automation techniques used in the solution: 21 | # * OS 22 | # * Application bootstrap/packaging 23 | automation: |- 24 | Gravida arcu ac tortor dignissim. Magna etiam tempor orci eu lobortis. Magna sit amet purus gravida. 25 | * aliquet 26 | * risus 27 | * feugiat 28 | 29 | # What is the overall availability achieved as a result on individual components availability targets. 30 | # Include exceptions where a single point-of-failure exists 31 | availability: |- 32 | Sagittis purus sit amet volutpat consequat mauris nunc congue nisi. 33 | Congue quisque egestas diam in. 34 | Vulputate eu scelerisque felis imperdiet. 35 | Blandit massa enim nec dui nunc mattis. 36 | 37 | # requirements: details of any compliance requirements to be adhered by the solution 38 | compliance: 39 | requirements: Fermentum leo vel orci porta non. 40 | 41 | # category: application's criticality rating. RTO/RPO 42 | criticality: 43 | category: Volutpat blandit aliquam etiam erat velit scelerisque. 44 | 45 | data: 46 | classification: In tellus integer feugiat scelerisque varius morbi. 47 | 48 | decisions: 49 | - context: Tellus at urna condimentum mattis.Nisl nisi scelerisque eu ultrices vitae auctor. 50 | date: January 1st, 2019 51 | description: Adipiscing enim eu turpis egestas pretium aenean. 52 | status: backlog 53 | - context: Integer malesuada nunc vel risus commodo. 54 | date: January 1st, 2020 55 | description: In metus vulputate eu scelerisque felis imperdiet proin. 56 | status: in-progress 57 | 58 | devops: 59 | - label: code 60 | url: https:// 61 | - label: build 62 | url: https:// 63 | - label: release 64 | url: https:// 65 | - label: artifacts 66 | url: https:// 67 | 68 | environments: 69 | - name: Development 70 | identifier: dev 71 | - name: Test 72 | identifier: tst 73 | - name: Production 74 | identifier: prd 75 | 76 | # List all systems the solution will integrate with: 77 | # * AD 78 | # * NAS Filer 79 | # * Logging solution 80 | # * Batch system 81 | # * Alarming system 82 | # * ITSM tool 83 | integration: |- 84 | * Leo - integer malesuada nunc vel. 85 | * Gravida - neque convallis a cras semper. 86 | 87 | # What level of reliability is achieved for the data hosted in cloud (e.g. s3 reliability) 88 | reliability: Viverra vitae congue eu consequat ac. 89 | 90 | # List components that are re-used from others services: 91 | # * AD 92 | # * OS Images 93 | # * Backups 94 | # * Internet Proxy 95 | # * Anti-virus Agents 96 | # * AWS CloudTrail 97 | # * Naming Standards 98 | # * Tagging Standards 99 | reusable: |- 100 | * Hendrerit - dolor magna eget. 101 | * Integer - malesuada nunc vel risus commodo. 102 | 103 | # Describe how the solution components meet scalability standards, 104 | # include if it does not need/meet any scalability targets. 105 | scalability: Pretium vulputate sapien nec sagittis. 106 | 107 | target: 108 | golive: dd/mm/yyyy 109 | -------------------------------------------------------------------------------- /box/resources/init/clencli/readme.tmpl: -------------------------------------------------------------------------------- 1 | 13 | 14 | {{ if has (ds "db") "logo" }} 15 | ![{{ (ds "db").logo.label }}]({{ (ds "db").logo.url }}) 16 | 17 | > {{ (ds "db").logo.label }} 18 | {{end}} 19 | 20 | {{ if has (ds "db") "shields" }}{{ range $badge := (ds "db").shields.badges }}{{ if ne $badge.image "" }}[![{{ $badge.description }}]({{$badge.image}})]({{$badge.url}}){{ else }}![{{ $badge.description }}]({{ $badge.url }}){{ end }}{{ end }}{{ end }} 21 | 22 | # {{ (ds "db").app.name }} {{ if has (ds "db").app "id" }} ( {{ (ds "db").app.id }} ) {{end}} 23 | 24 | {{ if has (ds "db").app "function" }}{{ (ds "db").app.function }}{{end}} 25 | 26 | ## Table of Contents 27 | --- 28 | 29 | 30 | {{ $db := ds "db" }} 31 | 32 | {{ if has (ds "db") "usage" }}{{ $usage := $db.usage }}{{ if ne $usage "" }} - [Usage](#usage) {{end}}{{end}} 33 | {{ if has (ds "db") "prerequisites" }}{{ $prerequisites := len $db.prerequisites }}{{ if gt $prerequisites 0 }} - [Prerequisites](#prerequisites) {{end}}{{end}} 34 | {{ if has (ds "db") "installing" }}{{ $installing := $db.installing }}{{ if ne $installing "" }} - [Installing](#installing) {{end}}{{end}} 35 | {{ if has (ds "db") "testing" }}{{ $testing := $db.testing }}{{ if ne $testing "" }} - [Testing](#testing) {{end}}{{end}} 36 | {{ if has (ds "db") "deployment" }}{{ $deployment := $db.deployment }}{{ if ne $deployment "" }} - [Deployment](#deployment) {{end}}{{end}} 37 | {{ if has (ds "db") "acknowledgments" }}{{ $acknowledgments := len $db.acknowledgments }}{{ if gt $acknowledgments 0 }} - [Acknowledgments](#acknowledgments) {{end}}{{end}} 38 | {{ if has (ds "db") "contributors" }}{{ $contributors := len $db.contributors }}{{ if gt $contributors 0 }} - [Contributors](#contributors) {{end}}{{end}} 39 | {{ if has (ds "db") "references" }}{{ $references := len $db.references }}{{ if gt $references 0 }} - [References](#references) {{end}}{{end}} 40 | {{ if has (ds "db") "license" }}{{ $license := $db.license }}{{ if ne $license "" }} - [License](#license) {{end}}{{end}} 41 | {{ if has (ds "db") "copyright" }}{{ $copyright := $db.copyright }}{{ if ne $copyright "" }} - [Copyright](#copyright) {{end}}{{end}} 42 | 43 | {{ if has (ds "db") "screenshots" }} 44 | ## Screenshots 45 | --- 46 |
47 | Expand 48 | 49 | {{ range $screenshot := (ds "db").screenshots }} 50 | | ![{{ $screenshot.label }}]({{ $screenshot.url }}) | 51 | |:--:| 52 | | *{{ $screenshot.caption }}* | 53 | {{ end }} 54 |
55 | {{ end }} 56 | 57 | {{ if has (ds "db") "usage" }} 58 | ## Usage 59 | --- 60 |
61 | Expand 62 | 63 | {{ (ds "db").usage }} 64 |
65 | {{ end }} 66 | 67 | {{ if has (ds "db") "prerequisites" }} 68 | ## Prerequisites 69 | --- 70 |
71 | Expand 72 | 73 | {{ range $prerequisite := (ds "db").prerequisites }}{{ printf "- [%s](%s) - %s\n" $prerequisite.name $prerequisite.url $prerequisite.description }}{{end}} 74 |
75 | {{end}} 76 | 77 | {{ if has (ds "db") "installing" }} 78 | ## Installing 79 | --- 80 |
81 | Expand 82 | 83 | {{ (ds "db").installing }} 84 |
85 | {{end}} 86 | 87 | {{ if has (ds "db") "testing" }} 88 | ## Testing 89 | --- 90 |
91 | Expand 92 | 93 | {{ (ds "db").testing }} 94 |
95 | {{end}} 96 | 97 | {{ if has (ds "db") "deployment" }} 98 | ## Deployment 99 | --- 100 |
101 | Expand 102 | 103 | {{ (ds "db").deployment }} 104 |
105 | {{end}} 106 | 107 | {{ if has (ds "db") "include" }} 108 | {{ range $file := (ds "db").include }} 109 | {{ defineDatasource $file $file }} 110 | {{ include $file }}{{end}} 111 | {{end}} 112 | 113 | {{ if has (ds "db") "contributors" }} 114 | ## Contributors 115 | --- 116 |
117 | Expand 118 | 119 | | Name | Email | Role | 120 | |:------------:|:--------------------:|:---------------:| 121 | {{ range $contributor := (ds "db").contributors }}{{ printf "| %s | %s | %s |\n" $contributor.name $contributor.email $contributor.role}}{{end}} 122 |
123 | {{end}} 124 | 125 | {{ if has (ds "db") "acknowledgments" }} 126 | ## Acknowledgments 127 | --- 128 |
129 | Expand 130 | 131 | Gratitude for assistance: 132 | {{ range $ack := (ds "db").acknowledgments }}{{ printf " * %s - %s\n" $ack.name $ack.role }}{{end}} 133 | 134 |
135 | {{end}} 136 | 137 | {{ if has (ds "db") "references" }} 138 | ## References 139 | --- 140 |
141 | Expand 142 | 143 | {{ range $ref := (ds "db").references }}{{ printf " * [%s](%s) - %s\n" $ref.name $ref.url $ref.description }}{{end}} 144 | 145 |
146 | {{end}} 147 | 148 | {{ if has (ds "db") "license" }} 149 | ## License 150 | --- 151 | {{ (ds "db").license}}{{ end }} 152 | {{ if (file.Exists "LICENSE") }} 153 | For more information please read [LICENSE](LICENSE). 154 | {{ end }} 155 | 156 | {{ if has (ds "db") "copyright" }} 157 | ## Copyright 158 | --- 159 | ``` 160 | {{ (ds "db").copyright}} 161 | ``` 162 | {{end}} 163 | -------------------------------------------------------------------------------- /box/resources/init/clencli/readme.yaml: -------------------------------------------------------------------------------- 1 | logo: 2 | label: logo 3 | url: https://via.placeholder.com/512x90.png 4 | app: 5 | name: Application 6 | function: Lorem ipsum dolor 7 | id: application-id 8 | screenshots: 9 | - caption: How to build 10 | label: how-to-build 11 | url: https://via.placeholder.com/512x256.png 12 | - caption: How to run 13 | label: how-to-run 14 | url: https://via.placeholder.com/512x256.png 15 | usage: |- 16 | Magnis dis parturient montes nascetur. 17 | Convallis posuere morbi leo urna molestie at. 18 | prerequisites: 19 | - description: Nisi quis eleifend quam adipiscing.Lacus vel facilisis volutpat est velit egestas dui id. 20 | name: Eget arcu dictum 21 | url: https:// 22 | installing: |- 23 | Donec adipiscing tristique risus nec feugiat. 24 | ``` 25 | $ sudo apt-get install vim 26 | ``` 27 | testing: |- 28 | Varius morbi enim nunc faucibus a pellentesque. 29 | ``` 30 | $ app test 31 | ``` 32 | deployment: |- 33 | Adipiscing bibendum est ultricies integer. 34 | ``` 35 | $ app deploy 36 | ``` 37 | contributors: 38 | - name: LastName, FirstName 39 | role: Job Role 40 | email: name@email.com 41 | acknowledgments: 42 | - name: LastName, FirstName 43 | role: Job Role 44 | references: 45 | - description: Cloud Engineer CLI 46 | name: clencli 47 | url: https://github.com/awslabs/clencli 48 | license: This project is licensed under the Apache License 2.0. 49 | copyright: Company, Inc. or its affiliates. All Rights Reserved. 50 | -------------------------------------------------------------------------------- /box/resources/init/project/type/clouformation/skeleton.json: -------------------------------------------------------------------------------- 1 | { 2 | "StackName": "", 3 | "TemplateBody": "", 4 | "TemplateURL": "", 5 | "Parameters": [ 6 | { 7 | "ParameterKey": "", 8 | "ParameterValue": "", 9 | "UsePreviousValue": true, 10 | "ResolvedValue": "" 11 | } 12 | ], 13 | "DisableRollback": true, 14 | "RollbackConfiguration": { 15 | "RollbackTriggers": [ 16 | { 17 | "Arn": "", 18 | "Type": "" 19 | } 20 | ], 21 | "MonitoringTimeInMinutes": 0 22 | }, 23 | "TimeoutInMinutes": 0, 24 | "NotificationARNs": [ 25 | "" 26 | ], 27 | "Capabilities": [ 28 | "CAPABILITY_IAM" 29 | ], 30 | "ResourceTypes": [ 31 | "" 32 | ], 33 | "RoleARN": "", 34 | "OnFailure": "DELETE", 35 | "StackPolicyBody": "", 36 | "StackPolicyURL": "", 37 | "Tags": [ 38 | { 39 | "Key": "", 40 | "Value": "" 41 | } 42 | ], 43 | "ClientRequestToken": "", 44 | "EnableTerminationProtection": true 45 | } 46 | -------------------------------------------------------------------------------- /box/resources/init/project/type/clouformation/skeleton.yaml: -------------------------------------------------------------------------------- 1 | StackName: '' # [REQUIRED] The name that is associated with the stack. 2 | TemplateBody: '' # Structure containing the template body with a minimum length of 1 byte and a maximum length of 51,200 bytes. 3 | TemplateURL: '' # Location of file containing the template body. 4 | Parameters: # A list of Parameter structures that specify input parameters for the stack. 5 | - ParameterKey: '' # The key associated with the parameter. 6 | ParameterValue: '' # The input value associated with the parameter. 7 | UsePreviousValue: true # During a stack update, use the existing parameter value that the stack is using for a given parameter key. 8 | ResolvedValue: '' # Read-only. 9 | DisableRollback: true # Set to true to disable rollback of the stack if stack creation failed. 10 | RollbackConfiguration: # The rollback triggers for AWS CloudFormation to monitor during stack creation and updating operations, and for the specified monitoring period afterwards. 11 | RollbackTriggers: # The triggers to monitor during stack creation or update actions. 12 | - Arn: '' # [REQUIRED] The Amazon Resource Name (ARN) of the rollback trigger. 13 | Type: '' # [REQUIRED] The resource type of the rollback trigger. 14 | MonitoringTimeInMinutes: 0 # The amount of time, in minutes, during which CloudFormation should monitor all the rollback triggers after the stack creation or update operation deploys all necessary resources. 15 | TimeoutInMinutes: 0 # The amount of time that can pass before the stack status becomes CREATE_FAILED; if DisableRollback is not set or is set to false, the stack will be rolled back. 16 | NotificationARNs: # The Simple Notification Service (SNS) topic ARNs to publish stack related events. 17 | - '' 18 | Capabilities: # In some cases, you must explicitly acknowledge that your stack template contains certain capabilities in order for AWS CloudFormation to create the stack. 19 | - CAPABILITY_IAM 20 | ResourceTypes: # The template resource types that you have permissions to work with for this create stack action, such as AWS. 21 | - '' 22 | RoleARN: '' # The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes to create the stack. 23 | OnFailure: DO_NOTHING # Determines what action will be taken if stack creation fails. Valid values are: DO_NOTHING, ROLLBACK, DELETE. 24 | StackPolicyBody: '' # Structure containing the stack policy body. 25 | StackPolicyURL: '' # Location of a file containing the stack policy. 26 | Tags: # Key-value pairs to associate with this stack. 27 | - Key: '' # [REQUIRED] Required. 28 | Value: '' # [REQUIRED] Required. 29 | ClientRequestToken: '' # A unique identifier for this CreateStack request. 30 | EnableTerminationProtection: true # Whether to enable termination protection on the specified stack. 31 | -------------------------------------------------------------------------------- /box/resources/init/project/type/terraform/LICENSE: -------------------------------------------------------------------------------- 1 | # License under which this code is distributed -------------------------------------------------------------------------------- /box/resources/init/project/type/terraform/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: aws/get-caller-identity 2 | aws/get-caller-identity: ## Returns details about the IAM user or role whose credentials are used to call the operation. 3 | aws sts get-caller-identity 4 | 5 | .PHONY: terraform/init 6 | terraform/init: ## Initializes a Terraform working directory 7 | terraform init 8 | 9 | .PHONY: terraform/get 10 | terraform/get: ## Downloads and installs modules for the configuration 11 | terraform get 12 | 13 | .PHONY: terraform/fmt 14 | terraform/fmt: ## Rewrites config files to canonical format 15 | terraform fmt 16 | 17 | .PHONY: terraform/plan 18 | terraform/plan: terraform/fmt terraform/validate terraform/workspace ## Generate and show an execution plan 19 | ifdef environment 20 | terraform plan -out=plan.tfplan -var-file=environments/$(environment).tf 21 | else 22 | @echo "argument environment NOT defined" && exit 1 23 | endif 24 | 25 | .PHONY: terraform/apply 26 | terraform/apply: ## Generates and shows the execution plan 27 | terraform apply plan.tfplan 28 | 29 | .PHONY: terraform/validate 30 | terraform/validate: ## Validates the Terraform files 31 | terraform validate 32 | 33 | .PHONY: terraform/clean 34 | terraform/clean: ## Removes local .terraform directory 35 | rm -rf .terraform/ 36 | rm -rf terraform.tfstate.d 37 | rm -f *.tfplan 38 | rm -f terraform.tfstate* 39 | rm -f .terraform.tfstate* 40 | 41 | .PHONY: terraform/workspace 42 | terraform/workspace: ## Selects a workspace based on environment 43 | ifdef environment 44 | @terraform workspace select $(environment) || (echo "terraform workspace not found, creating a new one..."; terraform workspace new $(environment)) 45 | else 46 | @echo "argument environment NOT defined" && exit 1 47 | endif 48 | 49 | .PHONY: terraform/destroy 50 | terraform/destroy: ## Destroys Terraform-managed infrastructure 51 | ifdef environment 52 | @terraform plan -destroy -out=destroy.tfplan -var-file=environments/$(environment).tf 53 | @terraform apply destroy.tfplan 54 | else 55 | echo "argument environment NOT defined" 56 | endif 57 | -------------------------------------------------------------------------------- /box/resources/init/project/type/terraform/environments/dev.tf: -------------------------------------------------------------------------------- 1 | environment="dev" -------------------------------------------------------------------------------- /box/resources/init/project/type/terraform/environments/prod.tf: -------------------------------------------------------------------------------- 1 | environment="prod" -------------------------------------------------------------------------------- /box/resources/init/project/type/terraform/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /box/resources/init/project/type/terraform/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/clencli/7ecf17182a209ce8ea2de534263b58850cfa2bd7/box/resources/init/project/type/terraform/outputs.tf -------------------------------------------------------------------------------- /box/resources/init/project/type/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "AWS Region" 4 | } 5 | -------------------------------------------------------------------------------- /box/resources/manual/configure.yaml: -------------------------------------------------------------------------------- 1 | use: configure [delete] 2 | example: > 3 | If you want to create a new named profile: 4 | clencli configure --profile work 5 | 6 | If you want to delete a named profile: 7 | clencli configure delete --profile work 8 | short: Configures CLENCLI global settings 9 | long: Configures CLENCLI global credentials and configurations used by commands -------------------------------------------------------------------------------- /box/resources/manual/gitignore.yaml: -------------------------------------------------------------------------------- 1 | use: gitignore 2 | example: > 3 | if you want to see a list of valid project types: 4 | clencli gitignore list 5 | 6 | If you want to create a .gitignore file for your terraform project: 7 | clencli gitignore --input terraform 8 | 9 | If you want to create a .gitignore file for your project with multiple rules: 10 | clencli gitignore --input terraform,vscode 11 | 12 | short: Download .gitignore based on the given input 13 | long: Download .gitignore based on the given input -------------------------------------------------------------------------------- /box/resources/manual/init.yaml: -------------------------------------------------------------------------------- 1 | use: init [project] [--project-name ] [ --project-type [basic|cloudformation|terraform] ] 2 | example: > 3 | If you want to initialize a basic project: 4 | clencli init project --project-name foo 5 | 6 | If you want to generate a cloud project: 7 | clencli init project --project-name foo --project-type cloud 8 | 9 | If you want to generate a cloudFormation project: 10 | clencli init project --project-name foo --project-type cloudformation 11 | 12 | If you want to generate a terraform project: 13 | clencli init project --project-name foo --project-type terraform 14 | short: Initialize a project 15 | long: Initialize a project with code structure and templates -------------------------------------------------------------------------------- /box/resources/manual/render.yaml: -------------------------------------------------------------------------------- 1 | use: render template [ --name ] 2 | short: Render a template 3 | long: Render a template -------------------------------------------------------------------------------- /box/resources/manual/root.yaml: -------------------------------------------------------------------------------- 1 | use: clencli 2 | short: The Cloud Engineer CLI 3 | long: The Cloud Engineer CLI -------------------------------------------------------------------------------- /box/resources/manual/unsplash.yaml: -------------------------------------------------------------------------------- 1 | use: unsplash [options] 2 | short: Downloads random photos from Unsplash.com 3 | long: Retrieve a single random photo, given optional filters. -------------------------------------------------------------------------------- /box/resources/manual/version.yaml: -------------------------------------------------------------------------------- 1 | use: version [options] 2 | short: Displays the version of CLENCLI and all installed plugins 3 | long: | 4 | Displays the version of Terraform and all installed plugins in the following format: 5 | clencli/{git-tag} {golang-version} {architecture} {source} -------------------------------------------------------------------------------- /clencli/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/clencli/7ecf17182a209ce8ea2de534263b58850cfa2bd7/clencli/logo.jpeg -------------------------------------------------------------------------------- /clencli/readme.tmpl: -------------------------------------------------------------------------------- 1 | 13 | 14 | {{ if has (ds "db") "logo" }} 15 | ![{{ (ds "db").logo.label }}]({{ (ds "db").logo.url }}) 16 | 17 | > {{ (ds "db").logo.label }} 18 | {{end}} 19 | 20 | {{ if has (ds "db") "shields" }}{{ range $badge := (ds "db").shields.badges }}{{ if ne $badge.image "" }}[![{{ $badge.description }}]({{$badge.image}})]({{$badge.url}}){{ else }}![{{ $badge.description }}]({{ $badge.url }}){{ end }}{{ end }}{{ end }} 21 | 22 | # {{ (ds "db").app.name }} {{ if has (ds "db").app "id" }} ( {{ (ds "db").app.id }} ) {{end}} 23 | 24 | {{ if has (ds "db").app "function" }}{{ (ds "db").app.function }}{{end}} 25 | 26 | ## Table of Contents 27 | --- 28 | 29 | 30 | {{ $db := ds "db" }} 31 | 32 | {{ if has (ds "db") "usage" }}{{ $usage := $db.usage }}{{ if ne $usage "" }} - [Usage](#usage) {{end}}{{end}} 33 | {{ if has (ds "db") "prerequisites" }}{{ $prerequisites := len $db.prerequisites }}{{ if gt $prerequisites 0 }} - [Prerequisites](#prerequisites) {{end}}{{end}} 34 | {{ if has (ds "db") "installing" }}{{ $installing := $db.installing }}{{ if ne $installing "" }} - [Installing](#installing) {{end}}{{end}} 35 | {{ if has (ds "db") "testing" }}{{ $testing := $db.testing }}{{ if ne $testing "" }} - [Testing](#testing) {{end}}{{end}} 36 | {{ if has (ds "db") "deployment" }}{{ $deployment := $db.deployment }}{{ if ne $deployment "" }} - [Deployment](#deployment) {{end}}{{end}} 37 | {{ if has (ds "db") "acknowledgments" }}{{ $acknowledgments := len $db.acknowledgments }}{{ if gt $acknowledgments 0 }} - [Acknowledgments](#acknowledgments) {{end}}{{end}} 38 | {{ if has (ds "db") "contributors" }}{{ $contributors := len $db.contributors }}{{ if gt $contributors 0 }} - [Contributors](#contributors) {{end}}{{end}} 39 | {{ if has (ds "db") "references" }}{{ $references := len $db.references }}{{ if gt $references 0 }} - [References](#references) {{end}}{{end}} 40 | {{ if has (ds "db") "license" }}{{ $license := $db.license }}{{ if ne $license "" }} - [License](#license) {{end}}{{end}} 41 | {{ if has (ds "db") "copyright" }}{{ $copyright := $db.copyright }}{{ if ne $copyright "" }} - [Copyright](#copyright) {{end}}{{end}} 42 | 43 | {{ if has (ds "db") "screenshots" }} 44 | ## Screenshots 45 | --- 46 |
47 | Expand 48 | 49 | {{ range $screenshot := (ds "db").screenshots }} 50 | | ![{{ $screenshot.label }}]({{ $screenshot.url }}) | 51 | |:--:| 52 | | *{{ $screenshot.caption }}* | 53 | {{ end }} 54 |
55 | {{ end }} 56 | 57 | {{ if has (ds "db") "usage" }} 58 | ## Usage 59 | --- 60 |
61 | Expand 62 | 63 | {{ (ds "db").usage }} 64 |
65 | {{ end }} 66 | 67 | {{ if has (ds "db") "prerequisites" }} 68 | ## Prerequisites 69 | --- 70 |
71 | Expand 72 | 73 | {{ range $prerequisite := (ds "db").prerequisites }}{{ printf "- [%s](%s) - %s\n" $prerequisite.name $prerequisite.url $prerequisite.description }}{{end}} 74 |
75 | {{end}} 76 | 77 | {{ if has (ds "db") "installing" }} 78 | ## Installing 79 | --- 80 |
81 | Expand 82 | 83 | {{ (ds "db").installing }} 84 |
85 | {{end}} 86 | 87 | {{ if has (ds "db") "testing" }} 88 | ## Testing 89 | --- 90 |
91 | Expand 92 | 93 | {{ (ds "db").testing }} 94 |
95 | {{end}} 96 | 97 | {{ if has (ds "db") "deployment" }} 98 | ## Deployment 99 | --- 100 |
101 | Expand 102 | 103 | {{ (ds "db").deployment }} 104 |
105 | {{end}} 106 | 107 | {{ if has (ds "db") "include" }} 108 | {{ range $file := (ds "db").include }} 109 | {{ defineDatasource $file $file }} 110 | {{ include $file }}{{end}} 111 | {{end}} 112 | 113 | {{ if has (ds "db") "contributors" }} 114 | ## Contributors 115 | --- 116 |
117 | Expand 118 | 119 | | Name | Email | Role | 120 | |:------------:|:--------------------:|:---------------:| 121 | {{ range $contributor := (ds "db").contributors }}{{ printf "| %s | %s | %s |\n" $contributor.name $contributor.email $contributor.role}}{{end}} 122 |
123 | {{end}} 124 | 125 | {{ if has (ds "db") "acknowledgments" }} 126 | ## Acknowledgments 127 | --- 128 |
129 | Expand 130 | 131 | Gratitude for assistance: 132 | {{ range $ack := (ds "db").acknowledgments }}{{ printf " * %s - %s\n" $ack.name $ack.role }}{{end}} 133 | 134 |
135 | {{end}} 136 | 137 | {{ if has (ds "db") "references" }} 138 | ## References 139 | --- 140 |
141 | Expand 142 | 143 | {{ range $ref := (ds "db").references }}{{ printf " * [%s](%s) - %s\n" $ref.name $ref.url $ref.description }}{{end}} 144 | 145 |
146 | {{end}} 147 | 148 | {{ if has (ds "db") "license" }} 149 | ## License 150 | --- 151 | {{ (ds "db").license}}{{ end }} 152 | {{ if (file.Exists "LICENSE") }} 153 | For more information please read [LICENSE](LICENSE). 154 | {{ end }} 155 | 156 | {{ if has (ds "db") "copyright" }} 157 | ## Copyright 158 | --- 159 | ``` 160 | {{ (ds "db").copyright}} 161 | ``` 162 | {{end}} 163 | -------------------------------------------------------------------------------- /clencli/readme.yaml: -------------------------------------------------------------------------------- 1 | logo: 2 | url: clencli/logo.jpeg 3 | label: Photo by [Felipe Dias](https://unsplash.com/fdiascreator) on [Unsplash](https://unsplash.com) 4 | shields: 5 | badges: 6 | - description: GitHub issues 7 | image: https://img.shields.io/github/issues/awslabs/clencli 8 | url: https://github.com/awslabs/clencli/issues 9 | - description: GitHub forks 10 | image: https://img.shields.io/github/forks/awslabs/clencli 11 | url: https://github.com/awslabs/clencli/network 12 | - description: GitHub stars 13 | image: https://img.shields.io/github/stars/awslabs/clencli 14 | url: https://github.com/awslabs/clencli/stargazers 15 | - description: GitHub license 16 | image: https://img.shields.io/github/license/awslabs/clencli 17 | url: https://github.com/awslabs/clencli/blob/master/LICENSE 18 | - description: Twitter 19 | image: https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fawslabs%2Fclencli 20 | url: https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fawslabs%2Fclencli 21 | app: 22 | name: Cloud Engineer CLI 23 | function: A CLI built to assist Cloud Engineers. 24 | id: clencli 25 | usage: |- 26 | In a polyglot world where a team can choose it's programming language, often this flexibility can spill into chaos as every repo looks different. 27 | CLENCLI solves this issue by giving developers a quick and easy way to create a standardised repo structure and easily rendering documentation via a YAML file. 28 | 29 | ### Create a new project 30 | ``` 31 | $ clencli init project --project-name foo 32 | $ tree -a moon/ 33 | foo/ 34 | ├── clencli 35 | │   ├── readme.tmpl 36 | │   └── readme.yaml 37 | └── .gitignore 38 | ``` 39 | 40 | ### Create a new CloudFormation project 41 | ``` 42 | $ clencli init project --project-name foo --project-type cloudformation 43 | $ tree -a sun/ 44 | foo/ 45 | ├── clencli 46 | │   ├── hld.tmpl 47 | │   ├── hld.yaml 48 | │   ├── readme.tmpl 49 | │   └── readme.yaml 50 | ├── environments 51 | │   ├── dev 52 | │   └── prod 53 | ├── .gitignore 54 | ├── skeleton.json 55 | └── skeleton.yaml 56 | ``` 57 | 58 | ### Create a new Terraform project 59 | ``` 60 | $ clencli init project --project-name foo --project-type terraform 61 | $ tree -a foo/ 62 | foo/ 63 | ├── clencli 64 | │   ├── hld.tmpl 65 | │   ├── hld.yaml 66 | │   ├── readme.tmpl 67 | │   └── readme.yaml 68 | ├── environments 69 | │   ├── dev.tf 70 | │   └── prod.tf 71 | ├── .gitignore 72 | ├── LICENSE 73 | ├── main.tf 74 | ├── Makefile 75 | ├── outputs.tf 76 | └── variables.tf 77 | ``` 78 | 79 | ## Render a template 80 | ``` 81 | $ clencli init project --project-name foo 82 | foo was successfully initialized as a basic project 83 | $ cd foo/ 84 | $ clencli render template 85 | Template readme.tmpl rendered as README.md 86 | ``` 87 | 88 | The `README.md` you are reading right now was generated and it's maintained by `CLENCLI` itself. 89 | Please check [readme.yaml](clencli/readme.yaml) for more details. 90 | 91 | ## Download a .gitignore for your project 92 | ``` 93 | $ clencli gitignore --input="terraform,visualstudiocode" 94 | .gitignore created successfully 95 | $ less .gitignore 96 | 97 | # Created by https://www.toptal.com/developers/gitignore/api/terraform,visualstudiocode 98 | # Edit at https://www.toptal.com/developers/gitignore?templates=terraform,visualstudiocode 99 | 100 | ### Terraform ### 101 | # Local .terraform directories 102 | **/.terraform/* 103 | 104 | # .tfstate files 105 | *.tfstate 106 | *.tfstate.* 107 | 108 | # Crash log files 109 | crash.log 110 | 111 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 112 | # .tfvars files are managed as part of configuration and so should be included in 113 | # version control. 114 | # 115 | # example.tfvars 116 | 117 | # Ignore override files as they are usually used to override resources locally and so 118 | # are not checked in 119 | override.tf 120 | override.tf.json 121 | *_override.tf 122 | *_override.tf.json 123 | 124 | # Include override files you do wish to add to version control using negated pattern 125 | # !example_override.tf 126 | 127 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 128 | # example: *tfplan* 129 | 130 | ### VisualStudioCode ### 131 | .vscode/* 132 | !.vscode/settings.json 133 | !.vscode/tasks.json 134 | !.vscode/launch.json 135 | !.vscode/extensions.json 136 | *.code-workspace 137 | 138 | ### VisualStudioCode Patch ### 139 | # Ignore all local history of files 140 | .history 141 | .ionide 142 | 143 | # End of https://www.toptal.com/developers/gitignore/api/terraform,visualstudiocode 144 | 145 | ``` 146 | Additionally, you can also *customize the initialization* of your projects (scaffolding) and download photos for your projects from [unsplash](https://unsplash.com), please read more [here](https://github.com/awslabs/clencli/wiki/Configuration). 147 | installing: Download the latest version [released](https://github.com/awslabs/clencli/releases) 148 | according to your platform and execute it directly. I recommend placing the binary 149 | into your `$PATH`, so it's easily accessible. 150 | include: 151 | - COMMANDS.md 152 | contributors: 153 | - name: Silva, Valter 154 | role: AWS Professional Services - Cloud Architect 155 | email: valterh@amazon.com 156 | acknowledgments: 157 | - name: Sia, William 158 | role: AWS Professional Service - Senior Cloud Architect 159 | - name: Dhingra, Prashit 160 | role: AWS Professional Service - Cloud Architect 161 | references: 162 | - description: Cobra is both a library for creating powerful modern CLI applications 163 | as well as a program to generate applications and command files. 164 | name: cobra 165 | url: https://github.com/spf13/cobra 166 | - description: Viper is a complete configuration solution for Go applications including 167 | 12-Factor apps. 168 | name: viper 169 | url: https://github.com/spf13/viper 170 | - description: The Twelve-Factor App 171 | name: twelve-factor-app 172 | url: https://12factor.net 173 | - description: gomplate is a template renderer which supports a growing list of datasources, 174 | such as JSON (including EJSON - encrypted JSON), YAML, AWS EC2 metadata, BoltDB, 175 | Hashicorp Consul and Hashicorp Vault secrets. 176 | name: gomplate 177 | url: https://github.com/hairyhenderson/gomplate 178 | - description: The most powerful photo engine in the world. 179 | name: unsplash 180 | url: https://unsplash.com 181 | - description: The Free Image Placeholder Service Favoured By Designers 182 | name: placeholder 183 | url: https://placeholder.com 184 | - description: The best Lorem Ipsum Generator in all the sea 185 | name: pirate-ipsum 186 | url: https://pirateipsum.me 187 | - description: Record Fast Screencasts 188 | name: recordit 189 | url: https://recordit.co 190 | - description: A terminal-to-gif recorder minus the headaches. 191 | name: ttystudio 192 | url: https://github.com/chjj/ttystudio 193 | - description: GitHub Super Linter 194 | name: gihub-super-linter 195 | url: https://github.com/github/super-linter 196 | - description: GitHub Actions 197 | name: github-actions 198 | url: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/introduction-to-github-actions 199 | - description: Create useful .gitignore files for your project 200 | name: gitignore.io 201 | url: https://www.toptal.com/developers/gitignore 202 | license: This project is licensed under the Apache License 2.0. 203 | copyright: Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 204 | -------------------------------------------------------------------------------- /clencli/unsplash.yaml: -------------------------------------------------------------------------------- 1 | randomphotoparameters: 2 | collections: "" 3 | featured: "" 4 | filter: "" 5 | orientation: "" 6 | query: cats 7 | size: "" 8 | username: "" 9 | randomphotoresponse: 10 | id: ldFbYytgEHE 11 | createdat: "2020-03-31T00:33:54-04:00" 12 | updatedat: "2020-10-07T01:19:23-04:00" 13 | promotedat: null 14 | width: 3024 15 | height: 4032 16 | color: '#0C060B' 17 | description: "" 18 | altdescription: white horse on brown field during daytime 19 | urls: 20 | raw: https://images.unsplash.com/photo-1585629220785-8039e6b5c009?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE1MjY5OH0 21 | full: https://images.unsplash.com/photo-1585629220785-8039e6b5c009?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE1MjY5OH0 22 | regular: https://images.unsplash.com/photo-1585629220785-8039e6b5c009?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjE1MjY5OH0 23 | small: https://images.unsplash.com/photo-1585629220785-8039e6b5c009?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE1MjY5OH0 24 | thumb: https://images.unsplash.com/photo-1585629220785-8039e6b5c009?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=200&fit=max&ixid=eyJhcHBfaWQiOjE1MjY5OH0 25 | links: 26 | self: https://api.unsplash.com/photos/ldFbYytgEHE 27 | html: https://unsplash.com/photos/ldFbYytgEHE 28 | download: https://unsplash.com/photos/ldFbYytgEHE/download 29 | downloadlocation: https://api.unsplash.com/photos/ldFbYytgEHE/download 30 | categories: [] 31 | likes: 25 32 | likedbyuser: false 33 | currentusercollections: [] 34 | sponsorship: null 35 | user: 36 | id: GQBRmp3ZWTM 37 | updatedat: "2020-10-13T09:57:06-04:00" 38 | username: sharifmatar 39 | name: Sharif Matar 40 | firstname: Sharif 41 | lastname: Matar 42 | twitterusername: "" 43 | portfoliourl: "" 44 | bio: "" 45 | location: "" 46 | links: 47 | self: https://api.unsplash.com/users/sharifmatar 48 | html: https://unsplash.com/@sharifmatar 49 | photos: https://api.unsplash.com/users/sharifmatar/photos 50 | likes: https://api.unsplash.com/users/sharifmatar/likes 51 | portfolio: https://api.unsplash.com/users/sharifmatar/portfolio 52 | following: https://api.unsplash.com/users/sharifmatar/following 53 | followers: https://api.unsplash.com/users/sharifmatar/followers 54 | profileimage: 55 | small: https://images.unsplash.com/profile-1587538413056-d264b0cf4b4eimage?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=32&w=32 56 | medium: https://images.unsplash.com/profile-1587538413056-d264b0cf4b4eimage?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=64&w=64 57 | large: https://images.unsplash.com/profile-1587538413056-d264b0cf4b4eimage?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=128&w=128 58 | instagramusername: "" 59 | totalcollections: 0 60 | totallikes: 16 61 | totalphotos: 13 62 | acceptedtos: true 63 | exif: 64 | make: Apple 65 | model: iPhone 11 Pro Max 66 | exposuretime: 1/950 67 | aperture: "2.0" 68 | focallength: "6.0" 69 | iso: 20 70 | location: 71 | title: "" 72 | name: "" 73 | city: null 74 | country: "" 75 | position: 76 | latitude: 0 77 | longitude: 0 78 | views: 10029 79 | downloads: 49 80 | -------------------------------------------------------------------------------- /cobra/README.md: -------------------------------------------------------------------------------- 1 | # cobra 2 | 3 | ## Packages 4 | 5 | Below you can find the responsibility and purpose for each package. 6 | 7 | ### aid 8 | 9 | Assist Cobra commands individually. Example: a command under `cmd/foo.go`, has its respective `aid/foo.go`. 10 | This allows a more cleaner and readable code. 11 | 12 | ### cmd 13 | 14 | Cobra commands. [reference](https://github.com/spf13/cobra) 15 | 16 | ### controller 17 | 18 | > Controller acts on both model and view. It controls the data flow into model object and updates the view whenever data changes. It keeps view and model separate. [reference](https://www.tutorialspoint.com/design_pattern/mvc_pattern.htm) 19 | 20 | ### dao (Data Access Object) 21 | 22 | > Data Access Object Pattern or DAO pattern is used to separate low level data accessing API or operations from high level business services. Following are the participants in Data Access Object Pattern. 23 | > Data Access Object Interface - This interface defines the standard operations to be performed on a model object(s). 24 | > Data Access Object concrete class - This class implements above interface. This class is responsible to get data from a data source which can be database / xml or any other storage mechanism. 25 | > Model Object or Value Object - This object is simple POJO containing get/set methods to store data retrieved using DAO class. [reference](https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm) 26 | 27 | ### model 28 | 29 | > Model represents an object carrying data. It can also have logic to update controller if its data changes. [reference](https://www.tutorialspoint.com/design_pattern/mvc_pattern.htm) 30 | 31 | ### view 32 | 33 | > View represents the visualization of the data that model contains. [reference](https://www.tutorialspoint.com/design_pattern/mvc_pattern.htm) 34 | 35 | 36 | -------------------------------------------------------------------------------- /cobra/aid/configure.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package aid 17 | 18 | import ( 19 | "bufio" 20 | "encoding/json" 21 | "fmt" 22 | "io/ioutil" 23 | "os" 24 | "runtime" 25 | "strconv" 26 | "strings" 27 | 28 | "github.com/awslabs/clencli/helper" 29 | "github.com/sirupsen/logrus" 30 | "github.com/spf13/cobra" 31 | "github.com/spf13/viper" 32 | "gopkg.in/yaml.v2" 33 | ) 34 | 35 | // ConfigurationsDirectoryExist returns `true` if the configuration directory exist, `false` otherwise 36 | func ConfigurationsDirectoryExist() bool { 37 | return helper.DirOrFileExists(GetAppInfo().ConfigurationsDir) 38 | } 39 | 40 | // ConfigurationsFileExist returns `true` if the configuration file exist, `false` otherwise 41 | func ConfigurationsFileExist() bool { 42 | return helper.DirOrFileExists(GetAppInfo().ConfigurationsPath) 43 | } 44 | 45 | // CreateConfigurationsDirectory creates the configuration directory, returns `true` if the configuration directory exist, `false` otherwise 46 | func CreateConfigurationsDirectory() (bool, string) { 47 | dir := GetAppInfo().ConfigurationsDir 48 | return helper.MkDirsIfNotExist(dir), dir 49 | } 50 | 51 | // CredentialsFileExist returns `true` if the credentials file exist, `false` otherwise 52 | func CredentialsFileExist() bool { 53 | return helper.DirOrFileExists(GetAppInfo().CredentialsPath) 54 | } 55 | 56 | // ReadConfig returns the viper instance of the given configuration `name` 57 | func ReadConfig(name string) (*viper.Viper, error) { 58 | v := viper.New() 59 | app := GetAppInfo() 60 | 61 | v.SetConfigName(name) 62 | v.SetConfigType("yaml") 63 | v.AddConfigPath(app.ConfigurationsDir) 64 | 65 | err := v.ReadInConfig() 66 | if err != nil { 67 | return v, fmt.Errorf("unable to read configuration:%s\n%v", name, err) 68 | } 69 | return v, err 70 | } 71 | 72 | // ReadConfigAsViper returns... 73 | func ReadConfigAsViper(configPath string, configName string, configType string) (*viper.Viper, error) { 74 | v := viper.New() 75 | 76 | v.AddConfigPath(configPath) 77 | v.SetConfigName(configName) 78 | v.SetConfigType(configType) 79 | 80 | err := v.ReadInConfig() 81 | if err != nil { 82 | return v, fmt.Errorf("unable to read configuration as viper\n%v", err) 83 | } 84 | return v, err 85 | } 86 | 87 | // ReadTemplate read the given template under clencli/*.yaml 88 | func ReadTemplate(fileName string) (*viper.Viper, error) { 89 | c := viper.New() 90 | c.AddConfigPath("clencli") 91 | c.SetConfigName(fileName) 92 | c.SetConfigType("yaml") 93 | c.SetConfigPermissions(os.ModePerm) 94 | 95 | err := c.ReadInConfig() // Find and read the c file 96 | if err != nil { // Handle errors reading the c file 97 | return c, fmt.Errorf("Unable to read "+fileName+" via Viper"+"\n%v", err) 98 | } 99 | 100 | return c, nil 101 | } 102 | 103 | // WriteInterfaceToFile write the given interface into a file 104 | func WriteInterfaceToFile(in interface{}, path string) error { 105 | b, err := yaml.Marshal(&in) 106 | if err != nil { 107 | _, ok := err.(*json.UnsupportedTypeError) 108 | if ok { 109 | return fmt.Errorf("json unsupported type error") 110 | } 111 | } 112 | 113 | err = ioutil.WriteFile(path, b, os.ModePerm) 114 | if err != nil { 115 | return fmt.Errorf("unable to update:%s\n%v", path, err) 116 | } 117 | 118 | return err 119 | } 120 | 121 | // DeleteCredentialFile delete the credentials file 122 | func DeleteCredentialFile() error { 123 | return helper.DeleteFile(GetAppInfo().CredentialsPath) 124 | } 125 | 126 | // DeleteConfigurationFile delete the credentials file 127 | func DeleteConfigurationFile() error { 128 | return helper.DeleteFile(GetAppInfo().ConfigurationsPath) 129 | } 130 | 131 | // DeleteConfigurationsDirectory delete the configurations directory 132 | func DeleteConfigurationsDirectory() error { 133 | return os.RemoveAll(GetAppInfo().ConfigurationsDir) 134 | } 135 | 136 | // GetSensitiveUserInput get sensitive input as string 137 | func GetSensitiveUserInput(cmd *cobra.Command, text string, info string) (string, error) { 138 | return getUserInput(cmd, text+" ["+maskString(info, 3)+"]", "") 139 | } 140 | 141 | func maskString(s string, showLastChars int) string { 142 | maskSize := len(s) - showLastChars 143 | if maskSize <= 0 { 144 | return s 145 | } 146 | 147 | return strings.Repeat("*", maskSize) + s[maskSize:] 148 | } 149 | 150 | // GetSensitiveUserInputAsString get sensitive input as string 151 | func GetSensitiveUserInputAsString(cmd *cobra.Command, text string, info string) string { 152 | answer, err := GetSensitiveUserInput(cmd, text, info) 153 | if err != nil { 154 | logrus.Fatalf("unable to get user input about profile's name\n%v", err) 155 | } 156 | 157 | // if user typed ENTER, keep the current value 158 | if answer != "" { 159 | return answer 160 | } 161 | 162 | return info 163 | } 164 | 165 | func getInput() (string, error) { 166 | 167 | reader := bufio.NewReader(os.Stdin) 168 | 169 | text, err := reader.ReadString('\n') 170 | if err != nil { 171 | return "", err 172 | } 173 | 174 | if runtime.GOOS == "windows" { 175 | // convert LF to CRLF 176 | text = strings.Replace(text, "\r\n", "", -1) 177 | } else { 178 | // convert CRLF to LF 179 | text = strings.Replace(text, "\n", "", -1) 180 | } 181 | 182 | return text, nil 183 | } 184 | 185 | func getUserInput(cmd *cobra.Command, text string, info string) (string, error) { 186 | if info == "" { 187 | cmd.Print(text + ": ") 188 | } else { 189 | cmd.Print(text + " [" + info + "]: ") 190 | } 191 | 192 | input, err := getInput() 193 | 194 | return input, err 195 | } 196 | 197 | // GetUserInputAsBool prints `text` on console and return answer as `boolean` 198 | func GetUserInputAsBool(cmd *cobra.Command, text string, info bool) bool { 199 | answer, err := getUserInput(cmd, text, strconv.FormatBool(info)) 200 | if err != nil { 201 | logrus.Fatalf("unable to get user input as boolean\n%s", err) 202 | } 203 | 204 | if answer == "true" { 205 | return true 206 | } else if answer == "false" { 207 | return false 208 | } 209 | 210 | return info 211 | } 212 | 213 | // GetUserInputAsString prints `text` on console and return answer as `string` 214 | func GetUserInputAsString(cmd *cobra.Command, text string, info string) string { 215 | answer, err := getUserInput(cmd, text, info) 216 | if err != nil { 217 | logrus.Fatalf("unable to get user input about profile's name\n%v", err) 218 | } 219 | 220 | // if user typed ENTER, keep the current value 221 | if answer != "" { 222 | return answer 223 | } 224 | 225 | return info 226 | } 227 | -------------------------------------------------------------------------------- /cobra/aid/gitignore.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package aid 17 | 18 | import ( 19 | "fmt" 20 | "io/ioutil" 21 | "net/http" 22 | 23 | "github.com/awslabs/clencli/helper" 24 | "github.com/sirupsen/logrus" 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | // DownloadGitIgnore .. 29 | func DownloadGitIgnore(cmd *cobra.Command, input string) (bool, error) { 30 | bytes, err := requestGitIgnore(cmd, input) 31 | if err != nil { 32 | logrus.Errorf("unable to download gitignore file\n%v", err) 33 | return false, err 34 | } 35 | 36 | if !saveGitIgnoreAsFile(bytes) { 37 | logrus.Errorln("unable to save gitignore API response as file") 38 | return false, fmt.Errorf("unable to create .gitignore file") 39 | } 40 | 41 | return true, nil 42 | } 43 | 44 | // GetGitIgnoreList ... 45 | func GetGitIgnoreList() string { 46 | bytes, err := requestGitIgnoreList() 47 | if err != nil { 48 | logrus.Errorf("unable to fetch the gitignore list") 49 | } 50 | return string(bytes) 51 | } 52 | 53 | func requestGitIgnore(cmd *cobra.Command, input string) ([]byte, error) { 54 | url := fmt.Sprintf("https://www.toptal.com/developers/gitignore/api/%s", input) 55 | var response []byte 56 | 57 | var client http.Client 58 | resp, err := client.Get(url) 59 | if err != nil { 60 | logrus.Errorf("unexpected error while performing GET on Toptal API\n%v", err) 61 | return response, fmt.Errorf("unable to fetch gitignore API\n%v", err) 62 | } 63 | defer resp.Body.Close() 64 | 65 | if resp.StatusCode == http.StatusOK { 66 | response, err = ioutil.ReadAll(resp.Body) 67 | if err != nil { 68 | return response, fmt.Errorf("unexpected error while reading Unsplash response \n%v", err) 69 | } 70 | 71 | return response, nil 72 | } 73 | 74 | return response, err 75 | } 76 | 77 | func requestGitIgnoreList() ([]byte, error) { 78 | url := fmt.Sprintf("https://www.toptal.com/developers/gitignore/api/list") 79 | var response []byte 80 | 81 | var client http.Client 82 | resp, err := client.Get(url) 83 | if err != nil { 84 | logrus.Errorf("unexpected error while performing GET on Toptal API\n%v", err) 85 | return response, fmt.Errorf("unable to fetch gitignore API\n%v", err) 86 | } 87 | defer resp.Body.Close() 88 | 89 | if resp.StatusCode == http.StatusOK { 90 | response, err = ioutil.ReadAll(resp.Body) 91 | if err != nil { 92 | return response, fmt.Errorf("unexpected error while reading Unsplash response \n%v", err) 93 | } 94 | 95 | return response, nil 96 | } 97 | 98 | return response, err 99 | } 100 | 101 | func saveGitIgnoreAsFile(bytes []byte) bool { 102 | return helper.WriteFile(".gitignore", bytes) 103 | } 104 | -------------------------------------------------------------------------------- /cobra/aid/init.go: -------------------------------------------------------------------------------- 1 | package aid 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/awslabs/clencli/cobra/model" 9 | "github.com/awslabs/clencli/helper" 10 | "github.com/sirupsen/logrus" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | /* BASIC PROJECT */ 15 | 16 | // CreateBasicProject creates a basic project 17 | func CreateBasicProject(cmd *cobra.Command, name string) error { 18 | err := createAndEnterProjectDir(name) 19 | if err != nil { 20 | return err 21 | } 22 | 23 | if initalized := initProject(); !initalized { 24 | logrus.Errorf("unable to initialize basic project") 25 | return fmt.Errorf("unable to initalize project %s", name) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | func createAndEnterProjectDir(name string) error { 32 | 33 | if !helper.MkDirsIfNotExist(name) { 34 | return fmt.Errorf("unable to create directory %s", name) 35 | } 36 | 37 | err := os.Chdir(name) 38 | if err != nil { 39 | return fmt.Errorf("unable to enter directory %s", name) 40 | } 41 | 42 | wd, err := os.Getwd() 43 | if err != nil { 44 | return fmt.Errorf("unable to returns a rooted path name corresponding to the current directory:\n%v", err) 45 | } 46 | logrus.Infof("current working directory changed to %s", wd) 47 | 48 | return nil 49 | } 50 | 51 | // create the basic configuration files 52 | func initProject() bool { 53 | 54 | // Create a directory for clencli 55 | a := helper.MkDirsIfNotExist("clencli") 56 | b := helper.WriteFileFromBox("/init/clencli/readme.yaml", "clencli/readme.yaml") 57 | c := helper.WriteFileFromBox("/init/clencli/readme.tmpl", "clencli/readme.tmpl") 58 | d := helper.WriteFileFromBox("/init/.gitignore", ".gitignore") 59 | 60 | return (a && b && c && d) 61 | } 62 | 63 | // InitCustomized TODO... 64 | func InitCustomized(profile string, config model.Configurations) bool { 65 | 66 | for _, p := range config.Profiles { 67 | if p.Name == profile { 68 | for _, c := range p.Configurations { 69 | for _, f := range c.Initialization.Files { 70 | if f.State == "directory" { 71 | if !helper.MkDirsIfNotExist(f.Path) { 72 | logrus.Errorf("unable to create directory based on configuration") 73 | return false 74 | } 75 | } else if f.State == "file" { 76 | if strings.Contains(f.Src, "http") { 77 | if err := helper.DownloadFileTo(f.Src, f.Dest); err != nil { 78 | logrus.Errorf("unable to download file based on configuration") 79 | return false 80 | } 81 | } else { 82 | if err := helper.CopyFileTo(f.Src, f.Dest); err != nil { 83 | logrus.Errorf("unable to copy file based on configuration") 84 | return false 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | return true 94 | } 95 | 96 | /* CLOUD PROJECT */ 97 | 98 | // CreateCloudProject copies the necessary templates for cloud projects 99 | func CreateCloudProject(cmd *cobra.Command, name string) error { 100 | if err := CreateBasicProject(cmd, name); err != nil { 101 | return nil 102 | } 103 | 104 | if initialized := initCloudProject(); !initialized { 105 | logrus.Errorf("unable to initialize cloud project") 106 | return fmt.Errorf("unable to initalize project %s", name) 107 | } 108 | 109 | return nil 110 | } 111 | 112 | // copies the High Level Design template file 113 | func initCloudProject() bool { 114 | a := helper.WriteFileFromBox("/init/clencli/hld.yaml", "clencli/hld.yaml") 115 | b := helper.WriteFileFromBox("/init/clencli/hld.tmpl", "clencli/hld.tmpl") 116 | 117 | return (a && b) 118 | } 119 | 120 | /* CLOUDFORMATION PROJECT */ 121 | 122 | // CreateCloudFormationProject creates an AWS CloudFormation project 123 | func CreateCloudFormationProject(cmd *cobra.Command, name string) error { 124 | if err := CreateBasicProject(cmd, name); err != nil { 125 | return nil 126 | } 127 | 128 | if initialized := initCloudProject(); !initialized { 129 | logrus.Errorf("unable to initialize cloud project") 130 | return fmt.Errorf("unable to initalize project %s", name) 131 | } 132 | 133 | if initialized := initCloudFormationProject(); !initialized { 134 | logrus.Errorf("unable to initialize cloudformation project") 135 | return fmt.Errorf("unable to initalize project %s", name) 136 | } 137 | 138 | return nil 139 | } 140 | 141 | // initialize a project with CloudFormation structure and copies template files 142 | func initCloudFormationProject() bool { 143 | 144 | a := helper.MkDirsIfNotExist("environments") 145 | b := helper.MkDirsIfNotExist("environments/dev") 146 | c := helper.MkDirsIfNotExist("environments/prod") 147 | d := helper.WriteFileFromBox("/init/project/type/clouformation/skeleton.yaml", "skeleton.yaml") 148 | e := helper.WriteFileFromBox("/init/project/type/clouformation/skeleton.json", "skeleton.json") 149 | 150 | /* TODO: copy a template to create standard tags for the entire stack easily 151 | https://docs.aws.amazon.com/cli/latest/reference/cloudformation/create-stack.html 152 | example aws cloudformation create-stack ... --tags */ 153 | 154 | /* TODO: copy Makefile */ 155 | /* TODO: copy LICENSE */ 156 | 157 | return (a && b && c && d && e) 158 | } 159 | 160 | /* TERRAFORM PROJECT */ 161 | 162 | // CreateTerraformProject creates a HashiCorp Terraform project 163 | func CreateTerraformProject(cmd *cobra.Command, name string) error { 164 | if err := CreateBasicProject(cmd, name); err != nil { 165 | return nil 166 | } 167 | 168 | if initialized := initCloudProject(); !initialized { 169 | logrus.Errorf("unable to initialize terraform project") 170 | return fmt.Errorf("unable to initalize project %s", name) 171 | } 172 | 173 | if initialized := initTerraformProject(); !initialized { 174 | logrus.Errorf("unable to initialize cloud project") 175 | return fmt.Errorf("unable to initalize project %s", name) 176 | } 177 | 178 | return nil 179 | } 180 | 181 | // InitTerraform initialize a project with Terraform structure 182 | func initTerraformProject() bool { 183 | a := helper.WriteFileFromBox("/init/project/type/terraform/Makefile", "Makefile") 184 | b := helper.WriteFileFromBox("/init/project/type/terraform/LICENSE", "LICENSE") 185 | 186 | c := helper.MkDirsIfNotExist("environments") 187 | d := helper.WriteFileFromBox("/init/project/type/terraform/environments/dev.tf", "environments/dev.tf") 188 | e := helper.WriteFileFromBox("/init/project/type/terraform/environments/prod.tf", "environments/prod.tf") 189 | 190 | f := helper.WriteFileFromBox("/init/project/type/terraform/main.tf", "main.tf") 191 | g := helper.WriteFileFromBox("/init/project/type/terraform/variables.tf", "variables.tf") 192 | h := helper.WriteFileFromBox("/init/project/type/terraform/outputs.tf", "outputs.tf") 193 | 194 | return (a && b && c && d && e && f && g && h) 195 | 196 | } 197 | 198 | // TODO: allow users to inform additional files to be added to their project initialization 199 | -------------------------------------------------------------------------------- /cobra/aid/render.go: -------------------------------------------------------------------------------- 1 | package aid 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/awslabs/clencli/cobra/model" 10 | "github.com/awslabs/clencli/helper" 11 | gomplateV3 "github.com/hairyhenderson/gomplate/v3" 12 | "github.com/sirupsen/logrus" 13 | ) 14 | 15 | // BuildTemplate build the given template located under clencli/ directory (without the .tmpl extension) 16 | func BuildTemplate(name string) error { 17 | var inputFiles = []string{} 18 | var outputFiles = []string{} 19 | 20 | sep := string(os.PathSeparator) 21 | 22 | if helper.FileExists("clencli" + sep + name + ".tmpl") { 23 | inputFiles = append(inputFiles, "clencli"+sep+name+".tmpl") 24 | outputFiles = append(outputFiles, strings.ToUpper(name)+".md") 25 | } 26 | 27 | var config gomplateV3.Config 28 | config.InputFiles = inputFiles 29 | config.OutputFiles = outputFiles 30 | 31 | dataSources := []string{} 32 | if helper.FileExists("clencli" + sep + name + ".yaml") { 33 | dataSources = append(dataSources, "db=."+sep+"clencli"+sep+name+".yaml") 34 | } 35 | 36 | config.DataSources = dataSources 37 | 38 | err := gomplateV3.RunTemplates(&config) 39 | if err != nil { 40 | logrus.Fatalf("Gomplate.RunTemplates() failed with %s\n", err) 41 | } 42 | 43 | return err 44 | } 45 | 46 | func writeInputs() error { 47 | variables, err := os.Open("variables.tf") 48 | if err != nil { 49 | logrus.Fatal(err) 50 | } 51 | defer variables.Close() 52 | 53 | // create INPUTS.md 54 | inputs, err := os.OpenFile("INPUTS.md", os.O_CREATE|os.O_WRONLY, 0644) 55 | if err != nil { 56 | logrus.Println(err) 57 | } 58 | defer inputs.Close() 59 | 60 | if _, err := inputs.WriteString("| Name | Description | Type | Default | Required |\n|------|-------------|:----:|:-----:|:-----:|\n"); err != nil { 61 | logrus.Println(err) 62 | } 63 | 64 | var varName, varType, varDescription, varDefault string 65 | varRequired := "no" 66 | 67 | // startBlock := false 68 | scanner := bufio.NewScanner(variables) 69 | for scanner.Scan() { 70 | line := scanner.Text() 71 | 72 | // skip empty lines 73 | if len(line) > 0 { 74 | if strings.Contains(line, "variable") && strings.Contains(line, "{") { 75 | out, found := helper.GetStringBetweenDoubleQuotes(line) 76 | if found { 77 | varName = out 78 | } 79 | 80 | } 81 | 82 | if strings.Contains(line, "type") && strings.Contains(line, "=") { 83 | slc := helper.GetStringTrimmed(line, "=") 84 | if slc[0] == "type" { 85 | varType = slc[1] 86 | if strings.Contains(varType, "({") { 87 | slc = helper.GetStringTrimmed(varType, "({") 88 | varType = slc[0] 89 | } 90 | } 91 | } 92 | 93 | if strings.Contains(line, "description") && strings.Contains(line, "=") { 94 | slc := helper.GetStringTrimmed(line, "=") 95 | if slc[0] == "description" { 96 | out, found := helper.GetStringBetweenDoubleQuotes(slc[1]) 97 | if found { 98 | varDescription = out 99 | } 100 | } 101 | } 102 | 103 | if strings.Contains(line, "default") && strings.Contains(line, "=") { 104 | slc := helper.GetStringTrimmed(line, "=") 105 | if slc[0] == "default" { 106 | varDefault = slc[1] 107 | if strings.Contains(varDefault, "{") { 108 | varDefault = "" 109 | } 110 | } 111 | } 112 | 113 | // end of the variable declaration 114 | if strings.Contains(line, "}") && len(line) == 1 { 115 | if len(varName) > 0 && len(varType) > 0 && len(varDescription) > 0 { 116 | 117 | var result string 118 | if len(varDefault) == 0 { 119 | varRequired = "yes" 120 | result = fmt.Sprintf("| %s | %s | %s | %s | %s |\n", varName, varDescription, varType, varDefault, varRequired) 121 | } else { 122 | result = fmt.Sprintf("| %s | %s | %s | `%s` | %s |\n", varName, varDescription, varType, varDefault, varRequired) 123 | } 124 | 125 | if _, err := inputs.WriteString(result); err != nil { 126 | logrus.Println(err) 127 | } 128 | varName, varType, varDescription, varDefault, varRequired = "", "", "", "", "no" 129 | } 130 | } 131 | 132 | } 133 | 134 | } 135 | 136 | if err := scanner.Err(); err != nil { 137 | logrus.Fatal(err) 138 | 139 | } 140 | return err 141 | } 142 | 143 | func writeOutputs() error { 144 | outputs, err := os.Open("outputs.tf") 145 | if err != nil { 146 | logrus.Fatal(err) 147 | } 148 | defer outputs.Close() 149 | 150 | // create INPUTS.md 151 | outs, err := os.OpenFile("OUTPUTS.md", os.O_CREATE|os.O_WRONLY, 0644) 152 | if err != nil { 153 | logrus.Println(err) 154 | } 155 | defer outs.Close() 156 | 157 | if _, err := outs.WriteString("| Name | Description |\n|------|-------------|\n"); err != nil { 158 | logrus.Println(err) 159 | } 160 | 161 | var outName, outDescription string 162 | 163 | scanner := bufio.NewScanner(outputs) 164 | for scanner.Scan() { 165 | line := scanner.Text() 166 | 167 | // skip empty lines 168 | if len(line) > 0 { 169 | if strings.Contains(line, "output") && strings.Contains(line, "{") { 170 | out, found := helper.GetStringBetweenDoubleQuotes(line) 171 | if found { 172 | outName = out 173 | } 174 | } 175 | 176 | if strings.Contains(line, "description") && strings.Contains(line, "=") { 177 | slc := helper.GetStringTrimmed(line, "=") 178 | if slc[0] == "description" { 179 | out, found := helper.GetStringBetweenDoubleQuotes(slc[1]) 180 | if found { 181 | outDescription = out 182 | } 183 | } 184 | } 185 | 186 | // end of the output declaration 187 | if strings.Contains(line, "}") && len(line) == 1 { 188 | if len(outName) > 0 && len(outDescription) > 0 { 189 | 190 | result := fmt.Sprintf("| %s | %s | |\n", outName, outDescription) 191 | 192 | if _, err := outs.WriteString(result); err != nil { 193 | logrus.Println(err) 194 | } 195 | outName, outDescription = "", "" 196 | } 197 | } 198 | 199 | } 200 | 201 | } 202 | 203 | if err := scanner.Err(); err != nil { 204 | logrus.Fatal(err) 205 | 206 | } 207 | return err 208 | } 209 | 210 | // UpdateReadMeLogoURL TODO ... 211 | func UpdateReadMeLogoURL(readme model.ReadMe, response model.UnsplashRandomPhotoResponse) error { 212 | readme.Logo.Label = "Photo by [" + response.User.Name + "](https://unsplash.com/" + response.User.Username + ") on [Unsplash](https://unsplash.com)" 213 | readme.Logo.URL = response.Urls.Regular 214 | err := WriteInterfaceToFile(readme, "clencli/readme.yaml") 215 | if err != nil { 216 | return fmt.Errorf("unable to save new readme template\n%v", err) 217 | } 218 | 219 | return nil 220 | } 221 | -------------------------------------------------------------------------------- /cobra/aid/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package aid 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/awslabs/clencli/cobra/model" 23 | "github.com/sirupsen/logrus" 24 | ) 25 | 26 | // GetAppInfo return information about clencli settings 27 | func GetAppInfo() model.App { 28 | var err error 29 | 30 | configDir, err := os.UserConfigDir() 31 | if err != nil { 32 | logrus.Fatalln("unable to read user configuration directory\n%v", err) 33 | } 34 | 35 | sep := string(os.PathSeparator) 36 | 37 | var app model.App 38 | app.Name = "clencli" 39 | app.ConfigurationsDir = configDir + sep + app.Name 40 | app.ConfigurationsName = "configurations" 41 | app.ConfigurationsType = "yaml" 42 | app.ConfigurationsPath = app.ConfigurationsDir + sep + app.ConfigurationsName + "." + app.ConfigurationsType 43 | app.ConfigurationsPermissions = os.ModePerm 44 | app.CredentialsName = "credentials" 45 | app.CredentialsType = "yaml" 46 | app.CredentialsPath = app.ConfigurationsDir + sep + app.CredentialsName + "." + app.CredentialsType 47 | app.CredentialsPermissions = os.ModePerm 48 | app.LogsDir = app.ConfigurationsDir 49 | app.LogsName = "logs" 50 | app.LogsType = "json" 51 | app.LogsPath = app.LogsDir + sep + app.LogsName + "." + app.LogsType 52 | app.LogsPermissions = os.ModePerm 53 | 54 | app.WorkingDir, err = os.Getwd() 55 | if err != nil { 56 | fmt.Printf("Unable to detect the current directory\n%v", err) 57 | os.Exit(1) 58 | } 59 | 60 | return app 61 | } 62 | 63 | // SetupLoggingOutput set logrun output file 64 | func SetupLoggingOutput(path string) error { 65 | if path == "" { 66 | app := GetAppInfo() 67 | path = app.LogsPath 68 | } 69 | 70 | file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm) 71 | if err != nil { 72 | return fmt.Errorf("unable to open log file\n%v", err) 73 | } 74 | 75 | logrus.SetFormatter(&logrus.JSONFormatter{}) 76 | logrus.SetOutput(file) 77 | 78 | return nil 79 | } 80 | 81 | // SetupLoggingLevel set logrus level 82 | func SetupLoggingLevel(level string) error { 83 | lvl, err := logrus.ParseLevel(level) 84 | if err != nil { 85 | return fmt.Errorf("unable to set log level\n%v", err) 86 | } 87 | 88 | logrus.SetLevel(lvl) 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /cobra/aid/unsplash.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package aid 17 | 18 | import ( 19 | "encoding/json" 20 | "fmt" 21 | "io/ioutil" 22 | "net/http" 23 | 24 | "github.com/awslabs/clencli/cobra/model" 25 | "github.com/awslabs/clencli/helper" 26 | "github.com/sirupsen/logrus" 27 | "github.com/spf13/cobra" 28 | "gopkg.in/yaml.v2" 29 | ) 30 | 31 | // GetModelFromFlags fills the parameters onto the Unsplash Random Photo Parameters struct 32 | func GetModelFromFlags(cmd *cobra.Command) model.UnsplashRandomPhotoParameters { 33 | var params model.UnsplashRandomPhotoParameters 34 | 35 | params.Query, _ = cmd.Flags().GetString("query") 36 | params.Collections, _ = cmd.Flags().GetString("collections") 37 | params.Featured, _ = cmd.Flags().GetBool("featured") 38 | params.Username, _ = cmd.Flags().GetString("username") 39 | params.Orientation, _ = cmd.Flags().GetString("orientation") 40 | params.Filter, _ = cmd.Flags().GetString("filter") 41 | params.Size, _ = cmd.Flags().GetString("size") 42 | 43 | return params 44 | } 45 | 46 | func buildURL(params model.UnsplashRandomPhotoParameters, cred model.Credential) string { 47 | clientID := cred.AccessKey 48 | url := fmt.Sprintf("https://api.unsplash.com/photos/random?client_id=%s", clientID) 49 | 50 | if len(params.Collections) > 0 { 51 | url += fmt.Sprintf("&collections=%s", params.Collections) 52 | } 53 | 54 | if len(params.Query) > 0 { 55 | url += fmt.Sprintf("&query=%s", params.Query) 56 | } 57 | 58 | url += fmt.Sprintf("&featured=%t", params.Featured) 59 | 60 | if len(params.Username) > 0 { 61 | url += fmt.Sprintf("&username=%s", params.Username) 62 | } 63 | 64 | if len(params.Orientation) > 0 { 65 | url += fmt.Sprintf("&orientation=%s", params.Orientation) 66 | } 67 | 68 | if len(params.Filter) > 0 { 69 | url += fmt.Sprintf("&filter=%s", params.Filter) 70 | } 71 | 72 | return url 73 | } 74 | 75 | // DownloadPhoto downloads a photo and saves into downloads/unsplash/ folder 76 | // It creates the downloads/ folder if it doesn't exists 77 | func DownloadPhoto(params model.UnsplashRandomPhotoParameters, cred model.Credential, photoSizes []string) error { 78 | response, err := RequestRandomPhoto(params, cred) 79 | if err != nil { 80 | return err 81 | } 82 | 83 | dumpUnsplashRandomPhotoResponse(response) 84 | 85 | dirPath, err := helper.CreateDirectoryNamedPath("downloads/unsplash/" + params.Query) 86 | if err != nil { 87 | return err 88 | } 89 | 90 | if params.Size == "all" { 91 | for _, pSize := range photoSizes { 92 | if pSize != "all" { 93 | params.Size = pSize 94 | err = helper.DownloadFile(getPhotoURLBySize(params, response), dirPath, response.ID+"-"+pSize+".jpeg") 95 | if err != nil { 96 | return err 97 | } 98 | } 99 | } 100 | } else { 101 | err = helper.DownloadFile(getPhotoURLBySize(params, response), dirPath, response.ID+".jpeg") 102 | } 103 | 104 | return err 105 | } 106 | 107 | // DownloadPhotoByID TODO ... 108 | func DownloadPhotoByID(r model.UnsplashGetPhotoResponse, size string) error { 109 | dirPath, err := helper.CreateDirectoryNamedPath("downloads/unsplash/" + r.ID) 110 | if err != nil { 111 | return err 112 | } 113 | 114 | if size == "raw" || size == "all" { 115 | err = helper.DownloadFile(r.Urls.Raw, dirPath, r.ID+"-raw.jpeg") 116 | if err != nil { 117 | return fmt.Errorf("unable to download photo\n%v", err) 118 | } 119 | } 120 | 121 | if size == "full" || size == "all" { 122 | err = helper.DownloadFile(r.Urls.Full, dirPath, r.ID+"-full.jpeg") 123 | if err != nil { 124 | return fmt.Errorf("unable to download photo\n%v", err) 125 | } 126 | } 127 | 128 | if size == "regular" || size == "all" { 129 | err = helper.DownloadFile(r.Urls.Regular, dirPath, r.ID+"-regular.jpeg") 130 | if err != nil { 131 | return fmt.Errorf("unable to download photo\n%v", err) 132 | } 133 | } 134 | 135 | if size == "small" || size == "all" { 136 | err = helper.DownloadFile(r.Urls.Small, dirPath, r.ID+"-small.jpeg") 137 | if err != nil { 138 | return fmt.Errorf("unable to download photo\n%v", err) 139 | } 140 | } 141 | 142 | if size == "thumb" || size == "all" { 143 | err = helper.DownloadFile(r.Urls.Thumb, dirPath, r.ID+"-thumb.jpeg") 144 | if err != nil { 145 | return fmt.Errorf("unable to download photo\n%v", err) 146 | } 147 | } 148 | 149 | return err 150 | 151 | } 152 | 153 | // GetPhotoURLBySize return the photo URL based on the given size 154 | func getPhotoURLBySize(p model.UnsplashRandomPhotoParameters, r model.UnsplashRandomPhotoResponse) string { 155 | switch p.Size { 156 | case "thumb": 157 | return r.Urls.Thumb 158 | case "small": 159 | return r.Urls.Small 160 | case "regular": 161 | return r.Urls.Regular 162 | case "full": 163 | return r.Urls.Full 164 | case "raw": 165 | return r.Urls.Raw 166 | default: 167 | return r.Urls.Small 168 | } 169 | } 170 | 171 | // RequestRandomPhoto retrieves a single random photo, given optional filters. 172 | func RequestRandomPhoto(params model.UnsplashRandomPhotoParameters, cred model.Credential) (model.UnsplashRandomPhotoResponse, error) { 173 | var response model.UnsplashRandomPhotoResponse 174 | url := buildURL(params, cred) 175 | 176 | var client http.Client 177 | resp, err := client.Get(url) 178 | if err != nil { 179 | return response, fmt.Errorf("unexpected error while performing GET on Unsplash API \n%v", err) 180 | } 181 | defer resp.Body.Close() 182 | 183 | if resp.StatusCode == http.StatusOK { 184 | bodyBytes, err := ioutil.ReadAll(resp.Body) 185 | if err != nil { 186 | return response, fmt.Errorf("unexpected error while reading Unsplash response \n%v", err) 187 | } 188 | 189 | json.Unmarshal(bodyBytes, &response) 190 | } 191 | 192 | return response, err 193 | } 194 | 195 | // GetPhoto TODO.. 196 | func GetPhoto(id string, cred model.Credential) (model.UnsplashGetPhotoResponse, error) { 197 | var response model.UnsplashGetPhotoResponse 198 | 199 | clientID := cred.AccessKey 200 | url := fmt.Sprintf("https://api.unsplash.com/photos/%s?client_id=%s", id, clientID) 201 | 202 | var client http.Client 203 | resp, err := client.Get(url) 204 | if err != nil { 205 | return response, fmt.Errorf("unexpected error while performing GET on Unsplash API \n%v", err) 206 | } 207 | defer resp.Body.Close() 208 | 209 | if resp.StatusCode == http.StatusOK { 210 | bodyBytes, err := ioutil.ReadAll(resp.Body) 211 | if err != nil { 212 | return response, fmt.Errorf("unexpected error while reading Unsplash response \n%v", err) 213 | } 214 | 215 | json.Unmarshal(bodyBytes, &response) 216 | } 217 | 218 | return response, err 219 | } 220 | 221 | // SaveGetPhotoResult TODO ... 222 | func SaveGetPhotoResult(r model.UnsplashGetPhotoResponse) { 223 | d, err := yaml.Marshal(r) 224 | if err != nil { 225 | logrus.Fatalf("error: %v", err) 226 | } 227 | helper.WriteFile("unsplash.yaml", d) 228 | } 229 | 230 | func dumpUnsplashRandomPhotoResponse(r model.UnsplashRandomPhotoResponse) { 231 | d, err := yaml.Marshal(r) 232 | if err != nil { 233 | logrus.Fatalf("error: %v", err) 234 | } 235 | helper.WriteFile("unsplash.yaml", d) 236 | } 237 | -------------------------------------------------------------------------------- /cobra/cmd/configure.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package cmd 17 | 18 | import ( 19 | controller "github.com/awslabs/clencli/cobra/controller" 20 | ) 21 | 22 | var configureCmd = controller.ConfigureCmd() 23 | 24 | func init() { 25 | rootCmd.AddCommand(configureCmd) 26 | } 27 | -------------------------------------------------------------------------------- /cobra/cmd/gitignore.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package cmd 17 | 18 | import "github.com/awslabs/clencli/cobra/controller" 19 | 20 | var gitignoreCmd = controller.GitIgnoreCmd() 21 | 22 | func init() { 23 | rootCmd.AddCommand(gitignoreCmd) 24 | } 25 | -------------------------------------------------------------------------------- /cobra/cmd/init.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package cmd 17 | 18 | import "github.com/awslabs/clencli/cobra/controller" 19 | 20 | var initCmd = controller.InitCmd() 21 | 22 | func init() { 23 | rootCmd.AddCommand(initCmd) 24 | } 25 | -------------------------------------------------------------------------------- /cobra/cmd/render.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package cmd 17 | 18 | import controller "github.com/awslabs/clencli/cobra/controller" 19 | 20 | var renderCmd = controller.RenderCmd() 21 | 22 | func init() { 23 | rootCmd.AddCommand(renderCmd) 24 | } 25 | -------------------------------------------------------------------------------- /cobra/cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package cmd 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/spf13/cobra" 23 | 24 | "github.com/awslabs/clencli/cobra/aid" 25 | "github.com/awslabs/clencli/cobra/controller" 26 | 27 | "github.com/spf13/viper" 28 | ) 29 | 30 | var rootCmd = controller.RootCmd() 31 | 32 | // Execute adds all child commands to the root command and sets flags appropriately. 33 | // This is called by main.main(). It only needs to happen once to the rootCmd. 34 | func Execute() { 35 | if err := rootCmd.Execute(); err != nil { 36 | fmt.Println(err) 37 | os.Exit(1) 38 | } 39 | } 40 | 41 | func init() { 42 | cobra.OnInitialize(initConfig) 43 | 44 | rootCmd.PersistentFlags().StringP("verbosity", "v", "error", "Valid log level:panic,fatal,error,warn,info,debug,trace).") 45 | rootCmd.PersistentFlags().Bool("log", true, "Enable or disable logs (can be found at ./clencli/log.json). Log outputs will be redirected default output if disabled.") 46 | rootCmd.PersistentFlags().String("log-file-path", "clencli/log.json", "Log file path. Requires log=true, ignored otherwise.") 47 | } 48 | 49 | // initConfig reads in config file and ENV variables if set. 50 | func initConfig() { 51 | app := aid.GetAppInfo() 52 | viper.AddConfigPath(app.ConfigurationsDir) // global directory 53 | viper.SetConfigName(app.ConfigurationsName) 54 | viper.AutomaticEnv() // read in environment variables that match 55 | 56 | // If a config file is found, read it in. 57 | if err := viper.ReadInConfig(); err == nil { 58 | fmt.Println("using config file:", viper.ConfigFileUsed()) 59 | } 60 | 61 | verbosity, err := rootCmd.Flags().GetString("verbosity") 62 | if err != nil { 63 | fmt.Printf("unable to read flag verbosity\n%v", err) 64 | } 65 | 66 | log, err := rootCmd.Flags().GetBool("log") 67 | if err != nil { 68 | fmt.Printf("unable to read flag err\n%v", err) 69 | } 70 | 71 | logFilePath, err := rootCmd.Flags().GetString("log-file-path") 72 | if err != nil { 73 | fmt.Printf("unable to read flag log-file-path\n%v", err) 74 | } 75 | 76 | if log && logFilePath != "" { 77 | if err := aid.SetupLoggingLevel(verbosity); err == nil { 78 | fmt.Printf("logging level: %s\n", verbosity) 79 | } 80 | 81 | if err := aid.SetupLoggingOutput(logFilePath); err == nil { 82 | fmt.Printf("logging path: %s\n", logFilePath) 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /cobra/cmd/unsplash.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package cmd 17 | 18 | import controller "github.com/awslabs/clencli/cobra/controller" 19 | 20 | var unsplashCmd = controller.UnsplashCmd() 21 | 22 | func init() { 23 | rootCmd.AddCommand(unsplashCmd) 24 | } 25 | -------------------------------------------------------------------------------- /cobra/cmd/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package cmd 17 | 18 | import controller "github.com/awslabs/clencli/cobra/controller" 19 | 20 | var versionCmd = controller.VersionCmd() 21 | 22 | func init() { 23 | rootCmd.AddCommand(versionCmd) 24 | } 25 | -------------------------------------------------------------------------------- /cobra/controller/configure.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package controller 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/awslabs/clencli/cobra/aid" 23 | "github.com/awslabs/clencli/cobra/dao" 24 | "github.com/awslabs/clencli/cobra/view" 25 | "github.com/awslabs/clencli/helper" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | var configureValidArgs = []string{"delete"} 30 | 31 | // ConfigureCmd command to display clencli current version 32 | func ConfigureCmd() *cobra.Command { 33 | man, err := helper.GetManual("configure") 34 | if err != nil { 35 | fmt.Println(err) 36 | os.Exit(1) 37 | } 38 | 39 | cmd := &cobra.Command{ 40 | Use: man.Use, 41 | Short: man.Short, 42 | Long: man.Long, 43 | Example: man.Example, 44 | ValidArgs: configureValidArgs, 45 | Args: cobra.OnlyValidArgs, 46 | RunE: configureRun, 47 | } 48 | 49 | return cmd 50 | } 51 | 52 | func configureRun(cmd *cobra.Command, args []string) error { 53 | if !aid.ConfigurationsDirectoryExist() { 54 | if created, dir := aid.CreateConfigurationsDirectory(); created { 55 | cmd.Printf("clencli configuration directory created at %s\n", dir) 56 | createCredentials(cmd) 57 | createConfigurations(cmd) 58 | } 59 | } else { 60 | // configurations directory exist 61 | if !aid.CredentialsFileExist() { 62 | createCredentials(cmd) 63 | } else { 64 | updateCredentials(cmd) 65 | } 66 | 67 | if !aid.ConfigurationsFileExist() { 68 | createConfigurations(cmd) 69 | } else { 70 | updateConfigurations(cmd) 71 | } 72 | } 73 | 74 | return nil 75 | } 76 | 77 | func createCredentials(cmd *cobra.Command) { 78 | answer := aid.GetUserInputAsBool(cmd, "Would you like to setup credentials?", false) 79 | if answer { 80 | credentials := view.CreateCredentials(cmd, profile) 81 | dao.SaveCredentials(credentials) 82 | } 83 | } 84 | 85 | func updateCredentials(cmd *cobra.Command) { 86 | answer := aid.GetUserInputAsBool(cmd, "Would you like to update credentials?", false) 87 | if answer { 88 | credentials := view.UpdateCredentials(cmd, profile) 89 | dao.SaveCredentials(credentials) 90 | } 91 | } 92 | 93 | func createConfigurations(cmd *cobra.Command) { 94 | answer := aid.GetUserInputAsBool(cmd, "Would you like to setup configurations?", false) 95 | if answer { 96 | configurations := view.CreateConfigurations(cmd, profile) 97 | dao.SaveConfigurations(configurations) 98 | } 99 | } 100 | 101 | func updateConfigurations(cmd *cobra.Command) { 102 | answer := aid.GetUserInputAsBool(cmd, "Would you like to update configurations?", false) 103 | if answer { 104 | configurations := view.UpdateConfigurations(cmd, profile) 105 | dao.SaveConfigurations(configurations) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /cobra/controller/gitignore.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package controller 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/awslabs/clencli/cobra/aid" 23 | "github.com/awslabs/clencli/helper" 24 | "github.com/sirupsen/logrus" 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | var gitIgnoreArgs = []string{"list"} 29 | 30 | // GitIgnoreCmd .... 31 | func GitIgnoreCmd() *cobra.Command { 32 | man, err := helper.GetManual("gitignore") 33 | if err != nil { 34 | fmt.Println(err) 35 | os.Exit(1) 36 | } 37 | 38 | cmd := &cobra.Command{ 39 | Use: man.Use, 40 | Short: man.Short, 41 | Long: man.Long, 42 | Example: man.Example, 43 | PreRunE: gitIgnorePreRun, 44 | RunE: gitIgnoreRun, 45 | } 46 | 47 | cmd.Flags().StringP("input", "i", "", "Gitignore input. If multiple, comma-separated") 48 | 49 | return cmd 50 | } 51 | 52 | func gitIgnorePreRun(cmd *cobra.Command, args []string) error { 53 | logrus.Traceln("start: command gitignore pre-run") 54 | 55 | if len(args) == 0 { 56 | input, err := cmd.Flags().GetString("input") 57 | 58 | if err != nil { 59 | logrus.Errorf("unable to access flag input\n%v", err) 60 | return err 61 | } 62 | 63 | if input == "" { 64 | logrus.Errorln("no flag or argument provided") 65 | return fmt.Errorf("no flag or argument provided") 66 | } 67 | } else if len(args) == 1 && args[0] != "list" { 68 | logrus.Errorf("unknow argument passed: %v", args) 69 | return fmt.Errorf("unknown argument provided: %s", args[0]) 70 | } 71 | 72 | logrus.Traceln("end: command gitignore pre-run") 73 | 74 | return nil 75 | } 76 | 77 | func gitIgnoreRun(cmd *cobra.Command, args []string) error { 78 | logrus.Traceln("start: command gitignore run") 79 | 80 | if len(args) > 0 && args[0] == "list" { 81 | list := aid.GetGitIgnoreList() 82 | if list == "" { 83 | return fmt.Errorf("unable to get gitignore list") 84 | } 85 | 86 | cmd.Println(list) 87 | } else { 88 | input, err := cmd.Flags().GetString("input") 89 | if err != nil { 90 | return err 91 | } 92 | 93 | downloaded, err := aid.DownloadGitIgnore(cmd, input) 94 | if err != nil { 95 | logrus.Errorf("unable to download gitignore\n%v", err) 96 | return err 97 | } 98 | 99 | if downloaded { 100 | cmd.Println(".gitignore created successfully") 101 | } 102 | } 103 | 104 | logrus.Traceln("end: command gitignore run") 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /cobra/controller/init.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package controller 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "os" 22 | 23 | "github.com/awslabs/clencli/cobra/aid" 24 | "github.com/awslabs/clencli/cobra/dao" 25 | "github.com/awslabs/clencli/helper" 26 | "github.com/sirupsen/logrus" 27 | "github.com/spf13/cobra" 28 | ) 29 | 30 | var initValidArgs = []string{"project"} 31 | var initValidProjectTypes = []string{"basic", "cloud", "cloudformation", "terraform"} 32 | 33 | // InitCmd command to initialize projects 34 | func InitCmd() *cobra.Command { 35 | man, err := helper.GetManual("init") 36 | if err != nil { 37 | fmt.Println(err) 38 | os.Exit(1) 39 | } 40 | 41 | cmd := &cobra.Command{ 42 | Use: man.Use, 43 | Short: man.Short, 44 | Long: man.Long, 45 | Example: man.Example, 46 | ValidArgs: initValidArgs, 47 | Args: cobra.OnlyValidArgs, 48 | PreRunE: initPreRun, 49 | RunE: initRun, 50 | } 51 | 52 | cmd.Flags().String("project-name", "", "The project name.") 53 | cmd.Flags().String("project-type", "basic", "The project type.") 54 | cmd.MarkFlagRequired("name") 55 | 56 | return cmd 57 | } 58 | 59 | func initPreRun(cmd *cobra.Command, args []string) error { 60 | logrus.Traceln("start: command init pre-run") 61 | 62 | if err := helper.ValidateCmdArgs(cmd, args, "init"); err != nil { 63 | return err 64 | } 65 | 66 | if err := helper.ValidateCmdArgAndFlag(cmd, args, "init", "project", "project-name"); err != nil { 67 | return err 68 | } 69 | 70 | if err := helper.ValidateCmdArgAndFlag(cmd, args, "init", "project", "project-type"); err != nil { 71 | return err 72 | } 73 | 74 | logrus.Traceln("end: command init pre-run") 75 | return nil 76 | } 77 | 78 | func initRun(cmd *cobra.Command, args []string) error { 79 | logrus.Traceln("start: command init run") 80 | 81 | cmd.SilenceUsage = true 82 | 83 | if args[0] == "project" { 84 | 85 | pName, err := cmd.Flags().GetString("project-name") 86 | if err != nil { 87 | logrus.Errorf("unable to access --project-name\n%v", err) 88 | return fmt.Errorf("unable to access --project-name\n%v", err) 89 | } 90 | 91 | pType, err := cmd.Flags().GetString("project-type") 92 | if err != nil { 93 | logrus.Errorf("unable to access --project-type\n%v", err) 94 | return fmt.Errorf("unable to access --project-type\n%v", err) 95 | } 96 | 97 | switch pType { 98 | case "basic": 99 | err = aid.CreateBasicProject(cmd, pName) 100 | case "cloud": 101 | err = aid.CreateCloudProject(cmd, pName) 102 | case "cloudformation": 103 | err = aid.CreateCloudFormationProject(cmd, pName) 104 | case "terraform": 105 | err = aid.CreateTerraformProject(cmd, pName) 106 | default: 107 | return errors.New("unknow project type") 108 | } 109 | 110 | if aid.ConfigurationsDirectoryExist() && aid.ConfigurationsFileExist() { 111 | config, err := dao.GetConfigurations() 112 | if err != nil { 113 | logrus.Errorf("unable to get configuration during initialization\n%v", err) 114 | return fmt.Errorf("unable to initialize project based on configurations\n%v", err) 115 | } 116 | 117 | created := aid.InitCustomized(profile, config) 118 | if !created { 119 | return fmt.Errorf("unable to initialize project based on configurations\n%s", err) 120 | } 121 | 122 | } 123 | 124 | if err != nil { 125 | return fmt.Errorf("unable to initialize project sucessfully \n%s", err) 126 | } 127 | 128 | cmd.Printf("%s was successfully initialized as a %s project\n", pName, pType) 129 | 130 | } 131 | 132 | logrus.Traceln("end: command init run") 133 | return nil 134 | } 135 | -------------------------------------------------------------------------------- /cobra/controller/render.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package controller 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "os" 22 | "strings" 23 | 24 | "github.com/awslabs/clencli/cobra/aid" 25 | "github.com/awslabs/clencli/cobra/dao" 26 | "github.com/awslabs/clencli/cobra/model" 27 | "github.com/awslabs/clencli/helper" 28 | "github.com/sirupsen/logrus" 29 | "github.com/spf13/cobra" 30 | ) 31 | 32 | var renderValidArgs = []string{"template"} 33 | 34 | // RenderCmd command to render templates 35 | func RenderCmd() *cobra.Command { 36 | man, err := helper.GetManual("render") 37 | if err != nil { 38 | fmt.Println(err) 39 | os.Exit(1) 40 | } 41 | 42 | cmd := &cobra.Command{ 43 | Use: man.Use, 44 | Short: man.Short, 45 | Long: man.Long, 46 | ValidArgs: renderValidArgs, 47 | Args: cobra.OnlyValidArgs, 48 | PreRunE: renderPreRun, 49 | RunE: renderRun, 50 | } 51 | 52 | cmd.Flags().StringP("name", "n", "readme", "Database file name of the template to be rendered (it must be under clencli/ directory.") 53 | 54 | return cmd 55 | } 56 | 57 | func renderPreRun(cmd *cobra.Command, args []string) error { 58 | logrus.Traceln("start: command render pre-run") 59 | 60 | if err := helper.ValidateCmdArgs(cmd, args, "render"); err != nil { 61 | return err 62 | } 63 | 64 | if err := helper.ValidateCmdArgAndFlag(cmd, args, "render", "template", "name"); err != nil { 65 | return err 66 | } 67 | 68 | name, err := cmd.Flags().GetString("name") 69 | if err != nil { 70 | logrus.Errorf("error: unable to access flag name\n%v", err) 71 | return fmt.Errorf("unable to access flag name\n%v", err) 72 | } 73 | 74 | path := "clencli/" + name + ".yaml" 75 | if !helper.FileExists(path) { 76 | logrus.Errorf("missing database " + path) 77 | return errors.New("missing database " + path) 78 | } 79 | 80 | path = "clencli/" + name + ".tmpl" 81 | if !helper.FileExists(path) { 82 | logrus.Errorf("missing template " + path) 83 | return errors.New("missing template " + path) 84 | } 85 | 86 | logrus.Traceln("end: command render pre-run") 87 | return nil 88 | } 89 | 90 | func renderRun(cmd *cobra.Command, args []string) error { 91 | logrus.Traceln("start: command render run") 92 | 93 | name, err := cmd.Flags().GetString("name") 94 | if err != nil { 95 | logrus.Errorf("error: unable to render template "+name+"\n%v", err) 96 | return fmt.Errorf("unable to render template "+name+"\n%v", err) 97 | } 98 | 99 | // TODO: fix this, causing issues on Windows 100 | 101 | // remove any trailing whitespaces 102 | // path := "./clencli/" + name + ".yaml" 103 | // if err := helper.TrimRightFile(path, true); err != nil { 104 | // logrus.Errorf("unexpected err: %v", err) 105 | // return fmt.Errorf("unable to remove white spaces from %s.yaml\n%v", name, err) 106 | // } 107 | 108 | if err := updateLogo(profile); err != nil { 109 | logrus.Errorf("Unexpected error: %v", err) 110 | return fmt.Errorf("unable to update logo url\n%v", err) 111 | } 112 | 113 | if err := aid.BuildTemplate(name); err != nil { 114 | logrus.Errorf("Unexpected error: %v", err) 115 | return fmt.Errorf("unable to render template "+name+"\n%v", err) 116 | } 117 | 118 | cmd.Println("Template " + name + ".tmpl rendered as " + strings.ToUpper(name) + ".md.") 119 | 120 | logrus.Traceln("end: command render run") 121 | return nil 122 | } 123 | 124 | func updateLogo(profile string) error { 125 | 126 | if !updateLogoFromUnsplashFile() { 127 | return updateLogoFromConfigurations(profile) 128 | } 129 | 130 | return nil 131 | } 132 | 133 | func updateLogoFromUnsplashFile() bool { 134 | if helper.FileExists("unsplash.yaml") { 135 | configPath, _ := os.Getwd() 136 | configName := "unsplash" 137 | configType := "yaml" 138 | 139 | var response model.UnsplashRandomPhotoResponse 140 | 141 | v, err := aid.ReadConfigAsViper(configPath, configName, configType) 142 | if err != nil { 143 | logrus.Errorf("unable to read unsplash.yaml as viper object\n%v", err) 144 | return false 145 | } 146 | 147 | err = v.Unmarshal(&response) 148 | if err != nil { 149 | logrus.Errorf("unable to unmarshall unsplash.yaml as unsplash response\n%v", err) 150 | return false 151 | } 152 | 153 | err = helper.DownloadFile(response.Urls.Regular, "clencli", "logo.jpeg") 154 | if err != nil { 155 | logrus.Errorf("unable to download photo\n%v", err) 156 | return false 157 | } 158 | 159 | response.Urls.Regular = "clencli/logo.jpeg" 160 | 161 | readMe, err := dao.GetReadMe() 162 | if err != nil { 163 | logrus.Errorf("Unable to get local readme config\n%v", err) 164 | return false 165 | } 166 | 167 | err = aid.UpdateReadMeLogoURL(readMe, response) 168 | if err != nil { 169 | logrus.Errorf("unable to update logo URL\n%s", err) 170 | return false 171 | } 172 | 173 | return true 174 | } 175 | 176 | return false 177 | } 178 | 179 | func updateLogoFromConfigurations(profile string) error { 180 | if aid.ConfigurationsDirectoryExist() { 181 | if aid.CredentialsFileExist() && aid.ConfigurationsFileExist() { 182 | 183 | // ignore error, as credentials doesn't exist 184 | cred, err := dao.GetCredentialByProvider(profile, "unsplash") 185 | if err != nil { 186 | logrus.Warnf("no unsplash credential found\n%v", err) 187 | return nil 188 | } 189 | 190 | if cred.AccessKey != "" && cred.SecretKey != "" { 191 | readMe, err := dao.GetReadMe() 192 | if err != nil { 193 | return fmt.Errorf("Unable to get local readme config\n%v", err) 194 | } 195 | 196 | params := dao.GetUnsplashRandomPhotoParameters(profile) 197 | if (model.UnsplashRandomPhotoParameters{}) == params { 198 | logrus.Warnf("no unsplash random photo parameters configuration found or enabled\n%v", err) 199 | return nil 200 | } 201 | 202 | response, err := aid.RequestRandomPhoto(params, cred) 203 | if err != nil { 204 | logrus.Warnf("unable to fetch response from unsplash during render command\n%v", err) 205 | return err 206 | } 207 | 208 | err = aid.UpdateReadMeLogoURL(readMe, response) 209 | if err != nil { 210 | return fmt.Errorf("unable to update logo URL\n%s", err) 211 | } 212 | } 213 | } 214 | } 215 | 216 | return nil 217 | } 218 | -------------------------------------------------------------------------------- /cobra/controller/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package controller 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/awslabs/clencli/helper" 23 | "github.com/spf13/cobra" 24 | ) 25 | 26 | var profile string 27 | 28 | // RootCmd represents the base command when called without any subcommands 29 | func RootCmd() *cobra.Command { 30 | man, err := helper.GetManual("root") 31 | if err != nil { 32 | fmt.Println(err) 33 | os.Exit(1) 34 | } 35 | 36 | cmd := &cobra.Command{ 37 | Use: man.Use, 38 | Short: man.Short, 39 | Long: man.Long, 40 | } 41 | 42 | // Here you will define your flags and configuration settings. 43 | // Cobra supports persistent flags, which, if defined here will be global for your application. 44 | cmd.PersistentFlags().StringVarP(&profile, "profile", "p", "default", "Use a specific profile from your credentials and configurations file.") 45 | 46 | // TODO: allow users to pass their prefer location for clencli's configurations directory 47 | 48 | return cmd 49 | } 50 | -------------------------------------------------------------------------------- /cobra/controller/unsplash.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package controller 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/awslabs/clencli/cobra/aid" 23 | "github.com/awslabs/clencli/cobra/dao" 24 | "github.com/awslabs/clencli/cobra/model" 25 | "github.com/awslabs/clencli/helper" 26 | "github.com/sirupsen/logrus" 27 | "github.com/spf13/cobra" 28 | ) 29 | 30 | var unsplashPhotoSizes = []string{"all", "thumb", "small", "regular", "full", "raw"} 31 | 32 | // UnsplashCmd command to download photos from Unsplash.com 33 | func UnsplashCmd() *cobra.Command { 34 | man, err := helper.GetManual("unsplash") 35 | if err != nil { 36 | fmt.Println(err) 37 | os.Exit(1) 38 | } 39 | 40 | cmd := &cobra.Command{ 41 | Use: man.Use, 42 | Short: man.Short, 43 | Long: man.Long, 44 | Example: man.Example, 45 | PreRunE: unsplashPreRun, 46 | RunE: unsplashRun, 47 | } 48 | 49 | cmd.Flags().String("id", "", "The photo’s ID. Leave it empty if you want to download a random photo instead") 50 | cmd.Flags().String("collections", "", "Public collection ID(‘s) to filter selection. If multiple, comma-separated") 51 | cmd.Flags().Bool("featured", false, "Limit selection to featured photos. Valid values: false, true.") 52 | cmd.Flags().String("filter", "low", "Limit results by content safety. Default: low. Valid values are low and high.") 53 | cmd.Flags().String("orientation", "landscape", "Filter by photo orientation. Valid values: landscape, portrait, squarish.") 54 | cmd.Flags().String("query", "mountains", "Limit selection to photos matching a search term.") 55 | cmd.Flags().String("size", "all", "Photos size. Valid values: all, thumb, small, regular, full, raw. Default: all") 56 | cmd.Flags().String("username", "", "Limit selection to a single user.") 57 | 58 | return cmd 59 | } 60 | 61 | func unsplashPreRun(cmd *cobra.Command, args []string) error { 62 | logrus.Traceln("start: command unsplash pre-run") 63 | 64 | params := aid.GetModelFromFlags(cmd) 65 | if !helper.ContainsString(unsplashPhotoSizes, params.Size) { 66 | return fmt.Errorf("unknown photo size provided: %s", params.Size) 67 | } 68 | 69 | logrus.Traceln("end: command unsplash pre-run") 70 | 71 | return nil 72 | } 73 | 74 | func unsplashRun(cmd *cobra.Command, args []string) error { 75 | logrus.Traceln("start: command unsplash run") 76 | 77 | cred, err := dao.GetCredentialByProvider(profile, "unsplash") 78 | if err != nil { 79 | logrus.Errorf("Unexpected error: %v", err) 80 | return err 81 | } 82 | 83 | if (model.Credential{}) == cred { 84 | return fmt.Errorf("no unsplash credential found or no profile enabled") 85 | } 86 | 87 | id, err := cmd.Flags().GetString("id") 88 | if err != nil { 89 | return fmt.Errorf("unable to get flag id\n%v", err) 90 | } 91 | 92 | if id != "" { 93 | // get photo 94 | response, err := aid.GetPhoto(id, cred) 95 | if err != nil || response.ID == "" { 96 | return fmt.Errorf("unablet to get photo\n%v", err) 97 | } 98 | 99 | size, err := cmd.Flags().GetString("size") 100 | if err != nil { 101 | return fmt.Errorf("unable to get flag id\n%v", err) 102 | } 103 | 104 | if response.ID != "" { 105 | aid.SaveGetPhotoResult(response) 106 | aid.DownloadPhotoByID(response, size) 107 | } 108 | 109 | } else { 110 | // random photo 111 | params := aid.GetModelFromFlags(cmd) 112 | err = aid.DownloadPhoto(params, cred, unsplashPhotoSizes) 113 | if err != nil { 114 | logrus.Errorf("unable to download photo\n%v", err) 115 | return err 116 | } 117 | 118 | } 119 | 120 | logrus.Traceln("end: command unsplash run") 121 | return err 122 | } 123 | -------------------------------------------------------------------------------- /cobra/controller/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package controller 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | "runtime" 22 | 23 | "github.com/awslabs/clencli/box" 24 | "github.com/awslabs/clencli/helper" 25 | "github.com/sirupsen/logrus" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | // VersionCmd command to display clencli current version 30 | func VersionCmd() *cobra.Command { 31 | man, err := helper.GetManual("version") 32 | if err != nil { 33 | fmt.Println(err) 34 | os.Exit(1) 35 | } 36 | 37 | return &cobra.Command{ 38 | Use: man.Use, 39 | Short: man.Short, 40 | Long: man.Long, 41 | RunE: versionRun, 42 | } 43 | } 44 | 45 | func versionRun(cmd *cobra.Command, args []string) error { 46 | // Get the version defined in the VERSION file 47 | cmd.SilenceUsage = true 48 | 49 | version, found := box.Get("/VERSION") 50 | if !found { 51 | logrus.Errorf("unable to find VERSION file under box/resources") 52 | return fmt.Errorf("version not available") 53 | } 54 | 55 | goOS := runtime.GOOS 56 | goVersion := runtime.Version() 57 | goArch := runtime.GOARCH 58 | 59 | cmd.Printf("clencli v%s %s %s %s\n", version, goVersion, goOS, goArch) 60 | 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /cobra/dao/configure.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package dao 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | 22 | "github.com/awslabs/clencli/cobra/aid" 23 | "github.com/awslabs/clencli/cobra/model" 24 | ) 25 | 26 | // GetConfigurations read the current configurations file and return its model 27 | func GetConfigurations() (model.Configurations, error) { 28 | var confs model.Configurations 29 | v, err := aid.ReadConfig(aid.GetAppInfo().ConfigurationsName) 30 | if err != nil { 31 | return confs, fmt.Errorf("unable to read configurations\n%v", err) 32 | } 33 | 34 | err = v.Unmarshal(&confs) 35 | if err != nil { 36 | return confs, fmt.Errorf("unable to unmarshall configurations\n%v", err) 37 | } 38 | 39 | return confs, err 40 | } 41 | 42 | // GetReadMe TODO... 43 | func GetReadMe() (model.ReadMe, error) { 44 | var readMe model.ReadMe 45 | wd, err := os.Getwd() 46 | if err != nil { 47 | return readMe, fmt.Errorf("unable to get the current working directory:\n%v", err) 48 | } 49 | 50 | configPath := wd + "/clencli" 51 | configName := "readme" 52 | configType := "yaml" 53 | 54 | v, err := aid.ReadConfigAsViper(configPath, configName, configType) 55 | if err != nil { 56 | return readMe, fmt.Errorf("unable to read configurations\n%v", err) 57 | } 58 | 59 | err = v.Unmarshal(&readMe) 60 | if err != nil { 61 | return readMe, fmt.Errorf("unable to unmarshall configurations\n%v", err) 62 | } 63 | 64 | return readMe, err 65 | } 66 | 67 | // GetConfigurationProfile returns credentials of a profile 68 | func GetConfigurationProfile(name string) (model.ConfigurationProfile, error) { 69 | configurations, err := GetConfigurations() 70 | 71 | if err != nil { 72 | return (model.ConfigurationProfile{}), err 73 | } 74 | 75 | for _, profile := range configurations.Profiles { 76 | if profile.Name == name { 77 | return profile, err 78 | } 79 | } 80 | 81 | return (model.ConfigurationProfile{}), err 82 | } 83 | 84 | // GetUnsplashRandomPhotoParameters TODO ... 85 | func GetUnsplashRandomPhotoParameters(name string) model.UnsplashRandomPhotoParameters { 86 | profile, _ := GetConfigurationProfile(name) 87 | 88 | if len(profile.Configurations) > 0 { 89 | // TODO: improve this logic 90 | for _, conf := range profile.Configurations { 91 | return conf.Unsplash.RandomPhoto.Parameters 92 | } 93 | } 94 | 95 | return (model.UnsplashRandomPhotoParameters{}) 96 | } 97 | 98 | // GetCredentials read the current credentials file and return its model 99 | func GetCredentials() (model.Credentials, error) { 100 | var creds model.Credentials 101 | v, err := aid.ReadConfig(aid.GetAppInfo().CredentialsName) 102 | if err != nil { 103 | return creds, fmt.Errorf("unable to read credentials\n%v", err) 104 | } 105 | 106 | err = v.Unmarshal(&creds) 107 | if err != nil { 108 | return creds, fmt.Errorf("unable to unmarshall credentials\n%v", err) 109 | } 110 | 111 | return creds, err 112 | } 113 | 114 | // GetCredentialProfile returns credentials of a profile 115 | func GetCredentialProfile(name string) (model.CredentialProfile, error) { 116 | credentials, err := GetCredentials() 117 | 118 | if err != nil { 119 | return (model.CredentialProfile{}), err 120 | } 121 | 122 | for _, profile := range credentials.Profiles { 123 | if profile.Name == name { 124 | return profile, err 125 | } 126 | } 127 | 128 | return (model.CredentialProfile{}), err 129 | } 130 | 131 | // GetCredentialByProvider return credentials based on the given provider, if non-existent, return an empty credential 132 | func GetCredentialByProvider(profile string, provider string) (model.Credential, error) { 133 | cp, err := GetCredentialProfile(profile) 134 | if err != nil { 135 | return (model.Credential{}), err 136 | } 137 | 138 | for _, c := range cp.Credentials { 139 | if c.Provider == provider { 140 | return c, err 141 | } 142 | } 143 | 144 | return (model.Credential{}), err 145 | } 146 | 147 | // SaveConfigurations saves the given configuration onto the configurations file 148 | func SaveConfigurations(configurations model.Configurations) error { 149 | return aid.WriteInterfaceToFile(configurations, aid.GetAppInfo().ConfigurationsPath) 150 | } 151 | 152 | // SaveCredentials saves the given credential onto the credentials file 153 | func SaveCredentials(credentials model.Credentials) error { 154 | return aid.WriteInterfaceToFile(credentials, aid.GetAppInfo().CredentialsPath) 155 | } 156 | -------------------------------------------------------------------------------- /cobra/model/configure.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package model 17 | 18 | // Credentials model 19 | type Credentials struct { 20 | Profiles []CredentialProfile `yaml:"profiles"` 21 | } 22 | 23 | // CredentialProfile model 24 | type CredentialProfile struct { 25 | Name string `yaml:"name"` 26 | Description string `yaml:"description,omitempty"` 27 | Credentials []Credential `yaml:"credentials"` 28 | } 29 | 30 | // Credential model 31 | type Credential struct { 32 | Name string `yaml:"name,omitempty"` 33 | Provider string `yaml:"provider"` 34 | AccessKey string `yaml:"accessKey"` 35 | SecretKey string `yaml:"secretKey"` 36 | SessionToken string `yaml:"sessionToken"` 37 | } 38 | 39 | // Configurations model 40 | type Configurations struct { 41 | Profiles []ConfigurationProfile `yaml:"profiles"` 42 | } 43 | 44 | // ConfigurationProfile model 45 | type ConfigurationProfile struct { 46 | Name string `yaml:"name"` 47 | Description string `yaml:"description,omitempty"` 48 | Configurations []Configuration `yaml:"configurations"` 49 | } 50 | 51 | // Configuration model 52 | type Configuration struct { 53 | Name string `yaml:"name,omitempty"` 54 | Initialization `yaml:"initialization,omitempty"` 55 | Unsplash `yaml:"unsplash,omitempty"` 56 | } 57 | -------------------------------------------------------------------------------- /cobra/model/init.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package model 17 | 18 | // Initialization struct to initalize things: projects, etc 19 | type Initialization struct { 20 | Files []File `yaml:"files"` 21 | } 22 | 23 | // File ... 24 | type File struct { 25 | Path string `yaml:"path,omitempty"` 26 | Src string `yaml:"src,omitempty"` 27 | Dest string `yaml:"dest,omitempty"` 28 | State string `yaml:"state"` 29 | } 30 | -------------------------------------------------------------------------------- /cobra/model/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package model 17 | 18 | import "os" 19 | 20 | // App represents the all the necessary information about clencli 21 | type App struct { 22 | // Name of file to look for inside the path 23 | Name string 24 | ConfigurationsDir string 25 | ConfigurationsName string 26 | ConfigurationsType string 27 | ConfigurationsPath string 28 | ConfigurationsPermissions os.FileMode 29 | CredentialsName string 30 | CredentialsType string 31 | CredentialsPath string 32 | CredentialsPermissions os.FileMode 33 | LogsDir string 34 | LogsName string 35 | LogsType string 36 | LogsPath string 37 | LogsPermissions os.FileMode 38 | 39 | WorkingDir string 40 | } 41 | 42 | // ReadMe struct of the readme.yaml 43 | type ReadMe struct { 44 | Logo struct { 45 | URL string `yaml:"url"` 46 | Label string `yaml:"label"` 47 | } `yaml:"logo,omitempty"` 48 | Shields struct { 49 | Badges []struct { 50 | Description string `yaml:"description"` 51 | Image string `yaml:"image"` 52 | URL string `yaml:"url"` 53 | } `yaml:"badges"` 54 | } `yaml:"shields,omitempty"` 55 | App struct { 56 | Name string `yaml:"name"` 57 | Function string `yaml:"function"` 58 | ID string `yaml:"id"` 59 | } `yaml:"app,omitempty"` 60 | Screenshots []struct { 61 | Caption string `yaml:"caption"` 62 | Label string `yaml:"label"` 63 | URL string `yaml:"url"` 64 | } `yaml:"screenshots,omitempty"` 65 | Usage string `yaml:"usage"` 66 | Prerequisites []struct { 67 | Description string `yaml:"description"` 68 | Name string `yaml:"name"` 69 | URL string `yaml:"url"` 70 | } `yaml:"prerequisites,omitempty"` 71 | Installing string `yaml:"installing,omitempty"` 72 | Testing string `yaml:"testing,omitempty"` 73 | Deployment string `yaml:"deployment,omitempty"` 74 | Include []string `yaml:"include,omitempty"` 75 | Contributors []struct { 76 | Name string `yaml:"name"` 77 | Role string `yaml:"role"` 78 | Email string `yaml:"email"` 79 | } `yaml:"contributors,omitempty"` 80 | Acknowledgments []struct { 81 | Name string `yaml:"name"` 82 | Role string `yaml:"role"` 83 | } `yaml:"acknowledgments,omitempty"` 84 | References []struct { 85 | Description string `yaml:"description"` 86 | Name string `yaml:"name"` 87 | URL string `yaml:"url"` 88 | } `yaml:"references,omitempty"` 89 | License string `yaml:"license,omitempty"` 90 | Copyright string `yaml:"copyright,omitempty"` 91 | } 92 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/awslabs/clencli 2 | 3 | go 1.16 4 | 5 | require ( 6 | cloud.google.com/go/storage v1.15.0 // indirect 7 | github.com/Masterminds/goutils v1.1.1 // indirect 8 | github.com/Microsoft/go-winio v0.5.0 // indirect 9 | github.com/armon/go-metrics v0.3.8 // indirect 10 | github.com/aws/aws-sdk-go v1.38.39 // indirect 11 | github.com/fatih/color v1.11.0 // indirect 12 | github.com/go-git/go-billy/v5 v5.3.1 // indirect 13 | github.com/go-git/go-git/v5 v5.3.0 // indirect 14 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 15 | github.com/golang/snappy v0.0.3 // indirect 16 | github.com/google/wire v0.5.0 // indirect 17 | github.com/hairyhenderson/gomplate/v3 v3.9.0 18 | github.com/hashicorp/errwrap v1.1.0 // indirect 19 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 20 | github.com/hashicorp/go-hclog v0.16.1 // indirect 21 | github.com/hashicorp/go-immutable-radix v1.3.0 // indirect 22 | github.com/hashicorp/go-multierror v1.1.1 // indirect 23 | github.com/hashicorp/go-retryablehttp v0.7.0 // indirect 24 | github.com/hashicorp/golang-lru v0.5.4 // indirect 25 | github.com/hashicorp/vault/api v1.1.0 // indirect 26 | github.com/hashicorp/vault/sdk v0.2.0 // indirect 27 | github.com/kevinburke/ssh_config v1.1.0 // indirect 28 | github.com/magiconair/properties v1.8.5 // indirect 29 | github.com/mitchellh/mapstructure v1.4.1 // indirect 30 | github.com/pelletier/go-toml v1.9.1 // indirect 31 | github.com/pierrec/lz4 v2.6.0+incompatible // indirect 32 | github.com/rs/zerolog v1.21.0 // indirect 33 | github.com/sergi/go-diff v1.2.0 // indirect 34 | github.com/sirupsen/logrus v1.8.1 35 | github.com/spf13/afero v1.6.0 // indirect 36 | github.com/spf13/cast v1.3.1 // indirect 37 | github.com/spf13/cobra v1.1.3 38 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 39 | github.com/spf13/viper v1.7.1 40 | github.com/stretchr/testify v1.7.0 41 | github.com/ugorji/go v1.2.5 // indirect 42 | gocloud.dev v0.22.0 // indirect 43 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect 44 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect 45 | golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect 46 | golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect 47 | golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect 48 | golang.org/x/tools v0.1.1 // indirect 49 | google.golang.org/api v0.46.0 // indirect 50 | google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384 // indirect 51 | google.golang.org/grpc v1.37.1 // indirect 52 | gopkg.in/ini.v1 v1.62.0 // indirect 53 | gopkg.in/yaml.v2 v2.4.0 54 | ) 55 | -------------------------------------------------------------------------------- /helper/cobra.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/sirupsen/logrus" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | // ValidateCmdArgs basic validation of a command's arguments, return error if fails 11 | func ValidateCmdArgs(cmd *cobra.Command, args []string, cmdName string) error { 12 | logrus.Tracef("start: validate command %s arguments", cmdName) 13 | 14 | if len(args) == 0 { 15 | logrus.Error("no arguments passed") 16 | return fmt.Errorf("this command requires one argument") 17 | } 18 | 19 | if len(args) > 1 { 20 | logrus.Errorf("more than one argument passed: %v", args) 21 | return fmt.Errorf("this command accepts only one argument at a time") 22 | } 23 | 24 | if !ContainsString(cmd.ValidArgs, args[0]) { 25 | logrus.Errorf("unknow argument passed: %v", args) 26 | logrus.Errorf("command %s only accepts the following arguments: %v", cmdName, args) 27 | return fmt.Errorf("unknown argument provided: %s", args[0]) 28 | } 29 | 30 | logrus.Tracef("end: validate command %s arguments", cmdName) 31 | return nil 32 | } 33 | 34 | // ValidateCmdArgAndFlag basic validation for the given arg on args, and if flag is empty, return error if fails 35 | func ValidateCmdArgAndFlag(cmd *cobra.Command, args []string, cmdName string, arg string, flag string) error { 36 | logrus.Tracef("start: validate command %s --%s", cmdName, flag) 37 | if args[0] == arg { 38 | pName, err := cmd.Flags().GetString(flag) 39 | if err != nil { 40 | logrus.Errorf("unable to access --%s: %s", flag, err.Error()) 41 | return err 42 | } 43 | 44 | if pName == "" { 45 | logrus.Errorf("empty value passed to --%s", flag) 46 | return fmt.Errorf("--%s must be defined", flag) 47 | } 48 | } else { 49 | logrus.Errorf("unknow argument passed: %v", args) 50 | return fmt.Errorf("unknown argument provided: %s", args[0]) 51 | } 52 | logrus.Tracef("end: validate command %s --%s", cmdName, flag) 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /helper/directories.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/sirupsen/logrus" 8 | ) 9 | 10 | // MkDirsIfNotExist creates a directory named path, 11 | // along with any necessary parents, and returns true, or else returns false. 12 | // The dirs have permission bits 511 (before umask) are used for all directories that this function creates. 13 | // If path is already a directory, the function does nothing and returns false. 14 | func MkDirsIfNotExist(name string) bool { 15 | _, err := os.Stat(name) 16 | if os.IsNotExist(err) { 17 | logrus.Infof("creating directory %s", name) 18 | err = os.MkdirAll(name, os.ModePerm) 19 | if err != nil { 20 | logrus.Errorf("unable to create %s directory", name) 21 | return false 22 | } 23 | logrus.Infof("directory %s created", name) 24 | return true 25 | } 26 | 27 | return false 28 | } 29 | 30 | // CreateDirectoryNamedPath creates a directory named path, along with any necessary parents, and returns nil, or else returns an error. 31 | // The permission bits perm (before umask) are used for all directories. If path is already a directory does nothing and returns nil. 32 | func CreateDirectoryNamedPath(path string) (string, error) { 33 | err := os.MkdirAll(path, os.ModePerm) 34 | if err != nil { 35 | logrus.Fatal("Unable to create directory (and its parents)", err) 36 | } 37 | 38 | return path, err 39 | } 40 | 41 | // CreateTempDir creates a new temporary directory in the directory dir. 42 | // The directory name is generated by taking pattern and applying a random string to the end 43 | func CreateTempDir(dir string, pattern string) string { 44 | dir, err := ioutil.TempDir(dir, pattern) 45 | if err != nil { 46 | logrus.Fatal(err) 47 | } 48 | 49 | return dir 50 | } 51 | -------------------------------------------------------------------------------- /helper/files.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io/ioutil" 7 | "runtime" 8 | "strings" 9 | 10 | "io" 11 | "net/http" 12 | "os" 13 | 14 | "github.com/awslabs/clencli/box" 15 | "github.com/sirupsen/logrus" 16 | ) 17 | 18 | // WriteFile writes a file and return true if successful 19 | func WriteFile(filename string, data []byte) bool { 20 | err := ioutil.WriteFile(filename, data, os.ModePerm) 21 | 22 | if err != nil { 23 | logrus.Fatal(err) 24 | return false 25 | } 26 | 27 | return true 28 | } 29 | 30 | // WriteFileFromBox get the file from box's resources and write into the given destination, returns false if not able to. 31 | func WriteFileFromBox(source string, dest string) bool { 32 | bytes, found := box.Get(source) 33 | 34 | if !found { 35 | logrus.Errorf("file \"%s\" not found under box/resources", source) 36 | return false 37 | } 38 | 39 | return WriteFile(dest, bytes) 40 | } 41 | 42 | // DownloadFileTo downloads a file and saves into the given directory with the given file name 43 | func DownloadFileTo(url string, destination string) error { 44 | // Get the data 45 | resp, err := http.Get(url) 46 | if err != nil { 47 | return err 48 | } 49 | defer resp.Body.Close() 50 | 51 | // Create the file 52 | out, err := os.Create(destination) 53 | if err != nil { 54 | return err 55 | } 56 | defer out.Close() 57 | 58 | // Write the body to file 59 | _, err = io.Copy(out, resp.Body) 60 | return err 61 | } 62 | 63 | // DownloadFile downloads a file and saves into the given directory with the given file name 64 | func DownloadFile(url string, dirPath string, filename string) error { 65 | // Get the data 66 | resp, err := http.Get(url) 67 | if err != nil { 68 | return err 69 | } 70 | defer resp.Body.Close() 71 | 72 | // Create the file 73 | sep := string(os.PathSeparator) 74 | out, err := os.Create(dirPath + sep + filename) 75 | if err != nil { 76 | return err 77 | } 78 | defer out.Close() 79 | 80 | // Write the body to file 81 | _, err = io.Copy(out, resp.Body) 82 | return err 83 | } 84 | 85 | // FileExists checks if a file exists and is not a directory before we 86 | // try using it to prevent further errors. 87 | func FileExists(path string) bool { 88 | info, err := os.Stat(path) 89 | if os.IsNotExist(err) { 90 | return false 91 | } 92 | return !info.IsDir() 93 | } 94 | 95 | // DirOrFileExists an error is known to report that a file or directory does not exist. 96 | // It is satisfied by ErrNotExist as well as some syscall errors. 97 | func DirOrFileExists(path string) bool { 98 | if _, err := os.Stat(path); os.IsNotExist(err) { 99 | return false 100 | } 101 | 102 | return true 103 | } 104 | 105 | // CopyFile copies a file from source into a given destination path 106 | func CopyFile(sourceFile string, destinationFile string) { 107 | input, err := ioutil.ReadFile(sourceFile) 108 | if err != nil { 109 | fmt.Println(err) 110 | os.Exit(1) 111 | } 112 | 113 | err = ioutil.WriteFile(destinationFile, input, 0644) 114 | if err != nil { 115 | fmt.Println("Error creating", destinationFile) 116 | fmt.Println(err) 117 | os.Exit(1) 118 | } 119 | } 120 | 121 | // CopyFileTo copy a file from source to destination 122 | func CopyFileTo(source string, dest string) error { 123 | input, err := ioutil.ReadFile(source) 124 | if err != nil { 125 | logrus.Errorf("unable to read file\n%v", err) 126 | return err 127 | } 128 | 129 | err = ioutil.WriteFile(dest, input, os.ModePerm) 130 | if err != nil { 131 | logrus.Errorf("unable to write file\n%v", err) 132 | return err 133 | } 134 | 135 | return nil 136 | } 137 | 138 | // FileSize return the size of the give file path. 139 | // Gives an error if files does not exist 140 | func FileSize(path string) (int64, error) { 141 | var size int64 = -1 142 | if FileExists(path) { 143 | info, err := os.Stat(path) 144 | if err != nil { 145 | if err != nil { 146 | return size, fmt.Errorf("unable to obtain information about file: %s\n%s", path, err) 147 | } 148 | return size, err 149 | } 150 | size = info.Size() 151 | } else { 152 | return size, fmt.Errorf("file does not exist") 153 | } 154 | return size, nil 155 | } 156 | 157 | // ListFiles list of all file names in the given directory. Pass "." if you want to list at the current directory. 158 | func ListFiles(dir string) []os.FileInfo { 159 | files, err := ioutil.ReadDir(dir) 160 | 161 | if err != nil { 162 | logrus.Fatal(err) 163 | } 164 | 165 | return files 166 | } 167 | 168 | // DeleteFile removes the named file or (empty) directory. 169 | // If there is an error, it will be of type *PathError. 170 | func DeleteFile(name string) error { 171 | return os.Remove(name) 172 | } 173 | 174 | // TrimRightFile open file in `path`, read line by line removing all trailing characters. 175 | // Overwrite original file in `path` if `true`, it creates a new file `path`.tmp otherwise. 176 | func TrimRightFile(path string, overwrite bool) error { 177 | f, err := os.Open(path) 178 | if err != nil { 179 | logrus.Errorf("unable to open file %s\n%v", f, err) 180 | } 181 | defer f.Close() 182 | 183 | tPath := path + ".tmp" 184 | tf, err := os.OpenFile(tPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm) 185 | if err != nil { 186 | logrus.Error(err) 187 | } 188 | defer tf.Close() 189 | 190 | scanner := bufio.NewScanner(f) 191 | for scanner.Scan() { 192 | tr := strings.TrimRight(scanner.Text(), " ") 193 | 194 | if runtime.GOOS == "windows" { 195 | // convert LF to CRLF 196 | if _, err := tf.WriteString(tr + "\r\n"); err != nil { 197 | logrus.Errorf("unable write trimmed string to temporary file\n%v", err) 198 | return err 199 | } 200 | } else { 201 | // convert CRLF to LF 202 | if _, err := tf.WriteString(tr + "\n"); err != nil { 203 | logrus.Errorf("unable write trimmed string to temporary file\n%v", err) 204 | return err 205 | } 206 | } 207 | 208 | } 209 | 210 | if err := scanner.Err(); err != nil { 211 | logrus.Errorf("unable to use scanner\n%v", err) 212 | return err 213 | } 214 | 215 | if overwrite { 216 | if err := CopyFileTo(tPath, path); err != nil { 217 | logrus.Errorf("unable to replace original file with temporary\n%v", err) 218 | return err 219 | } 220 | defer os.Remove(tPath) 221 | } 222 | 223 | return nil 224 | } 225 | -------------------------------------------------------------------------------- /helper/manual.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/awslabs/clencli/box" 7 | "github.com/sirupsen/logrus" 8 | yaml "gopkg.in/yaml.v2" 9 | ) 10 | 11 | // Manual mapping the fields used by a Cobra command 12 | type Manual struct { 13 | Use string `yaml:"use"` 14 | Example string `yaml:"example"` 15 | Short string `yaml:"short"` 16 | Long string `yaml:"long"` 17 | } 18 | 19 | // GetManual retrieve information about the given command 20 | func GetManual(command string) (Manual, error) { 21 | var man Manual 22 | var err error 23 | manualBlob, status := box.Get("/manual/" + command + ".yaml") 24 | if status { 25 | err = yaml.Unmarshal(manualBlob, &man) 26 | if err != nil { 27 | return man, fmt.Errorf("unable to decode YAML file, error:\n%v", err) 28 | } 29 | } else { 30 | logrus.Fatal("unable to read manual from box") 31 | } 32 | 33 | return man, err 34 | } 35 | -------------------------------------------------------------------------------- /helper/strings.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // GetStringInBetweenTwoString return the string between two strings 8 | func GetStringInBetweenTwoString(str string, startS string, endS string) (result string, found bool) { 9 | s := strings.Index(str, startS) 10 | if s == -1 { 11 | return result, false 12 | } 13 | newS := str[s+len(startS):] 14 | e := strings.Index(newS, endS) 15 | if e == -1 { 16 | return result, false 17 | } 18 | result = newS[:e] 19 | return result, true 20 | } 21 | 22 | // GetStringBetweenDoubleQuotes return the string between double quotes 23 | func GetStringBetweenDoubleQuotes(str string) (result string, found bool) { 24 | return GetStringInBetweenTwoString(str, "\"", "\"") 25 | } 26 | 27 | // GetStringTrimmed splits the string by the given separator and trims it by removing all spaces in between 28 | func GetStringTrimmed(s string, sep string) []string { 29 | slc := strings.Split(s, sep) 30 | for i := range slc { 31 | slc[i] = strings.TrimSpace(slc[i]) 32 | } 33 | return slc 34 | } 35 | 36 | // ContainsString return true if slice contains item 37 | func ContainsString(slice []string, item string) bool { 38 | set := make(map[string]struct{}, len(slice)) 39 | for _, s := range slice { 40 | set[s] = struct{}{} 41 | } 42 | 43 | _, ok := set[item] 44 | return ok 45 | } 46 | -------------------------------------------------------------------------------- /lib/make/cobra/Makefile: -------------------------------------------------------------------------------- 1 | # Use camelCase (not snake_case/snake-case) for command names. 2 | # Otherwise, you will encounter errors. 3 | # For example, make cobra/add add-user is incorrect, but make cobra/add addUser is valid. 4 | .PHONY: cobra/add 5 | cobra/add: ## Add a Cobra command 6 | ifdef command 7 | cd cobra && cobra add $(command) 8 | else 9 | @echo "command not defined" 10 | endif 11 | 12 | .PHONY: cobra/del 13 | cobra/del: ## Delete a Cobra command 14 | ifdef command 15 | rm -f cmd/$(command).go 16 | else 17 | @echo "command not defined" 18 | endif 19 | 20 | 21 | .PHONY: cobra/install 22 | cobra/install: 23 | go get github.com/spf13/cobra/cobra 24 | 25 | -------------------------------------------------------------------------------- /lib/make/docker/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: docker/build/ubuntu/lastest 2 | docker/build/ubuntu/latest: docker/build/ubuntu 3 | 4 | .PHONY: docker/build/ubuntu 5 | docker/build/ubuntu: 6 | docker build --target ubuntu -t clencli/ubuntu:latest . 7 | 8 | .PHONY: docker/build/ubuntu/bionic 9 | docker/build/ubuntu/bionic: 10 | docker build --target bionic -t clencli/ubuntu:bionic . -------------------------------------------------------------------------------- /lib/make/github/Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: github/build 3 | github/build: ## Builds the app on Github 4 | mkdir bin/ && go build -o bin/clencli 5 | 6 | # GitHub actions doesn't allow egress internet connectivity 7 | # Therefore integration tests that needs to download/pull data from the internet can't be executed 8 | .PHONY: github/test 9 | github/test: 10 | @cd tests && go test pkg_test.go cmd_root_test.go cmd_init_test.go cmd_render_test.go cmd_version_test.go 11 | 12 | .PHONY: go/test 13 | go/test: 14 | @cd tests && go test -v -------------------------------------------------------------------------------- /lib/make/go/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: go/build 2 | go/build: ## Compile packages and dependencies 3 | mkdir -p build 4 | go build -o build/clencli 5 | rm -rf build 6 | 7 | .PHONY: go/install 8 | go/install: ## Compile and install packages and dependencies 9 | go install 10 | 11 | .PHONY: go/run 12 | go/run: ## Compile and run Go program 13 | ifdef command 14 | go run main.go $(command) 15 | else 16 | go run main.go 17 | endif 18 | 19 | .PHONY: go/fmt 20 | go/fmt: ## gofmt (reformat) package sources 21 | go fmt github.com/awslabs/clencli/... 22 | go fmt ./... 23 | gofmt -s -w . 24 | 25 | .PHONY: go/generate 26 | go/generate: ## Generate Go files by processing source 27 | go generate ./... 28 | 29 | .PHONY: go/mod/tidy 30 | go/mod/tidy: ## Add missing and remove unused modules 31 | go mod tidy 32 | 33 | .PHONY: go/get 34 | go/get: ## Add dependencies to current module and install them 35 | go get ./... 36 | 37 | .PHONY: go/get/debug 38 | go/get/debug: 39 | GOPROXY=direct go get -x ./... 40 | 41 | .PHONY: go/clean 42 | go/clean: ## Remove object files and cached files 43 | go clean -cache -modcache -i -r 44 | 45 | .PHONY: go/list 46 | go/list: ## List packages or modules 47 | go list -m -versions $(package) 48 | 49 | .PHONY: go/version 50 | go/version: ## Print Go version 51 | go version 52 | 53 | .PHONY: go/env 54 | go/env: ## Print Go environment information 55 | go env 56 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | */ 16 | package main 17 | 18 | import "github.com/awslabs/clencli/cobra/cmd" 19 | 20 | func main() { 21 | cmd.Execute() 22 | } 23 | -------------------------------------------------------------------------------- /tests/cmd_gitignore_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/awslabs/clencli/cobra/controller" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestGitIgnoreCmd(t *testing.T) { 11 | tests := map[string]struct { 12 | args []string 13 | out string 14 | err string 15 | }{ 16 | // argument 17 | "empty": {args: []string{"gitignore"}, out: "", err: "no flag or argument provided"}, 18 | "empty arg": {args: []string{"gitignore", ""}, out: "", err: "unknown argument provided"}, 19 | "wrong arg": {args: []string{"gitignore", "foo"}, out: "", err: "unknown argument provided"}, 20 | 21 | // // flags 22 | "wrong flag": {args: []string{"gitignore", "--input"}, out: "", err: "flag needs an argument: --input"}, 23 | 24 | // // # projects 25 | "no project name": {args: []string{"gitignore", "--input", ""}, out: "", err: "no flag or argument provided"}, 26 | } 27 | 28 | for name, tc := range tests { 29 | t.Run(name, func(t *testing.T) { 30 | out, err := executeCommand(t, controller.GitIgnoreCmd(), tc.args) 31 | assert.Contains(t, out, tc.out) 32 | assert.Contains(t, err.Error(), tc.err) 33 | }) 34 | } 35 | } 36 | 37 | func TestGitIgnoreList(t *testing.T) { 38 | args := []string{"gitignore", "list"} 39 | out, err := executeCommand(t, controller.GitIgnoreCmd(), args) 40 | assert.Nil(t, err) 41 | assert.NotEmpty(t, out) 42 | } 43 | 44 | func TestGitIgnoreInput(t *testing.T) { 45 | args := []string{"gitignore", "--input", "terraform"} 46 | out, err := executeCommand(t, controller.GitIgnoreCmd(), args) 47 | assert.Nil(t, err) 48 | assert.NotEmpty(t, out) 49 | } -------------------------------------------------------------------------------- /tests/cmd_init_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/awslabs/clencli/cobra/controller" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestInitCmd(t *testing.T) { 12 | tests := map[string]struct { 13 | args []string 14 | out string 15 | err string 16 | }{ 17 | // argument 18 | "empty": {args: []string{"init"}, out: "", err: "this command requires one argument"}, 19 | "empty arg": {args: []string{"init", ""}, out: "", err: "invalid argument"}, 20 | "wrong arg": {args: []string{"init", "foo"}, out: "", err: "invalid argument"}, 21 | 22 | // flags 23 | "wrong flag": {args: []string{"init", "project", "--foo"}, out: "", err: "unknown flag: --foo"}, 24 | 25 | // # projects 26 | "no project name": {args: []string{"init", "project"}, out: "", err: "--project-name must be defined"}, 27 | 28 | // ## --project-name 29 | "emtpy project name": {args: []string{"init", "project", "--project-name"}, out: "", err: "flag needs an argument"}, 30 | 31 | // ## --project-type 32 | "empty project type": {args: []string{"init", "project", "--project-type"}, out: "", err: "flag needs an argument"}, 33 | "invalid project type": {args: []string{"init", "project", "--project-type", "nil"}, out: "", err: "--project-name must be defined"}, 34 | 35 | // ## --project-name && --project-type 36 | "with project name and empty project type": {args: []string{"init", "project", "--project-name", "foo", "--project-type"}, out: "", err: "flag needs an argument"}, 37 | "with project name and invalid project type": {args: []string{"init", "project", "--project-name", "foo", "--project-type", "bar"}, out: "", err: "unknow project type"}, 38 | } 39 | 40 | for name, tc := range tests { 41 | t.Run(name, func(t *testing.T) { 42 | out, err := executeCommand(t, controller.InitCmd(), tc.args) 43 | assert.Contains(t, out, tc.out) 44 | assert.Contains(t, err.Error(), tc.err) 45 | }) 46 | } 47 | } 48 | 49 | func assertBasicProject(t *testing.T, err error) (string, string) { 50 | sep := string(os.PathSeparator) 51 | dir := t.Name() + sep + "foo" 52 | 53 | assert.Nil(t, err) 54 | assert.DirExists(t, dir) 55 | assert.FileExists(t, dir+sep+".gitignore") 56 | assert.DirExists(t, dir+sep+"clencli") 57 | 58 | assert.FileExists(t, dir+sep+"clencli"+sep+"readme.tmpl") 59 | assert.FileExists(t, dir+sep+"clencli"+sep+"readme.yaml") 60 | 61 | return dir, sep 62 | } 63 | 64 | func assertCloudProject(t *testing.T, err error) (string, string) { 65 | dir, sep := assertBasicProject(t, err) 66 | assert.FileExists(t, dir+sep+"clencli"+sep+"hld.tmpl") 67 | assert.FileExists(t, dir+sep+"clencli"+sep+"hld.yaml") 68 | return dir, sep 69 | } 70 | 71 | /* PROJECT: BASIC */ 72 | 73 | func TestInitBasicProjectWithNameOnly(t *testing.T) { 74 | args := []string{"init", "project", "--project-name", "foo"} 75 | out, err := executeCommand(t, controller.InitCmd(), args) 76 | assert.Nil(t, err) 77 | assert.Contains(t, out, "was successfully initialized as a basic project") 78 | assertBasicProject(t, err) 79 | } 80 | 81 | func TestInitBasicProjectWithNameAndType(t *testing.T) { 82 | args := []string{"init", "project", "--project-name", "foo", "--project-type", "basic"} 83 | out, err := executeCommand(t, controller.InitCmd(), args) 84 | assert.Nil(t, err) 85 | assert.Contains(t, out, "was successfully initialized as a basic project") 86 | assertBasicProject(t, err) 87 | } 88 | 89 | /* PROJECT: CLOUD */ 90 | 91 | func TestInitCloudProjectWithName(t *testing.T) { 92 | args := []string{"init", "project", "--project-name", "foo", "--project-type", "cloud"} 93 | out, err := executeCommand(t, controller.InitCmd(), args) 94 | assert.Contains(t, out, "was successfully initialized as a cloud project") 95 | assertCloudProject(t, err) 96 | } 97 | 98 | /* PROJECT: CLOUDFORMATION */ 99 | 100 | func TestInitProjectWithNameAndCloudFormationType(t *testing.T) { 101 | args := []string{"init", "project", "--project-name", "foo", "--project-type", "cloudformation"} 102 | out, err := executeCommand(t, controller.InitCmd(), args) 103 | assert.Contains(t, out, "was successfully initialized as a cloudformation project") 104 | 105 | dir, sep := assertCloudProject(t, err) 106 | assert.DirExists(t, dir+sep+"environments"+sep+"dev") 107 | assert.DirExists(t, dir+sep+"environments"+sep+"prod") 108 | 109 | assert.FileExists(t, dir+sep+"skeleton.yaml") 110 | assert.FileExists(t, dir+sep+"skeleton.json") 111 | } 112 | 113 | /* PROJECT: TERRAFORM */ 114 | 115 | func TestInitProjectWithNameAndTerraformType(t *testing.T) { 116 | args := []string{"init", "project", "--project-name", "foo", "--project-type", "terraform"} 117 | out, err := executeCommand(t, controller.InitCmd(), args) 118 | assert.Contains(t, out, "was successfully initialized as a terraform project") 119 | 120 | dir, sep := assertCloudProject(t, err) 121 | 122 | assert.FileExists(t, dir+sep+"Makefile") 123 | assert.FileExists(t, dir+sep+"LICENSE") 124 | 125 | assert.DirExists(t, dir+sep+"environments") 126 | assert.FileExists(t, dir+sep+"environments"+sep+"dev.tf") 127 | assert.FileExists(t, dir+sep+"environments"+sep+"prod.tf") 128 | 129 | assert.FileExists(t, dir+sep+"main.tf") 130 | assert.FileExists(t, dir+sep+"variables.tf") 131 | assert.FileExists(t, dir+sep+"outputs.tf") 132 | } 133 | -------------------------------------------------------------------------------- /tests/cmd_render_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/awslabs/clencli/cobra/aid" 8 | "github.com/awslabs/clencli/cobra/controller" 9 | "github.com/sirupsen/logrus" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestRenderCmd(t *testing.T) { 14 | tests := map[string]struct { 15 | args []string 16 | out string 17 | err string 18 | }{ 19 | // argument 20 | "no arg": {args: []string{"render"}, out: "", err: "this command requires one argument"}, 21 | "empty arg": {args: []string{"render", ""}, out: "", err: "invalid argument"}, 22 | "wrong arg": {args: []string{"render", "foo"}, out: "", err: "invalid argument"}, 23 | "unknown flag": {args: []string{"render", "--foo"}, out: "", err: "unknown flag: --foo"}, 24 | "missing database": {args: []string{"render", "template"}, out: "", err: "missing database"}, 25 | "flag needs an argument name": {args: []string{"render", "template", "--name"}, out: "", err: "flag needs an argument: --name"}, 26 | } 27 | 28 | for name, tc := range tests { 29 | t.Run(name, func(t *testing.T) { 30 | out, err := executeCommand(t, controller.RenderCmd(), tc.args) 31 | assert.Contains(t, out, tc.out) 32 | assert.Contains(t, err.Error(), tc.err) 33 | }) 34 | } 35 | } 36 | 37 | // /* README */ 38 | 39 | func initProject(t *testing.T, pType string) { 40 | aid.DeleteConfigurationsDirectory() 41 | args := []string{"init", "project", "--project-name", "foo", "--project-type", pType} 42 | wd, out, err := executeCommandGetWorkingDirectory(t, controller.InitCmd(), args) 43 | 44 | assert.Nil(t, err) 45 | assert.Contains(t, out, "was successfully initialized") 46 | 47 | sep := string(os.PathSeparator) 48 | if err := os.Chdir(wd + sep + t.Name() + sep + "foo"); err != nil { 49 | logrus.Fatal("unable to change current working directory") 50 | } 51 | 52 | } 53 | 54 | func TestRenderDefault(t *testing.T) { 55 | initProject(t, "basic") 56 | 57 | args := []string{"render", "template"} 58 | out, err := executeCommandOnly(t, controller.RenderCmd(), args) 59 | assert.Nil(t, err) 60 | assert.Contains(t, out, "Template readme.tmpl rendered as README.md") 61 | } 62 | 63 | func TestRenderReadme(t *testing.T) { 64 | initProject(t, "basic") 65 | 66 | args := []string{"render", "template", "--name", "readme"} 67 | out, err := executeCommandOnly(t, controller.RenderCmd(), args) 68 | assert.Nil(t, err) 69 | assert.Contains(t, out, "Template readme.tmpl rendered as README.md") 70 | } 71 | 72 | func TestRenderHLD(t *testing.T) { 73 | initProject(t, "cloud") 74 | 75 | args := []string{"render", "template", "--name", "hld"} 76 | out, err := executeCommandOnly(t, controller.RenderCmd(), args) 77 | assert.Nil(t, err) 78 | assert.Contains(t, out, "Template hld.tmpl rendered as HLD.md") 79 | } 80 | -------------------------------------------------------------------------------- /tests/cmd_root_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package tests 17 | 18 | import ( 19 | "testing" 20 | 21 | "github.com/awslabs/clencli/cobra/controller" 22 | "github.com/stretchr/testify/assert" 23 | ) 24 | 25 | func TestRootWithNoArgAndNoFlags(t *testing.T) { 26 | args := []string{""} 27 | out, err := executeCommand(t, controller.RootCmd(), args) 28 | assert.Nil(t, err) 29 | assert.Contains(t, out, "The Cloud Engineer CLI") 30 | } 31 | -------------------------------------------------------------------------------- /tests/cmd_unsplash_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/awslabs/clencli/cobra/aid" 8 | "github.com/awslabs/clencli/cobra/controller" 9 | "github.com/awslabs/clencli/cobra/model" 10 | "github.com/awslabs/clencli/helper" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func createUnplashCredential() { 15 | aid.DeleteConfigurationsDirectory() 16 | aid.CreateConfigurationsDirectory() 17 | 18 | var credentials model.Credentials 19 | 20 | var profile model.CredentialProfile 21 | profile.Name = "default" 22 | 23 | var credential model.Credential 24 | credential.Name = "unit-testing" 25 | credential.AccessKey = os.Getenv("UNSPLASH_ACCESS_KEY") 26 | credential.SecretKey = os.Getenv("UNSPLASH_SECRET_KEY") 27 | credential.Provider = "unsplash" 28 | 29 | profile.Credentials = append(profile.Credentials, credential) 30 | credentials.Profiles = append(credentials.Profiles, profile) 31 | aid.WriteInterfaceToFile(credentials, aid.GetAppInfo().CredentialsPath) 32 | } 33 | 34 | func createUnplashConfiguration() { 35 | var configurations model.Configurations 36 | 37 | var profile model.ConfigurationProfile 38 | profile.Name = "default" 39 | 40 | var configuration model.Configuration 41 | configuration.Name = "unit-testing" 42 | 43 | var unsplash model.Unsplash 44 | 45 | var randomPhoto model.UnsplashRandomPhoto 46 | 47 | var params model.UnsplashRandomPhotoParameters 48 | params.Query = "eagle" 49 | 50 | randomPhoto.Parameters = params 51 | unsplash.RandomPhoto = randomPhoto 52 | configuration.Unsplash = unsplash 53 | 54 | profile.Configurations = append(profile.Configurations, configuration) 55 | configurations.Profiles = append(configurations.Profiles, profile) 56 | aid.WriteInterfaceToFile(configurations, aid.GetAppInfo().ConfigurationsPath) 57 | } 58 | 59 | func DeleteCredential() { 60 | if aid.CredentialsFileExist() { 61 | aid.DeleteCredentialFile() 62 | } 63 | } 64 | 65 | func TestUnsplashEmptyWithoutCredentials(t *testing.T) { 66 | aid.DeleteConfigurationsDirectory() 67 | args := []string{"unsplash"} 68 | out, err := executeCommand(t, controller.UnsplashCmd(), args) 69 | assert.NotNil(t, err) 70 | assert.Contains(t, out, "") 71 | assert.Contains(t, err.Error(), "unable to read credentials") 72 | assert.Contains(t, err.Error(), "unable to read configuration") 73 | 74 | } 75 | 76 | func TestUnsplashEmptyWithCredentials(t *testing.T) { 77 | createUnplashCredential() 78 | defer aid.DeleteConfigurationsDirectory() 79 | 80 | args := []string{"unsplash"} 81 | _, err := executeCommand(t, controller.UnsplashCmd(), args) 82 | 83 | sep := string(os.PathSeparator) 84 | dir := t.Name() + sep 85 | 86 | assert.Nil(t, err) 87 | assert.FileExists(t, dir+sep+"unsplash.yaml") 88 | assert.DirExists(t, dir+sep+"downloads") 89 | assert.DirExists(t, dir+sep+"downloads"+sep+"unsplash") 90 | assert.DirExists(t, dir+sep+"downloads"+sep+"unsplash"+sep+"mountains") 91 | 92 | files := helper.ListFiles(dir + sep + "downloads" + sep + "unsplash" + sep + "mountains") 93 | assert.GreaterOrEqual(t, len(files), 5) 94 | } 95 | 96 | func TestUnsplashQuery(t *testing.T) { 97 | createUnplashCredential() 98 | defer aid.DeleteConfigurationsDirectory() 99 | 100 | args := []string{"unsplash", "--query", "horse"} 101 | _, err := executeCommand(t, controller.UnsplashCmd(), args) 102 | 103 | sep := string(os.PathSeparator) 104 | dir := t.Name() + sep 105 | 106 | assert.Nil(t, err) 107 | assert.FileExists(t, dir+sep+"unsplash.yaml") 108 | assert.DirExists(t, dir+sep+"downloads") 109 | assert.DirExists(t, dir+sep+"downloads"+sep+"unsplash") 110 | assert.DirExists(t, dir+sep+"downloads"+sep+"unsplash"+sep+"horse") 111 | 112 | files := helper.ListFiles(dir + sep + "downloads" + sep + "unsplash" + sep + "horse") 113 | assert.GreaterOrEqual(t, len(files), 5) 114 | } 115 | 116 | func TestRenderUpdateLogoFromUnsplashFile(t *testing.T) { 117 | createUnplashCredential() 118 | 119 | args := []string{"init", "project", "--project-name", "foo", "--project-type", "basic"} 120 | wd, out, err := executeCommandOnTemporaryDirectory(t, controller.InitCmd(), args) 121 | assert.NotEmpty(t, wd) 122 | assert.NotEmpty(t, out) 123 | assert.Nil(t, err) 124 | 125 | os.Chdir("foo") 126 | 127 | args = []string{"unsplash", "--query", "horse", "--size", "regular"} 128 | out, err = executeCommandOnly(t, controller.UnsplashCmd(), args) 129 | assert.Empty(t, out) 130 | assert.Nil(t, err) 131 | 132 | args = []string{"render", "template"} 133 | out, err = executeCommandOnly(t, controller.RenderCmd(), args) 134 | assert.Nil(t, err) 135 | assert.Contains(t, out, "Template readme.tmpl rendered as README.md") 136 | 137 | } 138 | 139 | // func TestRenderUpdateLogoFromConfigurations(t *testing.T) { 140 | // createUnplashCredential() 141 | // createUnplashConfiguration() 142 | 143 | // args := []string{"init", "project", "--project-name", "foo", "--project-type", "basic"} 144 | // wd, out, err := executeCommandOnTemporaryDirectory(t, controller.InitCmd(), args) 145 | // assert.NotEmpty(t, wd) 146 | // assert.NotEmpty(t, out) 147 | // assert.Nil(t, err) 148 | 149 | // os.Chdir("foo") 150 | 151 | // args = []string{"render", "template"} 152 | // out, err = executeCommandOnly(t, controller.RenderCmd(), args) 153 | // assert.Nil(t, err) 154 | // assert.Contains(t, out, "Template readme.tmpl rendered as README.md") 155 | 156 | // } 157 | -------------------------------------------------------------------------------- /tests/cmd_version_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/awslabs/clencli/cobra/controller" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestVersionCmd(t *testing.T) { 11 | args := []string{"version"} 12 | cmd := controller.VersionCmd() 13 | out, err := executeCommand(t, cmd, args) 14 | assert.Nil(t, err) 15 | assert.Contains(t, out, "clencli v") 16 | } 17 | -------------------------------------------------------------------------------- /tests/pkg_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "testing" 7 | "time" 8 | 9 | "github.com/awslabs/clencli/cobra/controller" 10 | "github.com/awslabs/clencli/helper" 11 | "github.com/sirupsen/logrus" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | /* SETUP */ 16 | 17 | func beforeSetup() { 18 | format := "2006-01-02-15-04-05.000000000" 19 | dt := time.Now().Format(format) 20 | 21 | dir := helper.CreateTempDir(os.TempDir(), "clencli-"+dt) 22 | 23 | // enter the new directory 24 | err := os.Chdir(dir) 25 | if err != nil { 26 | logrus.Fatal(err) 27 | } 28 | } 29 | 30 | func TestMain(m *testing.M) { 31 | beforeSetup() 32 | os.Exit(m.Run()) 33 | } 34 | 35 | /* COBRA */ 36 | 37 | func executeCommand(t *testing.T, cmd *cobra.Command, args []string) (stdout string, err error) { 38 | wd := createAndEnterTestDirectory(t) 39 | _, stdout, err = executeCommandC(cmd, args) 40 | os.Chdir(wd) 41 | 42 | return stdout, err 43 | } 44 | 45 | func executeCommandGetWorkingDirectory(t *testing.T, cmd *cobra.Command, args []string) (wd string, stdout string, err error) { 46 | wd = createAndEnterTestDirectory(t) 47 | _, stdout, err = executeCommandC(cmd, args) 48 | os.Chdir(wd) 49 | 50 | return wd, stdout, err 51 | } 52 | 53 | func executeCommandOnTemporaryDirectory(t *testing.T, cmd *cobra.Command, args []string) (wd string, stdout string, err error) { 54 | wd = t.TempDir() 55 | os.Chdir(wd) 56 | _, stdout, err = executeCommandC(cmd, args) 57 | return wd, stdout, err 58 | } 59 | 60 | // executeCommandOnly only executes the given command without changing the current directory, useful for combined tests 61 | func executeCommandOnly(t *testing.T, cmd *cobra.Command, args []string) (stdout string, err error) { 62 | _, stdout, err = executeCommandC(cmd, args) 63 | return stdout, err 64 | } 65 | 66 | // return the current working directory, useful to return to the previous directory 67 | func createAndEnterTestDirectory(t *testing.T) string { 68 | wd, err := os.Getwd() 69 | if err != nil { 70 | logrus.Fatalf("unable to get current working directory") 71 | } 72 | 73 | dir := createTestDirectory(t) 74 | os.Chdir(dir) 75 | return wd 76 | } 77 | 78 | // createTestDirectory create the testing directory and enters it 79 | func createTestDirectory(t *testing.T) string { 80 | created := helper.MkDirsIfNotExist(t.Name()) 81 | if !created { 82 | logrus.Infoln("directory already exist, skipping...") 83 | } 84 | 85 | return t.Name() 86 | } 87 | 88 | func executeCommandC(cmd *cobra.Command, args []string) (command *cobra.Command, stdout string, err error) { 89 | buf := new(bytes.Buffer) 90 | 91 | rootCmd := controller.RootCmd() 92 | rootCmd.AddCommand(cmd) 93 | rootCmd.SetOut(buf) 94 | rootCmd.SetErr(buf) 95 | // args = append(args, "--verbosity", "trace") 96 | rootCmd.SetArgs(args) 97 | 98 | command, err = rootCmd.ExecuteC() 99 | stdout = buf.String() 100 | return command, stdout, err 101 | } 102 | 103 | // InitRootAndChildCmd initializes Cobra `root` command and add the `childCmd` to it 104 | func InitRootAndChildCmd(rootCmd *cobra.Command, childCmd *cobra.Command) (*cobra.Command, *cobra.Command) { 105 | rootCmd.AddCommand(childCmd) 106 | return rootCmd, childCmd 107 | } 108 | -------------------------------------------------------------------------------- /unsplash.yaml: -------------------------------------------------------------------------------- 1 | id: dBWrcQZhXYU 2 | createdat: "" 3 | updatedat: "" 4 | promotedat: null 5 | width: 5262 6 | height: 3758 7 | color: '#d9d9d9' 8 | description: "" 9 | altdescription: "" 10 | urls: 11 | raw: https://images.unsplash.com/photo-1615206605589-501c4d8e0d1f?ixid=MnwxOTEyNTB8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjA5OTkyNzc&ixlib=rb-1.2.1 12 | full: https://images.unsplash.com/photo-1615206605589-501c4d8e0d1f?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxOTEyNTB8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjA5OTkyNzc&ixlib=rb-1.2.1&q=85 13 | regular: https://images.unsplash.com/photo-1615206605589-501c4d8e0d1f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTEyNTB8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjA5OTkyNzc&ixlib=rb-1.2.1&q=80&w=1080 14 | small: https://images.unsplash.com/photo-1615206605589-501c4d8e0d1f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTEyNTB8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjA5OTkyNzc&ixlib=rb-1.2.1&q=80&w=400 15 | thumb: https://images.unsplash.com/photo-1615206605589-501c4d8e0d1f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxOTEyNTB8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjA5OTkyNzc&ixlib=rb-1.2.1&q=80&w=200 16 | links: 17 | self: https://api.unsplash.com/photos/dBWrcQZhXYU 18 | html: https://unsplash.com/photos/dBWrcQZhXYU 19 | download: https://unsplash.com/photos/dBWrcQZhXYU/download 20 | downloadlocation: "" 21 | categories: [] 22 | likes: 0 23 | likedbyuser: false 24 | currentusercollections: [] 25 | sponsorship: null 26 | user: 27 | id: OdVwjG7wqcI 28 | updatedat: "" 29 | username: fdiascreator 30 | name: Felipe Dias 31 | firstname: "" 32 | lastname: "" 33 | twitterusername: "" 34 | portfoliourl: "" 35 | bio: "" 36 | location: Salvador 37 | links: 38 | self: https://api.unsplash.com/users/fdiascreator 39 | html: https://unsplash.com/@fdiascreator 40 | photos: https://api.unsplash.com/users/fdiascreator/photos 41 | likes: https://api.unsplash.com/users/fdiascreator/likes 42 | portfolio: https://api.unsplash.com/users/fdiascreator/portfolio 43 | following: https://api.unsplash.com/users/fdiascreator/following 44 | followers: https://api.unsplash.com/users/fdiascreator/followers 45 | profileimage: 46 | small: "" 47 | medium: "" 48 | large: "" 49 | instagramusername: "" 50 | totalcollections: 0 51 | totallikes: 0 52 | totalphotos: 0 53 | acceptedtos: false 54 | exif: 55 | make: Hasselblad 56 | model: L1D-20c 57 | exposuretime: "" 58 | aperture: "6.3" 59 | focallength: "" 60 | iso: 100 61 | location: 62 | title: Remanso, BA, Brasil 63 | name: Remanso, BA, Brasil 64 | city: Remanso 65 | country: Brasil 66 | position: 67 | latitude: -9.625701 68 | longitude: -42.086128 69 | views: 92355 70 | downloads: 146 71 | --------------------------------------------------------------------------------