From f3db8230f45d9ced7760b8bd27fffd911ca47611 Mon Sep 17 00:00:00 2001 From: Ryan Parman Date: Wed, 3 Apr 2024 15:52:20 -0600 Subject: [PATCH] Feature/v2 (#3) * refactor: Massive reset of the entire codebase. * refactor: New implementation. * fix: Small adjustments to error handling. * deps: Updated the Go dependencies. * refactor: Switch to using a table. * docs: Added a README. * docs: Added a README. --- .dcignore | 110 ++++ .ecrc | 67 +++ .editorconfig | 44 +- .gitattributes | 50 +- .githooks/commit-msg.sh | 3 + .github/CODEOWNERS | 5 + .github/dco.yml | 7 + .github/dependabot.yml | 28 + .github/workflows/codeql-analysis.yml | 37 -- .gitignore | 44 +- .golangci.yml | 710 +++++++++++++++----------- .gommit.toml | 19 + .goplicate.yaml | 77 +++ .licensei.toml | 15 + .mailmap | 25 + .markdownlint.base.jsonc | 256 ++++++++++ .markdownlint.json | 107 ---- .markdownlint.jsonc | 15 + .pre-commit-config.yaml | 178 +++++++ .vscode/extensions.json | 123 +++-- .vscode/settings.json | 369 ++++++++++++- .woke.yml | 26 - CHANGELOG.md | 9 - LICENSE => LICENSE.txt | 2 +- Makefile | 196 ------- README.md | 74 +-- SECURITY.md | 35 ++ VERSION | 1 - __first_time.sh | 108 ++++ __update.sh | 86 ++++ aws/ec2_instances.go | 124 +++++ run_command.go => aws/functions.go | 10 +- cliff.toml | 136 +++++ cmd/root.go | 291 +++++++++++ cmd/version.go | 122 +++++ cmd_connect.go | 139 ----- cmd_version.go | 55 -- ec2_instances.go | 229 --------- ecrc.toml | 75 +++ go.mod | 72 ++- go.sum | 179 ++++--- main.go | 95 +--- mkdocs.yml | 97 ---- poetry.toml | 1 - prompt.go | 27 - pyproject.toml | 20 - requirements.txt | 1 - screenshot.png | Bin 31760 -> 0 bytes scripts/generate-contributors.sh | 21 + ssm-shell@2x.png | 3 + strings.go | 54 -- templates/usage.gohtml | 24 - trivy-license.yaml | 201 ++++++++ trivy-vuln.yaml | 62 +++ versions.go | 31 -- versions_test.go | 48 -- 56 files changed, 3214 insertions(+), 1729 deletions(-) create mode 100644 .dcignore create mode 100644 .ecrc create mode 100755 .githooks/commit-msg.sh create mode 100644 .github/CODEOWNERS create mode 100644 .github/dco.yml create mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .gommit.toml create mode 100644 .goplicate.yaml create mode 100644 .licensei.toml create mode 100644 .mailmap create mode 100644 .markdownlint.base.jsonc delete mode 100644 .markdownlint.json create mode 100644 .markdownlint.jsonc create mode 100644 .pre-commit-config.yaml delete mode 100644 .woke.yml delete mode 100644 CHANGELOG.md rename LICENSE => LICENSE.txt (99%) delete mode 100644 Makefile create mode 100644 SECURITY.md delete mode 100644 VERSION create mode 100755 __first_time.sh create mode 100755 __update.sh create mode 100644 aws/ec2_instances.go rename run_command.go => aws/functions.go (67%) create mode 100644 cliff.toml create mode 100644 cmd/root.go create mode 100644 cmd/version.go delete mode 100644 cmd_connect.go delete mode 100644 cmd_version.go delete mode 100644 ec2_instances.go create mode 100644 ecrc.toml delete mode 100644 mkdocs.yml delete mode 100644 poetry.toml delete mode 100644 prompt.go delete mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 screenshot.png create mode 100755 scripts/generate-contributors.sh create mode 100644 ssm-shell@2x.png delete mode 100644 strings.go delete mode 100644 templates/usage.gohtml create mode 100644 trivy-license.yaml create mode 100644 trivy-vuln.yaml delete mode 100644 versions.go delete mode 100644 versions_test.go diff --git a/.dcignore b/.dcignore new file mode 100644 index 0000000..a334c0b --- /dev/null +++ b/.dcignore @@ -0,0 +1,110 @@ +# Write glob rules for ignored files. +# Check syntax on https://deepcode.freshdesk.com/support/solutions/articles/60000531055-how-can-i-ignore-files-or-directories- +# Used by Snyk; https://docs.snyk.io/features/integrations/ide-tools/visual-studio-code-extension-for-snyk-code + +# Hidden directories +.*/ + +# Python +__pycache__/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +MANIFEST +htmlcov/ +cover/ +instance/ +docs/_build/ +target/ +profile_default/ +__pypackages__/ +celerybeat-schedule +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +/site +cython_debug/ + +# JupyterNotebooks +profile_default/ + +# Hugo +/public/ +/resources/_gen/ + +# VirtualEnv +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts + +# SublimeText +Package Control.cache/ +Package Control.ca-certs/ + +# Windows +$RECYCLE.BIN/ + +# Linux +*~ + +# Emacs +*~ +#*# +auto-save-list +tramp +*_archive +/eshell/history +/eshell/lastdir +/elpa/ +/auto/ +dist/ +/server/ + +# macOS +Icon + +Network Trash Folder +Temporary Items + +# JetBrains +cmake-build-*/ +out/ + +# Vim +*~ +tags + +# Node +logs +pids +lib-cov +coverage +bower_components +build/Release +node_modules/ +jspm_packages/ +web_modules/ +out +dist + +# Rust +debug/ +target/ + +# Packer +packer_cache/ diff --git a/.ecrc b/.ecrc new file mode 100644 index 0000000..dbcae21 --- /dev/null +++ b/.ecrc @@ -0,0 +1,67 @@ +{ + "Debug": false, + "Exclude": [ + "\\.7z$", + "\\.avif", + "\\.bak$", + "\\.bin$", + "\\.bz2$", + "\\.cache$", + "\\.css\\.map$", + "\\.dcignore$", + "\\.ecrc$", + "\\.eot$", + "\\.example$", + "\\.gif$", + "\\.go$", + "\\.golangci.yml$", + "\\.goreleaser.yml$", + "\\.gotmpl$", + "\\.gz$", + "\\.ico$", + "\\.jpeg$", + "\\.jpg$", + "\\.js\\.map$", + "\\.log$", + "\\.mp4$", + "\\.otf$", + "\\.patch$", + "\\.pbm", + "\\.pdf$", + "\\.pgm", + "\\.png$", + "\\.pnm", + "\\.ppm", + "\\.snap$", + "\\.svg$", + "\\.tar$", + "\\.terraform-docs\\.yml$", + "\\.terraform\\.lock\\.hcl$", + "\\.ttf$", + "\\.txt$", + "\\.vscode/.*?\\.json$", + "\\.webp$", + "\\.wmv$", + "\\.woff$", + "\\.woff2$", + "\\.zip$", + "^\\.pnp\\.cjs$", + "^\\.pnp\\.js$", + "^\\.pnp\\.loader\\.mjs$", + "^\\.yarn/", + "^Cargo\\.lock$", + "^composer\\.lock$", + "^package-lock\\.json$", + "^yarn\\.lock$", + "cliff\\.toml$", + "go\\.mod$", + "go\\.sum$", + "min\\.css$", + "min\\.js$", + "package-lock\\.json$" + ], + "IgnoreDefaults": true, + "NoColor": false, + "SpacesAfterTabs": false, + "Verbose": false +} diff --git a/.editorconfig b/.editorconfig index 63f4651..3478d46 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ # Uses editorconfig to maintain consistent coding styles -# http://EditorConfig.org +# https://editorconfig.org root = true @@ -10,21 +10,45 @@ indent_size = 4 indent_style = space insert_final_newline = true max_line_length = 120 +tab_width = 4 trim_trailing_whitespace = true -[*.toml] -indent_size = 4 +[Makefile*] +indent_style = tab +max_line_length = 20000 -[*.{css,html,js,json,less,sass,scss,yaml,yml}] +# Web +[*.{css,html,js,less,sass,scss}] indent_size = 2 -[*.{tf,tfvars}] +# Configuration formats +[*.{hcl,json,jsonc,toml,yaml,yml}] indent_size = 2 -indent_style = space -[*.md] -max_line_length = 0 -trim_trailing_whitespace = false +[.ecrc] +indent_size = 2 -[Makefile*] +[*.go] indent_style = tab + +[*.{md,md.tmpl}] +indent_size = 2 +max_line_length = 20000 + +[*.py] +indent_size = 4 + +[*.sh] +indent_size = 4 +max_line_length = 120 + +# Terraform files +[*.{tf,tftpl,tfvars}] +indent_size = 2 +indent_style = space + +[.yamllint] +indent_size = 2 + +[bats/*.sh] +max_line_length = 20000 diff --git a/.gitattributes b/.gitattributes index c42e551..343ab72 100644 --- a/.gitattributes +++ b/.gitattributes @@ -20,12 +20,12 @@ *.bibtex text diff=bibtex *.doc diff=astextplain *.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain +*.docx filter=lfs diff=lfs merge=lfs -text +*.DOCX filter=lfs diff=lfs merge=lfs -text *.dot diff=astextplain *.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain +*.pdf filter=lfs diff=lfs merge=lfs -text +*.PDF filter=lfs diff=lfs merge=lfs -text *.rtf diff=astextplain *.RTF diff=astextplain *.md text eol=lf diff=markdown @@ -37,7 +37,6 @@ *.tab text eol=lf *.tsv text eol=lf *.txt text eol=lf -*.sql text eol=lf *.markdown text eol=lf diff=markdown *.md text eol=lf diff=markdown *.mdwn text eol=lf diff=markdown @@ -54,33 +53,34 @@ Makefile text eol=lf *README* text eol=lf # Graphics -*.png binary -*.jpg binary -*.jpeg binary -*.gif binary -*.tif binary -*.tiff binary -*.ico binary -*.eps binary +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text +*.tif filter=lfs diff=lfs merge=lfs -text +*.tiff filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.eps filter=lfs diff=lfs merge=lfs -text *.svg text eol=lf -*.svgz binary -*.webp binary +*.svgz filter=lfs diff=lfs merge=lfs -text +*.webp filter=lfs diff=lfs merge=lfs -text # Scripts *.bash text eol=lf *.fish text eol=lf *.sh text eol=lf + # These are explicitly windows files and should use crlf *.bat text eol=crlf *.cmd text eol=crlf *.ps1 text eol=crlf # Fonts -*.ttf binary -*.eot binary -*.otf binary -*.woff binary -*.woff2 binary +*.ttf filter=lfs diff=lfs merge=lfs -text +*.eot filter=lfs diff=lfs merge=lfs -text +*.otf filter=lfs diff=lfs merge=lfs -text +*.woff filter=lfs diff=lfs merge=lfs -text +*.woff2 filter=lfs diff=lfs merge=lfs -text # Serialization *.ini text eol=lf @@ -101,11 +101,11 @@ Makefile text eol=lf package-lock.json text eol=lf -diff # Archives -*.7z binary -*.gz binary -*.tar binary -*.tgz binary -*.zip binary +*.7z filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text # Database *.sql text eol=lf diff --git a/.githooks/commit-msg.sh b/.githooks/commit-msg.sh new file mode 100755 index 0000000..bd89daf --- /dev/null +++ b/.githooks/commit-msg.sh @@ -0,0 +1,3 @@ +#!/bin/bash +# shellcheck disable=2312 +gommit check message "$(cat "$1")" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..02d81cd --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @skyzyx diff --git a/.github/dco.yml b/.github/dco.yml new file mode 100644 index 0000000..4fc52e8 --- /dev/null +++ b/.github/dco.yml @@ -0,0 +1,7 @@ +--- +# https://github.com/dcoapp/app +allowRemediationCommits: + individual: true + +require: + members: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8152274 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,28 @@ +--- +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + # ---------------------------------------------------------------------------- + # goplicate-start:always + - package-ecosystem: docker + directory: /.devcontainer + schedule: + interval: daily + + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + # goplicate-end:always + + # ---------------------------------------------------------------------------- + # goplicate-start:go + - package-ecosystem: gomod # See documentation for possible values + directory: / # Location of package manifests + schedule: + interval: daily + # goplicate-end:go diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 3c9bc02..0000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [ main ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] - schedule: - - cron: '33 20 * * 1' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.gitignore b/.gitignore index d902d94..b255586 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,42 @@ -bin/* -dist/* +# goplicate-start:always +_*.out +_*.txt +_/ +_cache_/* +.dccache +*.cache.json +*.zip +dist/ +# goplicate-end:always + +# goplicate-start:go +*.pgo +*.pprof +*.test +callgrind.* +# goplicate-end:go + +# goplicate-start:terraform +.infracost +.terraform +.terraform.lock.hcl +.terraformrc +*_override.tf +*_override.tf.json +_provider.tf +*.tfstate* +**/.terraform/* +override.tf +override.tf.json +secrets.auto.tfvars +terraform +terraform.d/ +terraform.rc +tests/.test-data +tests/**/.test-data +tests/**/terraform.* +tests/**/terratest-* +tests/terraform.* +tests/terratest-* +tfplan +# goplicate-end:terraform diff --git a/.golangci.yml b/.golangci.yml index f1e33e0..c6a0660 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,194 +1,158 @@ -# This file contains all available configuration options -# with their default values. +--- +# https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml +# https://golangci-lint.run/jsonschema/golangci.jsonschema.json -# options for analysis running run: - # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 1m + skip-files: + # - .*\.my\.go$ + # - lib/bad.go - # exit code when at least one issue was found, default is 1 + # goplicate-start:run + timeout: 1m issues-exit-code: 1 - - # include test files or not, default is true - tests: false - - # list of build tags, all linters use it. Default is empty list. - # build-tags: [] - - # which dirs to skip: issues from them won't be reported; - # can use regexp here: generated.*, regexp is applied on full path; - # default value is empty list, but default dirs are skipped independently - # from this option's value (see skip-dirs-use-default). - skip-dirs: - - assets - - # default is true. Enables skipping of directories: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + tests: true skip-dirs-use-default: true + modules-download-mode: readonly + allow-parallel-runners: false + # goplicate-end:run - # which files to skip: they will be analyzed, but issues from them - # won't be reported. Default value is empty list, but there is - # no need to include all autogenerated files, we confidently recognize - # autogenerated files. If it's not please let us know. - # skip-files: [] - # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": - # If invoked with -mod=readonly, the go command is disallowed from the implicit - # automatic updating of go.mod described above. Instead, it fails when any changes - # to go.mod are needed. This setting is most useful to check that go.mod does - # not need updates, such as in a continuous integration and testing system. - # If invoked with -mod=vendor, the go command assumes that the vendor - # directory holds the correct copies of dependencies and ignores - # the dependency descriptions in go.mod. - # modules-download-mode: readonly|release|vendor +# goplicate-start:severity +severity: + case-sensitive: false +# goplicate-end:severity +# goplicate-start:linters linters: - # please, do not use `enable-all`: it's deprecated and will be removed soon. - # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint disable-all: true enable: - - asciicheck - - bodyclose - - deadcode - - depguard + - decorder - dogsled - dupl - - durationcheck + - dupword - errcheck + - errchkjson - errorlint - exhaustive - - exportloopref - - forcetypeassert - - gci - - gochecknoinits + - funlen + - gocognit - goconst - gocritic - - godot - godox - - goerr113 - gofmt - - gofumpt - - goheader - goimports - gomnd - - gomodguard - - goprintffuncname - gosec - gosimple - govet - - ifshort - importas - - ineffassign + - interfacebloat - lll + - maintidx - makezero - misspell - nakedret - nestif - - nilerr + - nilnil - nlreturn - - noctx - - nolintlint + - nonamedreturns - paralleltest - - prealloc - - predeclared - - revive - - rowserrcheck - - sqlclosecheck + # - revive: @TODO: Review and enable + - sloglint - staticcheck - - structcheck - stylecheck - - testpackage + - tagalign + - tagliatelle - thelper - - tparallel - - typecheck - - unconvert - unparam - unused - - varcheck - - wastedassign + - usestdlibvars - whitespace - wrapcheck - wsl +# goplicate-end:linters - # ==> don't enable: - # - cyclop - # - forbidigo - # - funlen - # - gochecknoglobals - # - gocognit - # - gocyclo - - # ==> deprecated and will be removed soon: - # - exhaustivestruct - # - interfacer - # - maligned - -# output configuration options +# goplicate-start:output output: - # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" - format: colored-line-number - - # print lines of code with issue, default is true + formats: + - path: stdout + format: colored-line-number print-issued-lines: true - - # print linter name in the end of issue text, default is true print-linter-name: true + uniq-by-line: true + path-prefix: '' + sort-results: true +# goplicate-end:output -# all available settings of specific linters +# goplicate-start:linters-settings linters-settings: - # asciicheck: - # bodyclose: - # deadcode: - - depguard: - list-type: blacklist - include-go-root: true - packages-with-error-messages: - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" + decorder: + disable-dec-order-check: false + disable-init-func-first-check: false + disable-dec-num-check: false + dec-order: + - const + - var + - type + - func dogsled: - # checks assignments with too many blank identifiers; default is 2 max-blank-identifiers: 2 dupl: - # tokens count to trigger issue, 150 by default threshold: 100 - # durationcheck: + dupword: + keywords: + - a + - an + - and + - of + - or + - the + - this errcheck: - # report about not checking of errors in type assetions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. - check-type-assertions: false - - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; - # default is false: such cases aren't reported by default. + check-type-assertions: true check-blank: false - # path to a file containing a list of functions to exclude from checking - # see https://github.com/kisielk/errcheck#excluding-functions for details - # exclude: /path/to/file.txt + errchkjson: + check-error-free-encoding: false + report-no-exported: false + + errorlint: + errorf: true + asserts: true + comparison: true - # errorlint: - # exhaustive: - # exportloopref: - # forcetypeassert: + exhaustive: + check-generated: true + default-signifies-exhaustive: true + package-scope-only: false + explicit-exhaustive-switch: false + explicit-exhaustive-map: false + check: + - switch + - map - # funlen: - # lines: 120 - # statements: 85 + exhaustruct: - # gci: - # gochecknoinits: + funlen: + lines: -1 + statements: -1 + ignore-comments: true + + gocognit: + min-complexity: 20 goconst: - # minimal length of string constant, 3 by default - min-len: 2 - # minimal occurrences count to trigger, 3 by default - min-occurrences: 2 + min-len: 80 + min-occurrences: 3 + ignore-tests: false + match-constant: true + numbers: false + ignore-calls: true gocritic: - # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. - # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". enabled-tags: - diagnostic - experimental @@ -196,71 +160,135 @@ linters-settings: - performance - style - # Which checks should be enabled; can't be combined with 'disabled-checks'; - # See https://go-critic.github.io/overview#checks-overview - # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` - # By default list of stable checks is used. - # enabled-checks: - # - rangeValCopy - - # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty - disabled-checks: - - wrapperFunc - - dupImport # https://github.com/go-critic/go-critic/issues/845 - - ifElseChain - - octalLiteral - - settings: # settings passed to gocritic - captLocal: # must be valid enabled check name + disabled-tags: [] + + settings: + captLocal: paramsOnly: true + elseif: + skipBalanced: true + hugeParam: + sizeThreshold: 80 + nestingReduce: + bodyWidth: 5 + rangeExprCopy: + sizeThreshold: 512 + skipTestFuncs: true rangeValCopy: - sizeThreshold: 32 - - # godot: + sizeThreshold: 128 + skipTestFuncs: true + ruleguard: + tooManyResultsChecker: + maxResults: 20 + truncateCmp: + skipArchDependent: true + underef: + skipRecvDeref: true + unnamedResult: + checkExported: false godox: - # report any comments starting with keywords, this is useful for TODO or FIXME comments that - # might be left in the code accidentally and should be resolved before merging - keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + keywords: - BUG - FIXME - HACK - NOTE - OPTIMIZE - TODO - - "@todo" - - # goerr113: + - '@TODO' gofmt: - # simplify code: gofmt with `-s` option, true by default simplify: true - - # gofumpt: - # goheader: + rewrite-rules: + - pattern: interface{} + replacement: any + - pattern: a[b:len(a)] + replacement: a[b:] goimports: - # put imports beginning with prefix after 3rd-party packages; - # it's a comma-separated list of prefixes - local-prefixes: github.mheducation.com/monitoring-as-code/monitorkit - golint: - # minimal confidence for issues, default is 0.8 - min-confidence: 0 - - # gomnd: - # gomodguard: - # goprintffuncname: - # gosec: - # gosimple: + gomnd: + checks: + - argument + - case + - condition + - operation + - return + - assign + + ignored-numbers: + - '0666' + - '0755' + + gosimple: + checks: ['*'] + + gosec: + exclude-generated: true + severity: low + confidence: medium + config: + global: + nosec: false + show-ignored: false + audit: true + G101: + ignore_entropy: false + entropy_threshold: '80.0' + per_char_threshold: '3.0' + truncate: '16' + G104: + fmt: + - Fscanf + G111: + pattern: custom\.Dir\(\) + G301: '0750' + G302: '0600' + G306: '0666' govet: - # report about shadowed variables - check-shadowing: true + enable: + - appends + - asmdecl + - assign + - atomic + - atomicalign + - bools + - buildtag + - cgocall + - composites + - copylocks + - deepequalerrors + - defers + - directive + - errorsas + - fieldalignment + - findcall + - framepointer + - httpresponse + - ifaceassert + - loopclosure + - lostcancel + - nilfunc + - nilness + - printf + - reflectvaluecompare + - shadow + - shift + - sigchanyzer + - slog + - sortslice + - stdmethods + - stringintconv + - structtag + - testinggoroutine + - tests + - unmarshal + - unreachable + - unsafeptr + - unusedresult + - unusedwrite - # settings per analyzer - # run `go tool vet help` to see all analyzers - # run `go tool vet help printf` to see available settings for `printf` analyzer settings: asmdecl: {} assign: {} @@ -270,7 +298,6 @@ linters-settings: composites: whitelist: true copylocks: {} - # errorsas: {} loopclosure: {} lostcancel: {} nilfunc: {} @@ -286,203 +313,284 @@ linters-settings: funcs: true stringmethods: true - # ifshort: - # importas: - # ineffassign: + importas: + no-unaliased: true + no-extra-aliases: false + + interfacebloat: + max: 10 lll: - # max line length, lines longer will be reported. Default is 120. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option line-length: 120 - # tab width in spaces. Default to 1. tab-width: 1 - # makezero: + maintidx: + under: 20 + + makezero: + always: false misspell: - # Correct spellings using locale preferences for US or UK. - # Default is to use a neutral variety of English. - # Setting locale to US will correct the British spelling of 'colour' to 'color'. locale: US - # ignore-words: - # - someword nakedret: - # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 max-func-lines: 30 nestif: min-complexity: 10 - # nilerr: - # nlreturn: - # noctx: - # nolintlint: - # paralleltest: - # prealloc: - # predeclared: - # revive: - # rowserrcheck: - # staticcheck: - # structcheck: - # stylecheck: - # testpackage: - # thelper: - # tparallel: - # typecheck: - # unconvert: + nilnil: + checked-types: + - ptr + - func + - iface + - map + - chan + + nlreturn: + block-size: 2 + + nonamedreturns: + report-error-in-defer: false + + paralleltest: + ignore-missing: true + ignore-missing-subtests: true + + # revive: @TODO: Review and enable. + + sloglint: + kv-only: true + attr-only: false + no-raw-keys: true + args-on-sep-lines: false + + staticcheck: + checks: ['*'] + + stylecheck: + checks: ['*'] + + tagalign: + align: true + sort: true + strict: true + + tagliatelle: + case: + use-field-name: false + rules: + avro: snake + bson: camel + env: upperSnake + envconfig: upperSnake + ini: snake + json: camel + mapstructure: kebab + toml: snake + xml: camel + yaml: camel + + thelper: + test: + first: true + name: true + begin: true + + benchmark: + first: true + name: true + begin: true + + tb: + first: true + name: true + begin: true + + fuzz: + first: true + name: true + begin: true + + usestdlibvars: + http-method: true + http-status-code: true + time-weekday: true + time-month: false + time-layout: false + crypto-hash: false + default-rpc-path: false + sql-isolation-level: false + tls-signature-scheme: false + constant-kind: false unparam: - # Inspect exported functions, default is false. Set to true if no external program/library imports your code. - # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find external interfaces. All text editor integrations - # with golangci-lint call it on a directory with the changed file. check-exported: false unused: - # treat code as a program (not a library) and report unused exported identifiers; default is false. - # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find funcs usages. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: true - - # varcheck: - # wastedassign: + field-writes-are-uses: true + post-statements-are-reads: false + exported-is-used: true + exported-fields-are-used: true + parameters-are-used: true + local-variables-are-used: true + generated-is-used: true whitespace: - multi-if: false # Enforces newlines (or comments) after every multi-line if statement - multi-func: false # Enforces newlines (or comments) after every multi-line function signature + multi-if: false + multi-func: false + + wrapcheck: + ignoreSigs: + - .Errorf( + - errors.New( + - errors.Unwrap( + - .Wrap( + - .Wrapf( + - .WithMessage( + - .WithMessagef( + - .WithStack( - # wrapcheck: wsl: - # If true append is only allowed to be cuddled if appending value is - # matching variables, fields or types on line above. Default is true. - strict-append: true - - # Allow calls and assignments to be cuddled as long as the lines have any - # matching variables, fields or types. Default is true. + allow-assign-and-anything: false allow-assign-and-call: true - - # Allow multiline assignments to be cuddled. Default is true. - allow-multiline-assign: true - - # Allow declarations (var) to be cuddled. allow-cuddle-declarations: false - - # Allow trailing comments in ending of blocks + allow-cuddle-with-calls: [Lock, RLock] + allow-cuddle-with-rhs: [Unlock, RUnlock] + allow-multiline-assign: true + allow-separated-leading-comment: true allow-trailing-comment: false - - # Force newlines in end of case at this limit (0 = never). + error-variable-names: [err] force-case-trailing-whitespace: 0 + force-err-cuddling: false + force-short-decl-cuddling: false + strict-append: true +# goplicate-end:linters-settings - # Force cuddling of err checks with err var assignment - force-err-cuddling: true - - # Allow leading comments to be separated with empty liens - allow-separated-leading-comment: false - +# goplicate-start:issues issues: - # List of regexps of issue texts to exclude, empty list by default. - # But independently from this option we use default exclude patterns, - # it can be disabled by `exclude-use-default: false`. To list all - # excluded by default patterns execute `golangci-lint run --help` - exclude: - - "should have a package comment" - - 'declaration of "(err|ctx)" shadows declaration at' - - "`Println` arg list ends with redundant newline" - - "Println arg list ends with redundant newline" - - "type jsonschema.Schema has no field or method ValidateBytes" - - "declarations should never be cuddled" - - "unnamedResult: consider giving a name to these results" - - # Excluding configuration per-path, per-linter, per-text and per-source + exclude-case-sensitive: false + exclude-use-default: false + fix: false + max-issues-per-linter: 0 + max-same-issues: 0 + new: false + exclude-rules: - # Exclude some linters from running on tests files. - linters: - lll - source: "lint:ignore-length" + source: lint:ignore_length - linters: - gosec - source: "lint:allow_666" + source: lint:allow_666 - linters: - gosec - source: "lint:allow_possible_insecure" + source: lint:allow_possible_insecure - linters: - unparam - source: "lint:allow_param" - - - linters: - - deadcode - - unused - source: "lint:allow_dead" + source: lint:allow_param - linters: - gomnd - source: "lint:allow_raw_number" + source: lint:allow_raw_number - - linters: + - text: 'commentedOutCode: may want to remove commented-out code' + linters: - gocritic - source: "lint:ignore_criticism" + source: lint:allow_commented - linters: - nestif - source: "lint:allow_nesting" + source: lint:allow_nesting - linters: - dupl - source: "lint:no_dupe" - - - linters: - - wsl - source: "lint:allow_cuddling" + source: lint:no_dupe - linters: - goerr113 - source: "lint:allow_errorf" + source: lint:allow_errorf - linters: - wrapcheck - source: "lint:allow_unwrapped_errors" + source: lint:allow_unwrapped_errors - - text: "(SA1019|G402)" + - text: (SA1019|G402) linters: - staticcheck - gosec - source: "lint:allow_tls_min_version" + source: lint:allow_tls_min_version - - text: "(G404)" + - text: (returns interface) + linters: + - ireturn + source: lint:allow_return_interface + + - text: (G101) linters: - gosec - source: "lint:not_crypto" + source: lint:not_a_secret + + - text: (G104) + linters: + - gosec + source: lint:allow_unhandled + + - text: (G404) + linters: + - gosec + source: lint:not_crypto + + - text: (error-strings) + linters: + - revive + source: lint:allow_human_errors - - text: "(hugeParam)" + - text: (hugeParam) linters: - gocritic - source: "lint:allow_large_memory" + source: lint:allow_large_memory - # Independently from option `exclude` we use default exclude patterns, - # it can be disabled by this option. To list all - # excluded by default patterns execute `golangci-lint run --help`. - # Default value for this option is true. - exclude-use-default: false + - linters: + - tagliatelle + - gofumpt + source: lint:allow_format - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. - max-issues-per-linter: 0 + - text: (is unused) + linters: + - unused + source: lint:allow_unused - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. - max-same-issues: 0 + - text: (is a program, not an importable package) + linters: + - typecheck + source: lint:allow_importable_program - # Show only new issues: if there are unstaged changes or untracked files, - # only those changes are analyzed, else only changes in HEAD~ are analyzed. - # It's a super-useful option for integration of golangci-lint into existing - # large codebase. It's not practical to fix all existing issues at the moment - # of integration: much better don't allow issues in new code. - # Default is false. - new: false - # Show only new issues created after git revision `REV` - # new-from-rev: REV - # Show only new issues created in git patch with set file path. - # new-from-patch: path/to/patch/file + - text: (don't use `init` function) + linters: + - gochecknoinits + source: lint:allow_init + + - text: (cuddle) + linters: + - wsl + source: lint:allow_cuddle + + - text: (ST1000) + linters: + - stylecheck + source: lint:allow_no_pkg_comment + + - text: (cognitive complexity) + linters: + - gocognit + source: lint:allow_complexity + + - text: (make it a constant) + linters: + - goconst + source: lint:no_const +# goplicate-end:issues diff --git a/.gommit.toml b/.gommit.toml new file mode 100644 index 0000000..d57b1c3 --- /dev/null +++ b/.gommit.toml @@ -0,0 +1,19 @@ +[config] +exclude-merge-commits = true +check-summary-length = true +summary-length = 80 + +[matchers] +all = "(?:build|ci|deps|docs|feat|fix|lint|perf|refactor|relprep|style|test)(?:\\([^\\)]*\\))?: (?:.+)" + +[examples] +a_simple_commit = """ +[build|ci|deps|docs|feat|fix|lint|perf|refactor|relprep|style|test](module): A commit message +""" +an_extended_commit = """ +[build|ci|deps|docs|feat|fix|lint|perf|refactor|relprep|style|test](module): A commit message + +* first line +* second line +* and so on... +""" diff --git a/.goplicate.yaml b/.goplicate.yaml new file mode 100644 index 0000000..525c07d --- /dev/null +++ b/.goplicate.yaml @@ -0,0 +1,77 @@ +--- +# A .goplicate.yaml configuration file that tells goplicate +# which "target" files to sync, where to take the "source" +# configurations from, and how to fill parameter values. + +sync-config: + path: .goplicate.yaml + source: + repository: /tmp/terraform-makefile + path: updates/.goplicate.yaml + +targets: + # goplicate-start:file + - path: .github/dependabot.yml + source: + repository: /tmp/terraform-makefile + path: updates/.github/dependabot.yml + sync-initial: true + + - path: .gitignore + source: + repository: /tmp/terraform-makefile + path: updates/.gitignore + sync-initial: true + + - path: .pre-commit-config.yaml + source: + repository: /tmp/terraform-makefile + path: updates/.pre-commit-config.yaml + sync-initial: true + + - path: .vscode/extensions.json + source: + repository: /tmp/terraform-makefile + path: updates/.vscode/extensions.tmpl.jsonc + sync-initial: true + + - path: .vscode/settings.json + source: + repository: /tmp/terraform-makefile + path: updates/.vscode/settings.tmpl.jsonc + sync-initial: true + + - path: cliff.toml + source: + repository: /tmp/terraform-makefile + path: updates/cliff.tmpl.toml + sync-initial: true + + - path: ecrc.toml + source: + repository: /tmp/terraform-makefile + path: updates/ecrc.toml + sync-initial: true + + - path: SECURITY.md + source: + repository: /tmp/terraform-makefile + path: updates/SECURITY.md + sync-initial: true + # goplicate-end:file + + # goplicate-start:go + - path: .golangci.yml + source: + repository: /tmp/terraform-makefile + path: updates/go/.golangci.yml + sync-initial: false + # goplicate-end:go + + # goplicate-end:tf + - path: versions.tf + source: + repository: /tmp/terraform-makefile + path: updates/tf/versions.tf + sync-initial: false + # goplicate-end:tf diff --git a/.licensei.toml b/.licensei.toml new file mode 100644 index 0000000..5aa9c57 --- /dev/null +++ b/.licensei.toml @@ -0,0 +1,15 @@ +[header] +template = """// Copyright 2023-2024, Northwood Labs +// Copyright 2023-2024, Ryan Parman +// +// Licensed under the Apache License, Version 2.0 (the \"License\"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an \"AS IS\" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.""" diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..4012d4c --- /dev/null +++ b/.mailmap @@ -0,0 +1,25 @@ +# This file allows mapping several author and committer email addresses and +# names to a single canonical one for `git shortlog`, `git log --author`, +# or `git check-mailmap`. +# +# For example, if you commit as `random.person@example.com` but sometimes use +# "Rañdom Person" and sometimes "Random Person" as name and you want the former +# to be your canonical name, add +# +# Rañdom Person +# +# If you commit as both `random.person@example.com` and `ranp@example.com` and +# you want the former to be your canonical email address, add +# +# +# +# Combinations of both are possible too, see +# https://git-scm.com/docs/gitmailmap for format details. +# +# You can commit changes for your own names and email addresses without review. +# If you want to add entries for other people, please have them review the +# addition. +# +# Please keep this file sorted. + +Ryan Parman diff --git a/.markdownlint.base.jsonc b/.markdownlint.base.jsonc new file mode 100644 index 0000000..5da4a46 --- /dev/null +++ b/.markdownlint.base.jsonc @@ -0,0 +1,256 @@ +// Original: +// https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.jsonc +// +// Definitions: +// https://github.com/DavidAnson/markdownlint/tree/main/doc +{ + // Default state for all rules + "default": true, + + // Path to configuration file to extend + "extends": null, + + // MD001 + "heading-increment": true, + + // MD003 + "heading-style": { + "style": "atx" + }, + + // MD004 + "ul-style": { + "style": "asterisk" + }, + + // MD005 + "list-indent": true, + + // MD007 + "ul-indent": { + "indent": 2, + "start_indented": false + }, + + // MD009 + "no-trailing-spaces": { + "br_spaces": 2, + "list_item_empty_lines": false + }, + + // MD010 + "no-hard-tabs": { + "code_blocks": true, + "ignore_code_languages": [], + "spaces_per_tab": 4 + }, + + // MD011 + "no-reversed-links": true, + + // MD012 + "no-multiple-blanks": { + "maximum": 1 + }, + + // MD013 + "line-length": { + "line_length": 10000, + "code_block_line_length": 120, + "code_blocks": false, + "heading_line_length": 80, + "headings": true, + "stern": false, + "strict": false, + "tables": false + }, + + // MD014 + "commands-show-output": false, + + // MD018 + "no-missing-space-atx": true, + + // MD019 + "no-multiple-space-atx": true, + + // MD020 + "no-missing-space-closed-atx": true, + + // MD021 + "no-multiple-space-closed-atx": true, + + // MD022 + "blanks-around-headings": { + "lines_above": 1, + "lines_below": 1 + }, + + // MD023 + "heading-start-left": true, + + // MD024 + "no-duplicate-heading": { + "siblings_only": true + }, + + // MD025 + "single-h1": { + "level": 1 + }, + + // MD026 + "no-trailing-punctuation": { + "punctuation": ".,;:" + }, + + // MD027 + "no-multiple-space-blockquote": true, + + // MD028 + "no-blanks-blockquote": true, + + // MD029 + "ol-prefix": { + "style": "one" + }, + + // MD030 + "list-marker-space": { + "ul_single": 1, + "ol_single": 1, + "ul_multi": 1, + "ol_multi": 1 + }, + + // MD031 + "blanks-around-fences": { + "list_items": true + }, + + // MD032 + "blanks-around-lists": true, + + // MD033 + "no-inline-html": { + "allowed_elements": [ + "a", + "b", + "br", + "code", + "details", + "div", + "img", + "li", + "nobr", + "p", + "pre", + "summary", + "ul" + ] + }, + + // MD034 + "no-bare-urls": true, + + // MD035 + "hr-style": { + "style": "consistent" + }, + + // MD036 + "no-emphasis-as-heading": true, + + // MD037 + "no-space-in-emphasis": false, + + // MD038 + "no-space-in-code": false, + + // MD039 + "no-space-in-links": true, + + // MD040 + "fenced-code-language": { + "allowed_languages": [], + "language_only": true + }, + + // MD041 + "first-line-h1": false, + + // MD042 + "no-empty-links": true, + + // MD043 + "required-headings": { + // "headings": [], + "match_case": true + }, + + // MD044 + "proper-names": { + "names": [], + "code_blocks": false, + "html_elements": false + }, + + // MD045 + "no-alt-text": true, + + // MD046 + "code-block-style": { + "style": "fenced" + }, + + // MD047 + "single-trailing-newline": true, + + // MD048 + "code-fence-style": { + "style": "backtick" + }, + + // MD049 + "emphasis-style": { + "style": "underscore" + }, + + // MD050 + "strong-style": { + "style": "asterisk" + }, + + // MD051 + "link-fragments": true, + + // MD052 + "reference-links-images": { + "shortcut_syntax": false + }, + + // MD053 + "link-image-reference-definitions": { + "ignored_definitions": [ + "//" + ] + }, + + // MD054 + "link-image-style": { + "autolink": true, + "inline": true, + "full": true, + "collapsed": true, + "shortcut": true, + "url_inline": true + }, + + // MD055 + "table-pipe-style": { + "style": "leading_and_trailing" + }, + + // MD056 + "table-column-count": true +} diff --git a/.markdownlint.json b/.markdownlint.json deleted file mode 100644 index 2e69fc7..0000000 --- a/.markdownlint.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "heading-increment": false, - "first-header-h1": true, - "header-style": { - "style": "atx" - }, - "ul-style": { - "style": "asterisk" - }, - "list-indent": true, - "ul-start-left": true, - "ul-indent": { - "indent": 4 - }, - "no-trailing-spaces": { - "br_spaces": 2, - "list_item_empty_lines": false - }, - "single-trailing-newline": true, - "no-hard-tabs": { - "code_blocks": false - }, - "no-reversed-links": true, - "no-multiple-blanks": { - "maximum": 1 - }, - "line-length": { - "line_length": 10000, - "code_blocks": false, - "tables": false - }, - "commands-show-output": false, - "no-missing-space-atx": true, - "no-multiple-space-atx": true, - "blanks-around-headers": { - "lines_above": 1, - "lines_below": 1 - }, - "header-start-left": true, - "no-duplicate-header": { - "allow_different_nesting": true - }, - "single-h1": { - "level": 1 - }, - "no-trailing-punctuation": { - "punctuation": ".,;:" - }, - "no-multiple-space-blockquote": true, - "no-blanks-blockquote": true, - "ol-prefix": { - "style": "one" - }, - "list-marker-space": { - "ul_single": 1, - "ol_single": 1, - "ul_multi": 1, - "ol_multi": 1 - }, - "blanks-around-fences": { - "list_items": true - }, - "blanks-around-lists": true, - "no-inline-html": { - "allowed_elements": [ - "a", - "b", - "br", - "code", - "details", - "div", - "img", - "li", - "nobr", - "p", - "pre", - "summary", - "ul" - ] - }, - "no-bare-urls": true, - "hr-style": { - "style": "consistent" - }, - "no-emphasis-as-header": true, - "no-space-in-emphasis": false, - "no-space-in-code": false, - "no-space-in-links": true, - "fenced-code-language": true, - "code-block-style": { - "style": "fenced" - }, - "first-line-h1": false, - "no-empty-links": true, - "proper-names": { - "names": [ - "aws-vault", - "Bash", - "Git", - "GitHub", - "Golang", - "JavaScript", - "macOS" - ], - "code_blocks": false - } -} diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 0000000..41b412f --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,15 @@ +// This is the file that is read by markdownlint-cli. +// This is our editable copy, which overrides the base copy. +{ + // This is the base copy. Any changes to this file will be overwritten. + "extends": ".markdownlint.base.jsonc", + + // MD044 + "proper-names": { + + // Add strings to this array for words that should be spelled a particular way. + "names": [ + "Northwood Labs" + ] + } +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b305719 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,178 @@ +--- +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +default_language_version: + python: python3.11 + +default_stages: + - commit + - push + +fail_fast: false + +repos: + # ---------------------------------------------------------------------------- + # goplicate-start:always + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-added-large-files + args: + - --maxkb=500 + - --enforce-all + - id: check-case-conflict + - id: check-merge-conflict + - id: check-toml + - id: check-xml + - id: check-yaml + args: + - --allow-multiple-documents + - id: destroyed-symlinks + - id: detect-private-key + - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: mixed-line-ending + args: + - --fix=lf + - id: trailing-whitespace + + - repo: https://github.com/skyzyx/git-hooks + rev: 4a2f0dc93e5c5353ed5e619599b0d15e34df88db + hooks: + - id: git-check + + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.39.0 + hooks: + - id: markdownlint + args: + - --ignore=node_modules + - --ignore=.github + - --ignore=.templates + - --fix + - '**/*.md' + + - repo: local + hooks: + - id: editorconfig-checker + name: editorconfig-checker + description: Double-check editorconfig compliance + entry: bash -c 'editorconfig-checker' + language: system + stages: [commit, push] + + - id: trufflehog + name: TruffleHog + description: Detect secrets in your data. + entry: bash -c 'trufflehog git file://. --since-commit HEAD --only-verified --fail --json 2>/dev/null | jq "."' + language: system + stages: [commit, push] + + - id: trivy-vuln + name: Trivy (Vulnerabilities) + description: Check for security vulnerabilities. (https://trivy.dev) + entry: bash -c 'trivy fs --config trivy-vuln.yaml .' + language: system + stages: [commit, push] + # goplicate-end:always + + # ---------------------------------------------------------------------------- + # goplicate-start:shell + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-executables-have-shebangs + + - repo: https://github.com/skyzyx/git-hooks + rev: 4a2f0dc93e5c5353ed5e619599b0d15e34df88db + hooks: + - id: script-must-have-extension + - id: shellcheck + - id: shfmt + args: + - --simplify + - --write + - --language-dialect=auto + - --indent=4 + - --case-indent + - --space-redirects + # goplicate-end:shell + + # ---------------------------------------------------------------------------- + # goplicate-start:python + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: fix-encoding-pragma + args: + - --remove + - id: requirements-txt-fixer + + - repo: https://github.com/asottile/pyupgrade + rev: c21b4c4d153be0633357686c7697f539ac194868 + hooks: + - id: pyupgrade + args: + - --py311-plus + + - repo: https://github.com/asottile/reorder_python_imports + rev: c4fe43d9809f1507508b3aba24ad1a72b5407f58 + hooks: + - id: reorder-python-imports + args: + - --py311-plus + # goplicate-end:python + + # ---------------------------------------------------------------------------- + # goplicate-start:golang + - repo: https://github.com/skyzyx/git-hooks + rev: 4a2f0dc93e5c5353ed5e619599b0d15e34df88db + hooks: + - id: gofumpt + - id: golangci-lint + + - repo: local + hooks: + - id: go-consistent + name: 'Go: Consistent Patterns' + description: Analyzes Go packages to identify unnecessary type conversions. + entry: bash -c 'go-consistent ./...' + language: system + stages: [commit, push] + + - id: unconvert + name: 'Go: unconvert (current GOOS/GOARCH)' + description: Analyzes Go packages to identify unnecessary type conversions. + entry: bash -c 'unconvert -fastmath -tests -v ./...' + language: system + stages: [commit, push] + + - id: smrcptr + name: 'Go: Same Receiver Pointer' + description: Don't mix receiver types. Choose either pointers or struct types for all available methods. + entry: bash -c 'smrcptr -skip-std=true --constructor=true ./...' + language: system + stages: [commit, push] + + - id: govulncheck + name: 'Go: Vulnerability check' + description: Check for Go security vulnerabilities. (https://go.dev/blog/vuln) + entry: bash -c 'govulncheck -test ./...' + language: system + stages: [commit, push] + + - id: osvscanner + name: OSV Scanner + description: Check for security vulnerabilities. (https://osv.dev) + entry: bash -c 'osv-scanner -r .' + language: system + stages: [commit, push] + # goplicate-end:golang + + # ---------------------------------------------------------------------------- + # goplicate-start:terraform + - repo: https://github.com/skyzyx/git-hooks + rev: 4a2f0dc93e5c5353ed5e619599b0d15e34df88db + hooks: + - id: terraform-fmt + # goplicate-end:terraform diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 6535c4a..74dd1fc 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,53 +1,104 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - // List of extensions which should be recommended for users of this workspace. + /* + https://github.com/ilaif/goplicate + */ "recommendations": [ - "aleksandra.go-group-imports", - "amazonwebservices.aws-toolkit-vscode", + // goplicate-start:always + "albymor.increment-selection", "annsk.alignment", - "bierner.markdown-preview-github-styles", - "bin3377.iam-policy", + "arahata.linter-actionlint", + "chdsbd.github-code-owners", "christian-kohler.path-intellisense", - "cmacu.gotoanything", - "codezombiech.gitignore", - "darkriszty.markdown-table-prettify", - "davidanson.vscode-markdownlint", - "deerawan.vscode-dash", + "claui.email-addresses", + "Cmacu.gotoanything", + "DavidWang.ini-for-vscode", + "donjayamanne.git-extension-pack", "donjayamanne.githistory", + "dt.ghlink", "eamodio.gitlens", - "editorconfig.editorconfig", - "fcrespo82.markdown-table-formatter", - "foxundermoon.shell-format", - "golang.go", - "gruntfuggly.todo-tree", + "EditorConfig.EditorConfig", + "elagil.pre-commit-helper", + "emeraldwalk.RunOnSave", + "ExodiusStudios.comment-anchors", + "fnando.linter", + "GitHub.copilot", + "GitHub.remotehub", + "GitHub.vscode-codeql", + "github.vscode-github-actions", + "GitHub.vscode-pull-request-github", "gurumukhi.selected-lines-count", - "hashicorp.terraform", - "hcltechnologies.hclappscancodesweep", - "jfrog.jfrog-vscode-extension", - "lamartire.git-indicators", - "mads-hartmann.bash-ide-vscode", + "IBM.output-colorizer", + "kevinkyang.auto-comment-blocks", + "logerfo.json-trimmer", + "melt-inc.yamlfmt-vscode", + "mhutchie.git-graph", + "mkhl.direnv", "mohsen1.prettify-json", - "ms-python.python", - "ms-python.vscode-pylance", - "msyrus.go-doc", - "neverik.go-critic", + "ms-vscode-remote.remote-containers", + "ms-vscode-remote.vscode-remote-extensionpack", + "ms-vscode.test-adapter-converter", + "nhoizey.gremlins", + "oliversturm.fix-json", + "pflannery.vscode-versionlens", + "qezhu.gitlink", "quicktype.quicktype", - "rogalmic.bash-debug", - "sonarsource.sonarlint-vscode", - "steefh.terraform-documentation-links", + "redhat.vscode-yaml", + "sidneys1.gitconfig", "stkb.rewrap", - "timonwong.shellcheck", - "tyriar.sort-lines", + "tamasfe.even-better-toml", + "technosophos.vscode-make", + "Tyriar.sort-lines", "usernamehw.errorlens", - "visualstudioexptteam.vscodeintellicode", + "wmaurer.change-case", + "xshrim.txt-syntax", + "zardoy.fix-all-json", + "ziyasal.vscode-open-in-github", + // goplicate-end:always + // + // goplicate-start:aws + "bin3377.iam-policy", + // goplicate-end:aws + // + // goplicate-start:golang + "akshayn.GoGet", + "golang.go", + "MaxMedia.go-prof", + "msyrus.go-doc", + "premparihar.gotestexplorer", + "windmilleng.vscode-go-autotest", + // goplicate-end:golang + // + // goplicate-start:markdown + "arr.marksman", + "bierner.markdown-checkbox", + "bierner.markdown-preview-github-styles", + "bierner.markdown-yaml-preamble", + "DavidAnson.vscode-markdownlint", + "fcrespo82.markdown-table-formatter", "yzhang.markdown-all-in-one", + // goplicate-end:markdown + // + // goplicate-start:security + "1Password.op-vscode", + "anchoreinc.grype-vscode", + "AquaSecurityOfficial.trivy-vulnerability-scanner", + "jflbr.jwt-decoder", + "MS-SarifVSCode.sarif-viewer", + "redhat.fabric8-analytics", + "snyk-security.snyk-vulnerability-scanner", + // goplicate-end:security + // + // goplicate-start:shell + "foxundermoon.shell-format", + "jetmartin.bats", + "mads-hartmann.bash-ide-vscode", + "Remisa.shellman", + "rogalmic.bash-debug", + // goplicate-end:shell ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [ - "googlecloudtools.cloudcode", - "mindaro.mindaro", "ms-azuretools.vscode-azureterraform", - "ms-kubernetes-tools.vscode-kubernetes-tools", + "GoogleCloudTools.cloudcode", + "vscodevim.vim", ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index a2b6f69..50301cb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,14 @@ { + // goplicate-start:always + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + "[jsonc]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + "[yaml]": { + "editor.defaultFormatter": "redhat.vscode-yaml" + }, "alignment.chars": { ":": { "spaceBefore": 0, @@ -41,23 +51,352 @@ "spaceAfter": 1 } }, - "aws.profile": "add", - "diffEditor.codeLens": true, - "files.autoSave": "onWindowChange", - "files.defaultLanguage": "${activeEditorLanguage}", + "diffEditor.hideUnchangedRegions.enabled": true, + "editor.experimental.asyncTokenization": true, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "editor.inlineSuggest.enabled": true, + "editor.linkedEditing": true, + "editor.quickSuggestions": { + "comments": "on", + "strings": "on", + "other": "on" + }, + "editor.suggest.showMethods": true, + "editor.tabSize": 4, + "editor.trimAutoWhitespace": true, + "editor.wordSeparators": "./\\()\"'-:,.;<>~!@#%^&*|+=[]{}`~?", + "editor.wordWrap": "off", + "editor.suggest.preview": true, + "editorconfig.generateAuto": true, + "editorconfig.template": "default", + "errorLens.messageTemplate": "[$severity] $message [$source $code]", + "evenBetterToml.completion.maxKeys": 5, + "evenBetterToml.formatter.alignComments": true, + "evenBetterToml.formatter.alignEntries": true, + "evenBetterToml.formatter.allowedBlankLines": 1, + "evenBetterToml.formatter.arrayAutoCollapse": false, + "evenBetterToml.formatter.arrayAutoExpand": true, + "evenBetterToml.formatter.arrayTrailingComma": true, + "evenBetterToml.formatter.columnWidth": 120, + "evenBetterToml.formatter.compactArrays": false, + "evenBetterToml.formatter.compactEntries": false, + "evenBetterToml.formatter.crlf": false, + "evenBetterToml.formatter.indentEntries": true, + "evenBetterToml.formatter.indentString": " ", + "evenBetterToml.formatter.indentTables": true, + "evenBetterToml.formatter.inlineTableExpand": true, + "evenBetterToml.formatter.reorderKeys": true, + "evenBetterToml.formatter.trailingNewline": true, + "evenBetterToml.schema.associations": { + "^(.*(/|\\\\)\\.?taplo\\.toml|\\.?taplo\\.toml)$": "taplo://taplo.toml" + }, + "evenBetterToml.schema.enabled": true, + "evenBetterToml.schema.links": true, + "evenBetterToml.semanticTokens": true, + "evenBetterToml.syntax.semanticTokens": true, + "evenBetterToml.taplo.bundled": true, + "evenBetterToml.taplo.configFile.enabled": true, + "files.associations": { + ".htmlnanorc": "json", + ".parcelrc": "json", + ".postcssrc": "json", + ".posthtmlrc": "json", + ".style.yapf": "ini", + ".terraformrc": "hcl", + "*.hcl": "terraform", + "*.ini": "ini", + "*.md": "markdown", + "*.sh": "shellscript", + "*.tf": "terraform", + "*.toml": "toml", + "*.xml": "xml", + "config": "ini", + "Pipfile": "toml" + }, + "files.autoSave": "onFocusChange", "files.eol": "\n", "files.insertFinalNewline": true, "files.trimFinalNewlines": true, "files.trimTrailingWhitespace": true, - "problems.showCurrentInStatus": true, - "python.formatting.provider": "yapf", - "restructuredtext.languageServer.disabled": true, - "search.showLineNumbers": true, - "search.useGlobalIgnoreFiles": true, - "[terraform]": { - "editor.formatOnSave": true - }, - "terraform-ls.experimentalFeatures": { - "validateOnSave": true - } + "formate.additionalSpaces": 0, + "formate.alignColon": false, + "formate.enable": true, + "formate.verticalAlignProperties": false, + "git.alwaysSignOff": true, + "git.autofetch": true, + "git.autoStash": true, + "git.enableSmartCommit": true, + "git.openRepositoryInParentFolders": "always", + "gitHistory.alwaysPromptRepositoryPicker": false, + "gitHistory.avatarCacheExpiration": 60, + "gitHistory.editorTitleButtonOpenRepo": false, + "gitHistory.hideCommitViewExplorer": false, + "gitHistory.includeRemoteBranches": true, + "gitHistory.logLevel": "Info", + "gitHistory.pageSize": 100, + "gitHistory.showEditorTitleMenuBarIcons": true, + "gitHistory.showFileHistorySplit": true, + "gitHistory.sourceCodeProviderIntegrationLocation": "Inline", + "github.copilot.editor.enableAutoCompletions": true, + "github.copilot.enable": { + "*": true, + "go.mod": false, + "markdown": false, + "plaintext": false, + "yaml": false + }, + "github-actions.use-enterprise": false, + "github-actions.workflows.pinned.refresh.enabled": false, + "github-actions.workflows.pinned.refresh.interval": 30, + "github-code-owners.format.alignment-offset": 4, + "github-code-owners.format.enabled": true, + "gitlens.fileAnnotations.command": "blame", + "gitlens.advanced.abbreviatedShaLength": 8, + "gitlens.ai.experimental.generateCommitMessage.enabled": false, + "gitlens.blame.ignoreWhitespace": true, + "gitlens.cloudPatches.enabled": false, + "gitlens.codeLens.enabled": true, + "gitlens.detectNestedRepositories": true, + "gitlens.focus.allowMultiple": true, + "gitlens.gitCommands.avatars": true, + "gitlens.hovers.avatarSize": 64, + "gitlens.rebaseEditor.ordering": "desc", + "gitlens.rebaseEditor.showDetailsView": "selection", + "gitlens.telemetry.enabled": true, + "gitlens.terminal.overrideGitEditor": true, + "gitlens.terminalLinks.enabled": true, + "gitlens.terminalLinks.showDetailsView": true, + "gitlens.views.lineHistory.avatars": true, + "GitLink.defaultRemote": "origin", + "GitLink.hostType": "github", + "json.schemas": [ + { + "fileMatch": [ + ".prettierrc", + "prettier.config.js" + ], + "url": "http://json.schemastore.org/prettierrc" + }, + { + "fileMatch": [ + ".markdownlint.*", + ], + "url": "https://github.com/DavidAnson/markdownlint/raw/main/schema/markdownlint-config-schema.json" + }, + ], + "linter.cache": false, + "linter.debug": false, + "linter.delay": 300, + "linter.enabled": true, + "linter.runOnTextChange": true, + "linter-actionlint.config": { + "capabilities": [], + "command": [], + "configFiles": [ + "actionlint.yaml", + "actionlint.yml" + ], + "enabled": true, + "languages": [ + "yaml" + ], + "name": "actionlint", + "url": "https://github.com/rhysd/actionlint" + }, + "merge-conflict.autoNavigateNextConflict.enabled": true, + "openInGitHub.defaultPullRequestBranch": "main", + "openInGitHub.gitHubDomain": "github.com", + "openInGitHub.providerProtocol": "https", + "openInGitHub.providerType": "github", + "openInGitHub.requireSelectionForLines": true, + "openInGitHub.useCommitSHAInURL": true, + "path-intellisense.absolutePathToWorkspace": false, + "path-intellisense.autoSlashAfterDirectory": true, + "path-intellisense.autoTriggerNextSuggestion": true, + "path-intellisense.extensionOnImport": true, + "path-intellisense.ignoreTsConfigBaseUrl": false, + "path-intellisense.showHiddenFiles": false, + "path-intellisense.showOnAbsoluteSlash": true, + "redhat.telemetry.enabled": false, + "rewrap.autoWrap.enabled": false, + "scm.alwaysShowActions": true, + "scm.alwaysShowRepositories": true, + "sortLines.filterBlankLines": true, + "testExplorer.addToEditorContextMenu": true, + "testExplorer.codeLens": true, + "testExplorer.errorDecoration": true, + "testExplorer.errorDecorationHover": true, + "testExplorer.gutterDecoration": true, + "testExplorer.hideEmptyLog": true, + "testExplorer.hideWhen": "noTests", + "testExplorer.showCollapseButton": true, + "testExplorer.showOnRun": true, + "testExplorer.sort": "byLabel", + "testExplorer.useNativeTesting": true, + "testing.showCoverageInExplorer": true, + "todo-tree.general.tags": [ + "BUG", + "HACK", + "FIXME", + "TODO", + "XXX", + "[ ]", + "[x]" + ], + "todo-tree.regex.regex": "(//|#| + + +## Reporting a Vulnerability + +If you believe you have found a legitimate security vulnerability, please [report it](./security/advisories/new). + +There is no bounty program, and there are no payments for discovering/reporting security vulnerabilities, but we **all** benefit from software that is more secure. Happy to provide public thanks once the issue has been resolved. + +What I need is: + +* An explanation of the bug. +* A minimum viable reproduction case which triggers the issue. +* What you expected to happen. +* What actually happened. +* [OPTIONAL] A suggested patch attached as a .diff file, if you have one. + +I don't check my email every day, and I get LOTS of email. It may take me up to a week to discover your message. I will respond as soon as I see your message and confirm that I can reproduce the issue. + +Thank you for participating in the _responsible disclosure_ of security vulnerabilities. + diff --git a/VERSION b/VERSION deleted file mode 100644 index afaf360..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.0.0 \ No newline at end of file diff --git a/__first_time.sh b/__first_time.sh new file mode 100755 index 0000000..b7b0c41 --- /dev/null +++ b/__first_time.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash + +# Remove on the runner. +RUNNER_TEMP="/tmp/terraform-makefile" + +# Clone repo into TMP directory. +rm -Rf "${RUNNER_TEMP}" +git clone \ + --depth 1 \ + --branch main \ + --single-branch \ + https://github.com/northwood-labs/.github.git \ + "${RUNNER_TEMP}" \ + ; + +# Copy all "full-copy" files from the root into the repository. +find "${RUNNER_TEMP}/full-copy/" -maxdepth 1 -type f -print0 | + xargs -0 -I% cp -Rfv "%" "${PWD}" || + true + +# Folders to copy +FOLDERS=( + ".githooks" + ".github" + "scripts" +) + +for FOLDER in "${FOLDERS[@]}"; do + # Copy all files from this directory into the root of the repository. + mkdir -p "${PWD}/${FOLDER}" + find "${RUNNER_TEMP}/full-copy/${FOLDER}/" -maxdepth 1 -type f -print0 | + xargs -0 -I% cp -Rfv "%" "${PWD}/${FOLDER}" || + true +done + +TYPES=() + +# Pass GO=true when calling the script. +# shellcheck disable=2154 +if [[ "${GO}" == "true" ]]; then + TYPES+=("go") +fi + +# Pass TF=true when calling the script. +# shellcheck disable=2154 +if [[ "${TF}" == "true" ]]; then + TYPES+=("tf") +fi + +for TYPE in "${TYPES[@]}"; do + # Copy all files from this directory into the root of the repository. + mkdir -p "${PWD}" + find "${RUNNER_TEMP}/full-copy/${TYPE}/" -maxdepth 1 -type f -not \( -name "*tmpl*" \) -print0 | + xargs -0 -I% cp -Rfv "%" "${PWD}" || + true +done + +# Copy all "updates" files from the root into the repository. +find "${RUNNER_TEMP}/updates/" -maxdepth 1 -type f -not \( -name "*tmpl*" \) -print0 | + xargs -0 -I% cp -Rfv "%" "${PWD}" || + true + +# Folders to copy +FOLDERS=( + ".github" + ".vscode" +) + +for FOLDER in "${FOLDERS[@]}"; do + # Copy all files from this directory into the root of the repository. + mkdir -p "${PWD}/${FOLDER}" + find "${RUNNER_TEMP}/updates/${FOLDER}/" -maxdepth 1 -type f -not \( -name "*tmpl*" \) -print0 | + xargs -0 -I% cp -Rfv "%" "${PWD}/${FOLDER}" || + true +done + +TYPES=() + +# Pass GO=true when calling the script. +# shellcheck disable=2154 +if [[ "${GO}" == "true" ]]; then + TYPES+=("go") +fi + +# Pass TF=true when calling the script. +# shellcheck disable=2154 +if [[ "${TF}" == "true" ]]; then + TYPES+=("tf") +fi + +for TYPE in "${TYPES[@]}"; do + # Copy all files from this directory into the root of the repository. + mkdir -p "${PWD}" + find "${RUNNER_TEMP}/updates/${TYPE}/" -maxdepth 1 -type f -not \( -name "*tmpl*" \) -print0 | + xargs -0 -I% cp -Rfv "%" "${PWD}" || + true +done + +# Run Goplicate +goplicate run --allow-dirty --confirm --stash-changes + +# Generate .ecrc +tomljson ecrc.toml >.ecrc + +# Make shell scripts executable +find "${PWD}" -type f -name "*.sh" -print0 | + xargs -0 -I% chmod +x "%" || + true diff --git a/__update.sh b/__update.sh new file mode 100755 index 0000000..47d7bd8 --- /dev/null +++ b/__update.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# Remove on the runner. +RUNNER_TEMP="/tmp/terraform-makefile" + +# Clone repo into TMP directory. +rm -Rf "${RUNNER_TEMP}" +git clone \ + --depth 1 \ + --branch main \ + --single-branch \ + https://github.com/northwood-labs/.github.git \ + "${RUNNER_TEMP}" \ + ; + +# Copy all "full-copy" files from the root into the repository. +FILES="$(find "${RUNNER_TEMP}/full-copy/" -maxdepth 1 -type f)" + +# Files that should only be copied the first time. Do not overwrite on +# subsequent copies. +ONE_TIME_ONLY=( + ".markdownlint.jsonc" +) + +# shellcheck disable=2068 +for FILE in ${FILES[@]}; do + for IGNORE in "${ONE_TIME_ONLY[@]}"; do + # If the file does not exist, go ahead and copy it (first time) + if [[ ! -f "${PWD}/${IGNORE}" ]]; then + cp -Rfv "${FILE}" "${PWD}" + + # Otherwise, as long as the copied file is not the ignored file, go + # ahead and copy it (no restricton) + elif [[ "${FILE}" != "${RUNNER_TEMP}/full-copy/${IGNORE}" ]]; then + cp -Rfv "${FILE}" "${PWD}" + fi + done +done + +# Folders to copy +FOLDERS=( + ".githooks" + ".github" + "scripts" +) + +for FOLDER in "${FOLDERS[@]}"; do + # Copy all files from this directory into the root of the repository. + mkdir -p "${PWD}/${FOLDER}" + find "${RUNNER_TEMP}/full-copy/${FOLDER}/" -maxdepth 1 -type f -print0 | + xargs -0 -I% cp -Rfv "%" "${PWD}/${FOLDER}" || + true +done + +TYPES=() + +# Pass GO=true when calling the script. +# shellcheck disable=2154 +if [[ "${GO}" == "true" ]]; then + TYPES+=("go") +fi + +# Pass TF=true when calling the script. +# shellcheck disable=2154 +if [[ "${TF}" == "true" ]]; then + TYPES+=("tf") +fi + +for TYPE in "${TYPES[@]}"; do + # Copy all files from this directory into the root of the repository. + mkdir -p "${PWD}" + find "${RUNNER_TEMP}/full-copy/${TYPE}/" -maxdepth 1 -type f -print0 | + xargs -0 -I% cp -Rfv "%" "${PWD}" || + true +done + +# Run Goplicate +goplicate run --allow-dirty --confirm --stash-changes + +# Generate .ecrc +tomljson ecrc.toml >.ecrc + +# Make shell scripts executable +find "${PWD}" -type f -name "*.sh" -print0 | + xargs -0 -I% chmod +x "%" || + true diff --git a/aws/ec2_instances.go b/aws/ec2_instances.go new file mode 100644 index 0000000..c569cd3 --- /dev/null +++ b/aws/ec2_instances.go @@ -0,0 +1,124 @@ +package aws + +import ( + "context" + "fmt" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/northwood-labs/awsutils" + "github.com/northwood-labs/golang-utils/exiterrorf" +) + +type ( + // Ec2Instance represents a list of EC2 instances by name tag and instance ID. + Ec2Instance struct { + LaunchTime time.Time + ID string + Name string + Architecture string + Hypervisor string + ImageID string + InstanceType string + Platform string + EbsOptimized bool + EnaSupport bool + } + + // Tag represents a list of EC2 instance tags that we want to filter by. + Tag struct { + Name string + Equals string + Contains string + StartsWith string + } + + // Filter represents a list of EC2 instance filters that we want to apply. + Filter struct { + Name string + Equals string + } +) + +func GetEC2Instances() ([]Ec2Instance, error) { + ctx := context.Background() + retries := 5 + verbose := false + + config, err := awsutils.GetAWSConfig(ctx, "", "", retries, verbose) + if err != nil { + exiterrorf.ExitErrorf(err) + } + + var collectedInstances []Ec2Instance + + ec2Client := ec2.NewFromConfig(config) + + // Base filter + ffs := []types.Filter{ + { + // Only running instances... + Name: aws.String("instance-state-name"), + Values: []string{ + *aws.String("running"), + }, + }, + } + + response, err := ec2Client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ + Filters: ffs, + }) + if err != nil { + return []Ec2Instance{}, fmt.Errorf("error looking up instances from EC2 API: %w", err) + } + + for r := range response.Reservations { + reservation := &response.Reservations[r] + instances := reservation.Instances + + for i := range instances { + instance := &instances[i] + + // If the conditions exist, apply them. + name := findName(instance) + + collectedInstances = append(collectedInstances, Ec2Instance{ + ID: *instance.InstanceId, + Name: *name, + Architecture: string(instance.Architecture), + Hypervisor: string(instance.Hypervisor), + ImageID: *instance.ImageId, + InstanceType: string(instance.InstanceType), + Platform: func() string { + v := string(instance.Platform) + if v == "" { + return "linux" + } else { + return v + } + }(), + EbsOptimized: *instance.EbsOptimized, + EnaSupport: *instance.EnaSupport, + }) + } + } + + return collectedInstances, nil +} + +// Calling this is duplicate work. Refactor to collect this data in a single pass. +func findName(instance *types.Instance) *string { + emptyString := "" + + for t := range instance.Tags { + tag := instance.Tags[t] + + if *tag.Key == "Name" { + return tag.Value + } + } + + return &emptyString +} diff --git a/run_command.go b/aws/functions.go similarity index 67% rename from run_command.go rename to aws/functions.go index aafb6f3..b9c9761 100644 --- a/run_command.go +++ b/aws/functions.go @@ -1,4 +1,4 @@ -package main +package aws import ( "fmt" @@ -6,7 +6,13 @@ import ( "os/exec" ) -func runCommand(args []string) { +const ( + CondEquals = "==" + CondContains = "=~" + CondStartsWith = "=^" +) + +func RunCommand(args []string) { cmd := exec.Command(args[0], args[1:]...) // lint:allow_possible_insecure cmd.Stdin = os.Stdin diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..85958e1 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,136 @@ +# git-cliff ~ default configuration file +# https://git-cliff.org/docs/configuration +# +# Lines starting with "#" are comments. +# Configuration options are organized into tables and keys. +# See documentation for more information on available options. + +[remote.github] +owner = "northwood-labs" +repo = "ssm-shell" +# token = "" # Use GITHUB_TOKEN environment variable instead. + +# goplicate-start:changelog +[changelog] +header = """ +# CHANGELOG + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com), adheres to [Semantic Versioning](https://semver.org), and uses [Conventional Commit](https://www.conventionalcommits.org) syntax. +""" + +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{% if version %} + ## {{ version | trim_start_matches(pat="v") }} — {{ timestamp | date(format="%Y-%m-%d") }} + {% if previous.version %} + [Compare: {{ previous.version }} → {{ version }}]({{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}) + {% endif %}\ +{% else %} + ## Unreleased + {% if previous.version %} + [Compare: {{ previous.version }} → `HEAD`]({{ self::remote_url() }}/compare/{{ previous.version }}..HEAD) + {% endif %}\ +{% endif %}\ +{% for group, commits in commits | filter(attribute="merge_commit", value=false) | group_by(attribute="group") %} + ### {{ group | upper_first }} + {% for commit in commits %} + {% set commit_message = commit.message -%} + * {% if commit.breaking %}**[BC BREAK]** {% endif %}\ + [`{{ commit.id | truncate(length=7, end="") }}`]({{ self::remote_url() }}/commit/{{ commit.id }}): {% if commit.scope %}\ + **{{ commit.scope }}**: {% endif %}{{ commit_message | split(pat="\n") | first | upper_first | trim_end }} \ + ({% if commit.github.username %}[@{{ commit.github.username | replace(from="[bot]", to="") }}](https://github.com/{{ commit.github.username | replace(from="[bot]", to="") }}){%- endif -%})\ + {%- endfor %} +{% endfor %} +{%- macro remote_url() -%} + https://github.com/northwood-labs/terraform-provider-corefunc +{%- endmacro -%} +""" + +# remove the leading and trailing whitespace from the template +trim = true + +# changelog footer +footer = """ + +

Generated on {{ now() | date(format="%Y-%m-%d") }}.

+""" +# goplicate-end:changelog + +postprocessors = [ + { pattern = "([^ ]+)\\(\\)", replace = "`$0`" }, + { pattern = "AUTHORS|CONTRIBUTORS|CONTRIBUTING|README", replace = "$0.md" }, + { pattern = "([^ ]+)\\.md", replace = "`$0`" }, + { pattern = "([^ ]+)\\.ya?ml", replace = "`$0`" }, + { pattern = "\\.md\\.md", replace = ".md" }, + { pattern = "go\\.(mod|sum)", replace = "`$0`" }, + { pattern = "(?i)pkg\\.go\\.dev", replace = "`$0`" }, + { pattern = "Bump ([^ ]+)", replace = "Bump `$1`" }, + { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](@REPO/issues/${1}))" }, + { pattern = '@REPO', replace = "https://github.com/northwood-labs/ssm-shell" }, +] + +# goplicate-start:git +[git] + +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true + +# filter out the commits that are not conventional +filter_unconventional = true + +# process each line of a commit as an individual commit +split_commits = false + +# regex for preprocessing the commit messages +commit_preprocessors = [ + # { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, # replace issue numbers +] + +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^feat", group = ":rocket: Features" }, + { message = "^fix", group = ":bug: Bug Fixes" }, + { message = "^perf", group = ":racecar: Performance" }, + { message = "^docs", group = ":books: Documentation" }, + { message = "^refactor", group = ":tractor: Refactor" }, + { message = "^style", group = ":art: Styling" }, + { message = "^sync", group = ":arrows_counterclockwise: Configuration Syncing" }, + { message = "^build|deps", group = ":dependabot: Building and Dependencies" }, + { message = "^test", group = ":test_tube: Testing" }, + { message = "^lint", group = ":soap: Linting" }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore\\(deps\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^relprep", skip = true }, + { message = "^chore|ci", group = ":gear: Miscellaneous Tasks" }, + { message = "^security", group = ":closed_lock_with_key: Security" }, + { body = ".*security", group = ":closed_lock_with_key: Security" }, + { message = "^revert", group = ":x: Revert" }, + { message = "^automation", skip = true }, +] + +# protect breaking changes from being skipped due to matching a skipping commit_parser +protect_breaking_commits = false + +# filter out the commits that are not matched by commit parsers +filter_commits = false + +# regex for matching git tags +tag_pattern = "v[0-9].*" + +# regex for skipping tags +skip_tags = "beta|alpha" + +# regex for ignoring tags +ignore_tags = "rc" + +# sort the tags topologically +topo_order = true + +# sort the commits inside sections by oldest/newest order +sort_commits = "oldest" +# goplicate-end:git diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..79e6388 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,291 @@ +// Copyright 2023–2024, Northwood Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "os" + "sort" + "strings" + "time" + + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + + "github.com/charmbracelet/bubbles/table" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/huh/spinner" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/log" + "github.com/northwood-labs/ssm-shell/aws" + "github.com/spf13/cobra" +) + +const height = 20 + +var ( + instanceID string + instances []aws.Ec2Instance + + baseStyle = lipgloss.NewStyle(). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("240")) + + helpText = lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(lipgloss.Color("99")). + Padding(1, 2) // lint:allow_raw_number + + style = lipgloss.NewStyle(). + Bold(true). + Border(lipgloss.RoundedBorder()) + + logger = log.NewWithOptions(os.Stderr, log.Options{ + ReportTimestamp: true, + TimeFormat: time.Kitchen, + Prefix: "ssm-shell", + }) + + keys = keyMap{ + Up: key.NewBinding( + key.WithKeys("up", "k"), + key.WithHelp("↑/k", "move up"), + ), + Down: key.NewBinding( + key.WithKeys("down", "j"), + key.WithHelp("↓/j", "move down"), + ), + Help: key.NewBinding( + key.WithKeys("?"), + key.WithHelp("?", "toggle help"), + ), + Enter: key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "make selection"), + ), + Quit: key.NewBinding( + key.WithKeys("q", "esc", "ctrl+c"), + key.WithHelp("q/esc", "quit"), + ), + } + + // rootCmd represents the base command when called without any subcommands + rootCmd = &cobra.Command{ + Use: "ssm-shell", + Short: "Simplifies the process of connecting to EC2 Instances using AWS Session Manager.", + Long: helpText.Render(`ssm-shell + +Simplifies the process of connecting to EC2 Instances using AWS Session Manager. + +Disabling SSH and leveraging AWS Session Manager to connect to EC2 Instances is +the recommended approach for managing EC2 Instances. This approach is more +secure and does not require the need to manage SSH keys.`), + Run: func(cmd *cobra.Command, args []string) { + err := spinner.New(). + Title("Getting EC2 instances for this account..."). + Type(spinner.Dots). + Action(func(instances *[]aws.Ec2Instance) func() { + return func() { + insts, e := aws.GetEC2Instances() + if e != nil { + logger.Fatal(e) + } + + // Sort by text + sort.SliceStable(insts, func(i, j int) bool { + return strings.ToLower(insts[i].Name) < strings.ToLower(insts[j].Name) + }) + + *instances = insts + } + }(&instances)). + Run() + if err != nil { + logger.Fatal(err) + } + + columns := []table.Column{ + {Title: "Name", Width: 35}, // lint:allow_raw_number + {Title: "ID", Width: 20}, // lint:allow_raw_number + {Title: "CPU", Width: 6}, // lint:allow_raw_number + {Title: "Type", Width: 15}, // lint:allow_raw_number + {Title: "AMI", Width: 25}, // lint:allow_raw_number + {Title: "Platform", Width: 10}, // lint:allow_raw_number + } + + rows := []table.Row{} + + for i := range instances { + rows = append( + rows, + table.Row{ + instances[i].Name, + instances[i].ID, + instances[i].Architecture, + instances[i].InstanceType, + instances[i].ImageID, + instances[i].Platform, + }, + ) + } + + t := table.New( + table.WithColumns(columns), + table.WithRows(rows), + table.WithFocused(true), + table.WithHeight(height), + ) + + s := table.DefaultStyles() + s.Header = s.Header. + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("240")). + BorderBottom(true). + Bold(false) + s.Selected = s.Selected. + Foreground(lipgloss.Color("229")). + Background(lipgloss.Color("57")). + Bold(false) + t.SetStyles(s) + + m := model{ + table: t, + keys: keys, + help: help.New(), + } + if _, err := tea.NewProgram(m).Run(); err != nil { + fmt.Println("Error running program:", err) + os.Exit(1) + } + + if instanceID != "" { + fmt.Printf("Connecting to instance %s...\n", instanceID) + aws.RunCommand( + strings.Split( + fmt.Sprintf("aws ssm start-session --target %s", instanceID), + " ", + ), + ) + } + }, + } +) + +type ( + model struct { + help help.Model + lastKey string + keys keyMap + table table.Model + quitting bool + } + + // keyMap defines a set of keybindings. To work for help it must satisfy + // key.Map. It could also very easily be a map[string]key.Binding. + keyMap struct { + Up key.Binding + Down key.Binding + Help key.Binding + Enter key.Binding + Quit key.Binding + } +) + +// ShortHelp returns keybindings to be shown in the mini help view. It's part +// of the key.Map interface. +func (k keyMap) ShortHelp() []key.Binding { // lint:allow_large_memory // Implementing a model I have no control over. + return []key.Binding{ + k.Help, + k.Enter, + k.Quit, + } +} + +// FullHelp returns keybindings for the expanded help view. It's part of the +// key.Map interface. +func (k keyMap) FullHelp() [][]key.Binding { // lint:allow_large_memory // Implementing a model I have no control over. + return [][]key.Binding{ + { // first column + k.Up, + k.Down, + }, + { // second column + k.Help, + k.Quit, + }, + { // third column + k.Enter, + }, + } +} + +func (m model) Init() tea.Cmd { // lint:allow_large_memory // Implementing a model I have no control over. + return nil +} + +func (m model) Update( // lint:allow_large_memory // Implementing a model I have no control over. + msg tea.Msg, +) (tea.Model, tea.Cmd) { + var cmd tea.Cmd + + switch msg := msg.(type) { + case tea.WindowSizeMsg: + // If we set a width on the help menu it can gracefully truncate + // its view as needed. + m.help.Width = msg.Width + + case tea.KeyMsg: + switch { + case key.Matches(msg, m.keys.Up): + m.lastKey = "↑" + case key.Matches(msg, m.keys.Down): + m.lastKey = "↓" + case key.Matches(msg, m.keys.Help): + m.help.ShowAll = !m.help.ShowAll + case key.Matches(msg, m.keys.Enter): + m.quitting = true + instanceID = m.table.SelectedRow()[1] + + return m, tea.Quit + case key.Matches(msg, m.keys.Quit): + m.quitting = true + + return m, tea.Quit + } + } + + m.table, cmd = m.table.Update(msg) + + return m, cmd +} + +func (m model) View() string { // lint:allow_large_memory // Implementing a model I have no control over. + if m.quitting { + return "" + } + + helpView := m.help.View(m.keys) + + return baseStyle.Render(m.table.View()) + "\n" + helpView +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + logger.Fatal(err) + } +} diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 0000000..a76cced --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,122 @@ +// Copyright 2023–2024, Northwood Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "path/filepath" + "runtime" + "runtime/debug" + "strings" + + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/table" + "github.com/northwood-labs/golang-utils/archstring" + "github.com/spf13/cobra" +) + +var ( + // Version represents the version of the software. + Version = "dev" + + // Commit represents the git commit hash of the software. + Commit = vcs("vcs.revision", "unknown") + + // BuildDate represents the date the software was built. + BuildDate = vcs("vcs.time", "unknown") + + // Dirty represents whether or not the git repo was dirty when the software was built. + Dirty = vcs("vcs.modified", "unknown") + + // PGOEnabled represents whether or not the build leveraged Profile-Guided Optimization (PGO). + PGOEnabled = vcs("-pgo", "false") + + versionCmd = &cobra.Command{ + Use: "version", + Short: "Long-form version information", + Long: helpText.Render(`Long-form version information, including the build commit hash, build date, Go +version, and external dependencies.`), + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(style.Render(" BUILD INFO ")) + + t := table.New(). + Border(lipgloss.RoundedBorder()). + BorderStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("99"))). + BorderColumn(true). + StyleFunc(func(row, col int) lipgloss.Style { + return lipgloss.NewStyle().Padding(0, 1) + }). + Headers("FIELD", "VALUE") + + t.Row("Version", Version) + t.Row("Go version", runtime.Version()) + t.Row("Git commit", Commit) + if Dirty == "true" { + t.Row("Dirty repo", Dirty) + } + if !strings.Contains(PGOEnabled, "false") { + t.Row("PGO", filepath.Base(PGOEnabled)) + } + t.Row("Build date", BuildDate) + t.Row("OS/Arch", fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)) + t.Row("System", archstring.GetFriendlyName(runtime.GOOS, runtime.GOARCH)) + t.Row("CPU cores", fmt.Sprintf("%d", runtime.NumCPU())) + + fmt.Println(t.Render()) + + //---------------------------------------------------------------------- + + if buildInfo, ok := debug.ReadBuildInfo(); ok { + fmt.Println(style.Render(" DEPENDENCIES ")) + + td := table.New(). + Border(lipgloss.RoundedBorder()). + BorderStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("99"))). + BorderColumn(true). + StyleFunc(func(row, col int) lipgloss.Style { + return lipgloss.NewStyle().Padding(0, 1) + }). + Headers("DEPENDENCY", "VERSION") + + for i := range buildInfo.Deps { + dependency := buildInfo.Deps[i] + td.Row(dependency.Path, dependency.Version) + } + + fmt.Println(td.Render()) + } + + fmt.Println("") + }, + } +) + +func init() { // lint:allow_init + rootCmd.AddCommand(versionCmd) +} + +func vcs(key, fallback string) string { + if info, ok := debug.ReadBuildInfo(); ok { + for i := range info.Settings { + setting := info.Settings[i] + + if setting.Key == key { + return setting.Value + } + } + } + + return fallback +} diff --git a/cmd_connect.go b/cmd_connect.go deleted file mode 100644 index 4a7514c..0000000 --- a/cmd_connect.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "fmt" - "sort" - "strings" - - prompt "github.com/c-bata/go-prompt" - cli "github.com/jawher/mow.cli" - "github.com/northwood-labs/golang-utils/exiterrorf" -) - -const ( - condEquals = "==" - condContains = "=~" - condStartsWith = "=^" -) - -func cmdConnect(cmd *cli.Cmd) { - tags := cmd.StringsOpt("t tag", []string{}, fmt.Sprintf( - "Tag names and tag values, separated by a condition. Conditions are `%s` (equals),\n"+ - "`%s` (contains), and `%s` (starts with). Flag can be called multiple times.", - condEquals, - condContains, - condStartsWith, - )) - - filters := cmd.StringsOpt("f filter", []string{}, fmt.Sprintf( - "Filter names and filter values, separated by a `%s` (equals) condition. Flag can\n"+ - "be called multiple times. See https://bit.ly/3JqctHs for list of valid values.", - condEquals, - )) - - cmd.Action = func() { - tagStructs := processTagInput(*tags) - filterStructs := processFilterInput(*filters) - - instances, err = getEc2Instances(tagStructs, filterStructs) - if err != nil { - exiterrorf.ExitErrorf(err) - } - - // Sort by text - sort.SliceStable(instances, func(i, j int) bool { - return strings.ToLower(instances[i].Name) < strings.ToLower(instances[j].Name) - }) - - instanceName := prompt.Input( - "The instance to connect to [press tab]: ", - func(tags []Tag) func(in prompt.Document) []prompt.Suggest { - return instanceCompleter - }(tagStructs), - CustomOptions()..., - ) - - runCommand( - strings.Split( - fmt.Sprintf("aws ssm start-session --target %s", instanceName), - " ", - ), - ) - } -} - -func processTagInput(tags []string) []Tag { - out := []Tag{} - - for i := range tags { - tag := tags[i] - - if strings.Contains(tag, condEquals) { - result := strings.Split(tag, condEquals) - - out = append(out, Tag{ - Name: result[0], - Equals: result[1], - }) - } else if strings.Contains(tag, condContains) { - result := strings.Split(tag, condContains) - - out = append(out, Tag{ - Name: result[0], - Contains: result[1], - }) - } else if strings.Contains(tag, condStartsWith) { - result := strings.Split(tag, condStartsWith) - - out = append(out, Tag{ - Name: result[0], - StartsWith: result[1], - }) - } - } - - return out -} - -func processFilterInput(filters []string) []Filter { - out := []Filter{} - - for i := range filters { - filter := filters[i] - result := strings.Split(filter, condEquals) - - out = append(out, Filter{ - Name: result[0], - Equals: result[1], - }) - } - - return out -} - -func instanceCompleter(in prompt.Document) []prompt.Suggest { - s := []prompt.Suggest{} - - for i := range instances { - instance := instances[i] - - s = append(s, prompt.Suggest{ - Text: func() string { - if instance.ID != "" { - return instance.ID - } - - return "" - }(), - Description: func() string { - if instance.Name != "" { - return instance.Name - } - - return "" - }(), - }) - } - - return prompt.FilterFuzzy(s, in.GetWordBeforeCursorUntilSeparator("\n"), true) -} diff --git a/cmd_version.go b/cmd_version.go deleted file mode 100644 index 45f86ec..0000000 --- a/cmd_version.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "fmt" - "os" - "runtime" - "runtime/debug" - "text/tabwriter" - - cli "github.com/jawher/mow.cli" - "github.com/northwood-labs/golang-utils/exiterrorf" -) - -func cmdVersion(cmd *cli.Cmd) { - cmd.Action = func() { - fmt.Println(colorHeader.Render(" BASIC ")) - - w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) - - fmt.Fprintf(w, " Version:\t%s\t\n", version) - fmt.Fprintf(w, " Go version:\t%s\t\n", runtime.Version()) - fmt.Fprintf(w, " Git commit:\t%s\t\n", commit) - fmt.Fprintf(w, " Build date:\t%s\t\n", date) - fmt.Fprintf(w, " OS/Arch:\t%s/%s\t\n", runtime.GOOS, runtime.GOARCH) - fmt.Fprintf(w, " System:\t%s\t\n", getFriendlyName(runtime.GOOS, runtime.GOARCH)) - fmt.Fprintf(w, " CPU Cores:\t%d\t\n", runtime.NumCPU()) - - err := w.Flush() - if err != nil { - exiterrorf.ExitErrorf(err) - } - - fmt.Println("") - - //---------------------------------------------------------------------- - - if buildInfo, ok := debug.ReadBuildInfo(); ok { - fmt.Println(colorHeader.Render(" DEPENDENCIES ")) - - w = tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) - - for i := range buildInfo.Deps { - dependency := buildInfo.Deps[i] - fmt.Fprintf(w, " %s\t%s\t\n", dependency.Path, dependency.Version) - } - } - - err = w.Flush() - if err != nil { - exiterrorf.ExitErrorf(err) - } - - fmt.Println("") - } -} diff --git a/ec2_instances.go b/ec2_instances.go deleted file mode 100644 index 2fa0fa5..0000000 --- a/ec2_instances.go +++ /dev/null @@ -1,229 +0,0 @@ -package main - -import ( - "context" - "fmt" - "strings" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/aws/aws-sdk-go-v2/service/ec2/types" - "github.com/caarlos0/spin" - "github.com/northwood-labs/awsutils" - "github.com/northwood-labs/golang-utils/exiterrorf" -) - -// Ec2Instance represents a list of EC2 instances by name tag and instance ID. -type Ec2Instance struct { - ID string - Name string -} - -// Tag represents a list of EC2 instance tags that we want to filter by. -type Tag struct { - Name string - Equals string - Contains string - StartsWith string -} - -// Filter represents a list of EC2 instance filters that we want to apply. -type Filter struct { - Name string - Equals string -} - -func getEc2Instances(tags []Tag, filters []Filter) ([]Ec2Instance, error) { - s := spin.New("Fetching instances %s ") - s.Set(spin.Box2) - s.Start() - - defer s.Stop() - - ctx := context.Background() - retries := 5 - verbose := false - - config, err := awsutils.GetAWSConfig(ctx, *awsRegion, *awsProfile, retries, verbose) - if err != nil { - exiterrorf.ExitErrorf(err) - } - - var collectedInstances []Ec2Instance - - ec2Client := ec2.NewFromConfig(config) - - // Base filter - ffs := []types.Filter{ - { - // Only running instances... - Name: aws.String("instance-state-name"), - Values: []string{ - *aws.String("running"), - }, - }, - } - - // Apply user filters - for i := range filters { - filter := filters[i] - - ffs = append(ffs, types.Filter{ - Name: aws.String(filter.Name), - Values: func() []string { - out := []string{} - parts := strings.Split(filter.Equals, ",") - - for i := range parts { - part := parts[i] - out = append(out, *aws.String(part)) - } - - return out - }(), - }) - } - - // Apply user tags - allTags := getTagEquals(tags) - - for i := range allTags { - tag := allTags[i] - - ffs = append(ffs, types.Filter{ - Name: aws.String("tag:" + tag.Name), - Values: []string{ - *aws.String(tag.Equals), - }, - }) - } - - response, err := ec2Client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ - Filters: ffs, - }) - if err != nil { - return []Ec2Instance{}, fmt.Errorf("error looking up instances from EC2 API: %w", err) - } - - // Everything after this point is client-side filtering. - allContains := getTagContains(tags) - allStartsWith := getTagStartsWith(tags) - - for r := range response.Reservations { - reservation := &response.Reservations[r] - instances := reservation.Instances - - // Super inefficient. I wanna say O(n²)...? - if len(allContains) > 0 { - instances = filterInstances(instances, func(instance types.Instance) bool { - for i := range instance.Tags { - t := instance.Tags[i] - - for j := range allContains { - c := allContains[j] - - if *t.Key == c.Name { - return strings.Contains(*t.Value, c.Contains) - } - } - } - - return false - }) - } - - // Super inefficient. I wanna say O(n²)...? - if len(allStartsWith) > 0 { - instances = filterInstances(instances, func(instance types.Instance) bool { - for i := range instance.Tags { - t := instance.Tags[i] - - for j := range allStartsWith { - c := allStartsWith[j] - - if *t.Key == c.Name { - return strings.HasPrefix(*t.Value, c.StartsWith) - } - } - } - - return false - }) - } - - for i := range instances { - instance := &instances[i] - - // If the conditions exist, apply them. - name := findName(instance) - - collectedInstances = append(collectedInstances, Ec2Instance{ - ID: *instance.InstanceId, - Name: *name, - }) - } - } - - return collectedInstances, nil -} - -// Calling this is duplicate work. Refactor to collect this data in a single pass. -func findName(instance *types.Instance) *string { - emptyString := "" - - for t := range instance.Tags { - tag := instance.Tags[t] - - if *tag.Key == "Name" { - return tag.Value - } - } - - return &emptyString -} - -func filterTags(vs []Tag, f func(Tag) bool) []Tag { - vsf := make([]Tag, 0) - - for i := range vs { - v := vs[i] - - if f(v) { - vsf = append(vsf, v) - } - } - - return vsf -} - -func filterInstances(vs []types.Instance, f func(types.Instance) bool) []types.Instance { - vsf := make([]types.Instance, 0) - - for i := range vs { - v := vs[i] - - if f(v) { - vsf = append(vsf, v) - } - } - - return vsf -} - -func getTagEquals(tags []Tag) []Tag { - return filterTags(tags, func(t Tag) bool { - return t.Equals != "" - }) -} - -func getTagContains(tags []Tag) []Tag { - return filterTags(tags, func(t Tag) bool { - return t.Contains != "" - }) -} - -func getTagStartsWith(tags []Tag) []Tag { - return filterTags(tags, func(t Tag) bool { - return t.StartsWith != "" - }) -} diff --git a/ecrc.toml b/ecrc.toml new file mode 100644 index 0000000..f54d301 --- /dev/null +++ b/ecrc.toml @@ -0,0 +1,75 @@ +## +# Generate with: +# tomljson ecrc.toml > .ecrc +## + +# goplicate-start:config +Debug = false +IgnoreDefaults = true +NoColor = false +SpacesAfterTabs = false +Verbose = false +# goplicate-end:config + +Exclude = [ + # goplicate-start:excludes + "\\.7z$", + "\\.avif", + "\\.bak$", + "\\.bin$", + "\\.bz2$", + "\\.cache$", + "\\.css\\.map$", + "\\.dcignore$", + "\\.ecrc$", + "\\.eot$", + "\\.example$", + "\\.gif$", + "\\.go$", + "\\.golangci.yml$", + "\\.goreleaser.yml$", + "\\.gotmpl$", + "\\.gz$", + "\\.ico$", + "\\.jpeg$", + "\\.jpg$", + "\\.js\\.map$", + "\\.log$", + "\\.mp4$", + "\\.otf$", + "\\.patch$", + "\\.pbm", + "\\.pdf$", + "\\.pgm", + "\\.png$", + "\\.pnm", + "\\.ppm", + "\\.snap$", + "\\.svg$", + "\\.tar$", + "\\.terraform-docs\\.yml$", + "\\.terraform\\.lock\\.hcl$", + "\\.ttf$", + "\\.txt$", + "\\.vscode/.*?\\.json$", + "\\.webp$", + "\\.wmv$", + "\\.woff$", + "\\.woff2$", + "\\.zip$", + "^\\.pnp\\.cjs$", + "^\\.pnp\\.js$", + "^\\.pnp\\.loader\\.mjs$", + "^\\.yarn/", + "^Cargo\\.lock$", + "^composer\\.lock$", + "^package-lock\\.json$", + "^yarn\\.lock$", + "cliff\\.toml$", + "go\\.mod$", + "go\\.sum$", + "min\\.css$", + "min\\.js$", + "package-lock\\.json$", + # goplicate-end:excludes +] diff --git a/go.mod b/go.mod index 1242e32..bd380e1 100644 --- a/go.mod +++ b/go.mod @@ -1,37 +1,53 @@ module github.com/northwood-labs/ssm-shell -go 1.17 +go 1.22.0 require ( - github.com/aws/aws-sdk-go-v2 v1.11.1 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.23.0 - github.com/c-bata/go-prompt v0.2.6 - github.com/caarlos0/spin v1.1.0 - github.com/gookit/color v1.5.0 - github.com/jawher/mow.cli v1.2.1-0.20200813103149-519fe99ae7ae - github.com/northwood-labs/awsutils v0.0.0-20211122202415-2cf4914afebd - github.com/northwood-labs/golang-utils/exiterrorf v0.0.0-20211120002424-5d7d1452056f - github.com/sirupsen/logrus v1.8.1 + github.com/aws/aws-sdk-go-v2 v1.26.1 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.155.1 + github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/bubbletea v0.25.0 + github.com/charmbracelet/huh/spinner v0.0.0-20240328185852-590ecabc34b9 + github.com/charmbracelet/lipgloss v0.10.0 + github.com/charmbracelet/log v0.4.0 + github.com/northwood-labs/awsutils v0.0.0-20240315061544-28570bf7115b + github.com/northwood-labs/golang-utils/archstring v0.0.0-20240301221220-6be250811dab + github.com/northwood-labs/golang-utils/exiterrorf v0.0.0-20240301221220-6be250811dab + github.com/spf13/cobra v1.8.0 ) require ( - github.com/aws/aws-sdk-go-v2/config v1.10.2 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.6.2 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.6.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.10.1 // indirect - github.com/aws/smithy-go v1.9.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.10 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.10 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.20.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect + github.com/aws/smithy-go v1.20.2 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/containerd/console v1.0.4 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/mattn/go-colorable v0.1.7 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect - github.com/mattn/go-tty v0.0.3 // indirect - github.com/pkg/term v1.2.0-beta.2 // indirect - github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index ebb0105..a559893 100644 --- a/go.sum +++ b/go.sum @@ -1,95 +1,112 @@ -github.com/aws/aws-sdk-go-v2 v1.11.1 h1:GzvOVAdTbWxhEMRK4FfiblkGverOkAT0UodDxC1jHQM= -github.com/aws/aws-sdk-go-v2 v1.11.1/go.mod h1:SQfA+m2ltnu1cA0soUkj4dRSsmITiVQUJvBIZjzfPyQ= -github.com/aws/aws-sdk-go-v2/config v1.10.2 h1:lrNnqRpPDgrozyKMnt5/Bhcv01kel7JO6KFx4VdroCY= -github.com/aws/aws-sdk-go-v2/config v1.10.2/go.mod h1:OY1jfuHozx6GDg+NITKNukVQi4fLlnenu1PAbDJg5fk= -github.com/aws/aws-sdk-go-v2/credentials v1.6.2 h1:2faRNX8JgZVy7dDxERkaGBqb/xo5Rgmc8JMPL5j1o58= -github.com/aws/aws-sdk-go-v2/credentials v1.6.2/go.mod h1:8kRH9fthlxHEeNJ3g1N3NTSUMBba+KtTM8hp6SvUWn8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.1 h1:pXwGBINU30CsjYztV/IyCgA7QKp99Q8wM4Gb0Ls3rB0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.1/go.mod h1:MYiG3oeEcmrdBOV7JOIWhionzyRZJWCnByS5FmvhAoU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.1 h1:LZwqhOyqQ2w64PZk04V0Om9AEExtW8WMkCRoE1h9/94= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.1/go.mod h1:22SEiBSQm5AyKEjoPcG1hzpeTI+m9CXfE6yt1h49wBE= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.1 h1:ObMfGNk0xjOWduPxsrRWVwZZia3e9fOcO6zlKCkt38s= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.1/go.mod h1:1xvCD+I5BcDuQUc+psZr7LI1a9pclAWZs3S3Gce5+lg= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.1 h1:fdQSN/ieDwbxdj7ptvFKjS2cS2a91l/WdjacCt5GgTE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.1/go.mod h1:5eEM4wZ6I2GaeOaVXsiJexIH4P1sFnK5Yp2Tlw9Ah3c= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.23.0 h1:ZZiG7Hol3Zjb7rGStw4QUuj4MAHBSdZjnt3D7+csgS8= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.23.0/go.mod h1:Xv0jfvBUvJMRnYA5sX+VisekFtkWzD68qTW1VkvcrIo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.1 h1:ZFSfgetO5kf4WXy+a2B8zug6DXGUYjsWacyvwx5cgXU= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.1/go.mod h1:fEaHB2bi+wVZw4uKMHEXTL9LwtT4EL//DOhTeflqIVo= -github.com/aws/aws-sdk-go-v2/service/sso v1.6.1 h1:NF/qN6e8hdHO/Pt5jN+S65dxFom3b8+ciVdyv8Jr00U= -github.com/aws/aws-sdk-go-v2/service/sso v1.6.1/go.mod h1:/73aFBwUl60wKBKhdth2pEOkut5ZNjVHGF9hjXz0bM0= -github.com/aws/aws-sdk-go-v2/service/sts v1.10.1 h1:2DKYFOmC7d3WOzdBTFJxfkcMXVVIgcitrpEoJDUKlN4= -github.com/aws/aws-sdk-go-v2/service/sts v1.10.1/go.mod h1:+BmlPeQ1Y+PuIho93MMKDby12PoUnt1SZXQdEHCzSlw= -github.com/aws/smithy-go v1.9.0 h1:c7FUdEqrQA1/UVKKCNDFQPNKGp4FQg3YW4Ck5SLTG58= -github.com/aws/smithy-go v1.9.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI= -github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= -github.com/caarlos0/spin v1.1.0 h1:EjsfGbZJejib25BPnDqf7iL2z9RUna7refvUf+AN9UE= -github.com/caarlos0/spin v1.1.0/go.mod h1:HOC4pUvfhjXR2yDt+sEY9dRc2m4CCaK5z5oQYAbzXSA= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= +github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2/config v1.27.10 h1:PS+65jThT0T/snC5WjyfHHyUgG+eBoupSDV+f838cro= +github.com/aws/aws-sdk-go-v2/config v1.27.10/go.mod h1:BePM7Vo4OBpHreKRUMuDXX+/+JWP38FLkzl5m27/Jjs= +github.com/aws/aws-sdk-go-v2/credentials v1.17.10 h1:qDZ3EA2lv1KangvQB6y258OssCHD0xvaGiEDkG4X/10= +github.com/aws/aws-sdk-go-v2/credentials v1.17.10/go.mod h1:6t3sucOaYDwDssHQa0ojH1RpmVmF5/jArkye1b2FKMI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.155.1 h1:JBwnHlQvL39eeT03+vmBZuziutTKljmOKboKxQuIBck= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.155.1/go.mod h1:xejKuuRDjz6z5OqyeLsz01MlOqqW7CqpAB4PabNvpu8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.4 h1:WzFol5Cd+yDxPAdnzTA5LmpHYSWinhmSj4rQChV0ee8= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.4/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw= +github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= +github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/huh/spinner v0.0.0-20240328185852-590ecabc34b9 h1:gC8QvRaFHkC8iMyFR1HSUa7gWrDn8iYMNRcmyY/NNG4= +github.com/charmbracelet/huh/spinner v0.0.0-20240328185852-590ecabc34b9/go.mod h1:nrBG0YEHaxdbqHXW1xvG1hPqkuac9Eg7RTMvogiXuz0= +github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= +github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= +github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= +github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= -github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= -github.com/jawher/mow.cli v1.2.1-0.20200813103149-519fe99ae7ae h1:qll00DBP8zNOgDDJ5ypWpgGKXrY3vMlViNttWJ1SjZE= -github.com/jawher/mow.cli v1.2.1-0.20200813103149-519fe99ae7ae/go.mod h1:y+pcA3jBAdo/GIZx/0rFjw/K2bVEODP9rfZOfaiq8Ko= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= -github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= -github.com/northwood-labs/awsutils v0.0.0-20211122202415-2cf4914afebd h1:2jv8NnuuqB+T+HkEQF6ixdWpCfV6meMWQHXtxx3iUgM= -github.com/northwood-labs/awsutils v0.0.0-20211122202415-2cf4914afebd/go.mod h1:3yNQ3Fwync1OZXb9djMObgqZtk8/BidO5HqYVn40+t8= -github.com/northwood-labs/golang-utils/exiterrorf v0.0.0-20211120002424-5d7d1452056f h1:Fp3lv6LzKcBeNOkTYFq3EiFcpFWwNIJJ20FphJV+7no= -github.com/northwood-labs/golang-utils/exiterrorf v0.0.0-20211120002424-5d7d1452056f/go.mod h1:wTNgA9UbpSJrpyt3ZLEIbDBRlUR8wy0UqJd0EFm3gHU= -github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= -github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/northwood-labs/awsutils v0.0.0-20240315061544-28570bf7115b h1:CV3xX5uJB4hRxLSJPJPsv2cn4f2BqTieGaMUYe5biCk= +github.com/northwood-labs/awsutils v0.0.0-20240315061544-28570bf7115b/go.mod h1:2DYDOSdfW0RFCRgvT6GSlagO+f7gd5fN0N2vgklmQUY= +github.com/northwood-labs/golang-utils/archstring v0.0.0-20240301221220-6be250811dab h1:3aLFKtnh41to4V21HVfohTtgdNIMqvXWICOmjngohc0= +github.com/northwood-labs/golang-utils/archstring v0.0.0-20240301221220-6be250811dab/go.mod h1:ixVR+WTyw0LxuhcprIZzxgxAy7LyZRFkpyIwvyNdpcY= +github.com/northwood-labs/golang-utils/exiterrorf v0.0.0-20240301221220-6be250811dab h1:wTZCozOiUWNj6T2al4r4+RINAtsgrtPriLXOS9Urucc= +github.com/northwood-labs/golang-utils/exiterrorf v0.0.0-20240301221220-6be250811dab/go.mod h1:DZOF/zxKfLJhhFfPhDNrUEU0/MvT5GpFeX3HL1UdYTY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 1006b84..b186b60 100644 --- a/main.go +++ b/main.go @@ -1,84 +1,21 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os" - "runtime" - - "github.com/gookit/color" - cli "github.com/jawher/mow.cli" - "github.com/northwood-labs/golang-utils/exiterrorf" - logrus "github.com/sirupsen/logrus" -) - -const ( - fileWritable = 0o666 -) +// Copyright 2023–2024, Northwood Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -var ( - // Make referencable throughout. - app *cli.Cli - err error - awsProfile *string - awsRegion *string - instances []Ec2Instance - - // Color text. - colorHeader = color.New(color.FgWhite, color.BgBlue, color.OpBold) - - // Logger. - logger = logrus.New() - ff *os.File +package main - // Buildtime variables. - commit string - date string - version string -) +import "github.com/northwood-labs/ssm-shell/cmd" func main() { - app = cli.App("ssm-shell", `Simplifies opening a shell session on your EC2 instances using AWS Session -Manager. Supports standard AWS environment variables for authentication. -https://go.aws/3LCabH9 - -See also: - - * https://github.com/99designs/aws-vault - * https://github.com/fiveai/aws-okta`) - - app.Version("version", fmt.Sprintf( - "AWS SSM Shell %s (%s_%s)", - version, - runtime.GOOS, - runtime.GOARCH, - )) - - _, ssmShellLog := os.LookupEnv("SSMSHELL_LOG") - - logger.Level = logrus.DebugLevel - logger.SetFormatter(&logrus.TextFormatter{ - DisableColors: true, - DisableQuote: true, - FullTimestamp: true, - }) - - // You could set this to any `io.Writer` such as a file - ff, err = os.OpenFile("/tmp/ssm-shell.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, fileWritable) // lint:allow_666 - if err == nil && ssmShellLog { - logger.Out = ff - } else { - logger.Out = ioutil.Discard - } - - awsProfile = app.StringOpt("p profile", "", "The AWS profile entry from your AWS CLI configuration.") - awsRegion = app.StringOpt("r region", "", "The AWS region to which to communicate.") - - app.Command("connect", "Fetch a list of instances to select from.", cmdConnect) - app.Command("version", "Verbose information about the build.", cmdVersion) - - err = app.Run(os.Args) - if err != nil { - exiterrorf.ExitErrorf(err) - } + cmd.Execute() } diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 7dc042a..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,97 +0,0 @@ -# Project Info -site_name: SSM Shell -site_description: >- - This is my description. - -# Repository -repo_url: https://github.com/northwood-labs/ssm-shell/ -repo_name: ssm-shell -edit_uri: edit/main/markdown/ -docs_dir: markdown/ - -# Configuration -theme: - name: material - # custom_dir: overrides - include_search_page: true - search_index_only: true - language: en - favicon: img/favicon.ico - icon: - logo: octicons/graph-24 - repo: fontawesome/brands/github - font: false - palette: - scheme: preference - accent: indigo - features: - - instant - - search.highlight - - header.hide - -# Custom CSS -extra_css: - - static/styles.css - -# Custom JS -# extra_javascript: -# - static/scripts.js - -module_name: markdown-macros - -# Extensions -markdown_extensions: - - admonition - - attr_list - - def_list - - footnotes - - meta - - pymdownx.betterem: - smart_enable: all - - pymdownx.caret - - pymdownx.details - - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg - - pymdownx.highlight: - use_pygments: true - # linenums: true - # linenums_style: pymdownx.inline - - pymdownx.inlinehilite - - pymdownx.keys - - pymdownx.mark - - pymdownx.snippets - - pymdownx.superfences - - pymdownx.tasklist: - custom_checkbox: true - - toc: - toc_depth: 5 - permalink: true - # slugify: pymdownx.slugs.uslugify - - pymdownx.tabbed - - pymdownx.tilde - -# Plugins -plugins: - - git-revision-date - - git-revision-date-localized: - type: timeago - fallback_to_build_date: true - - macros - - search: - prebuild_index: true - lang: - - en - -# Social Icons -extra: - social: - - icon: fontawesome/brands/github - link: https://github.com/northwood-labs/ssm-shell/ - -# Navigation -nav: - - index.md - - Section: - - features-autocomplete.md - - troubleshooting.md diff --git a/poetry.toml b/poetry.toml deleted file mode 100644 index 975eeee..0000000 --- a/poetry.toml +++ /dev/null @@ -1 +0,0 @@ -virtualenvs.in-project = true diff --git a/prompt.go b/prompt.go deleted file mode 100644 index 04fd23c..0000000 --- a/prompt.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - prompt "github.com/c-bata/go-prompt" -) - -// CustomOptions are a standardized set of parameters that get passed uniformly to all prompt instances. -func CustomOptions() []prompt.Option { - options := []prompt.Option{ - // Initial - prompt.OptionInputTextColor(prompt.White), - prompt.OptionSuggestionBGColor(prompt.Cyan), - prompt.OptionDescriptionTextColor(prompt.White), - prompt.OptionDescriptionBGColor(prompt.DarkGray), - - // Selected - prompt.OptionSelectedSuggestionTextColor(prompt.Yellow), - prompt.OptionSelectedSuggestionBGColor(prompt.Cyan), - prompt.OptionSelectedDescriptionBGColor(prompt.DarkGray), - - // Other - prompt.OptionShowCompletionAtStart(), - // prompt.OptionCompletionWordSeparator(emptyString), - } - - return options -} diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 2ff830e..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,20 +0,0 @@ -[tool.poetry] -name = "SSM Shell" -version = "0.0" -description = "Generating SSM Shell documentation." -authors = ["Ryan Parman "] - -[tool.poetry.dependencies] -python = "^3.8" -mkdocs = "^1.1.2" -mkdocs-git-revision-date-localized-plugin = "^0.7.2" -mkdocs-git-revision-date-plugin = "^0.3" -mkdocs-material = "^6.0.1" -mkdocs-material-extensions = "^1.0.1" -mkdocs-pymdownx-material-extras = "^1.1.1" -pymdown-extensions = "^8.0.1" -mkdocs-macros-plugin = "^0.4.18" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1f5d71b..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -poetry>=1.1.0,<2.0 diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index 00bde30834b02d0ae75f6f109b8ccb97afb2fb98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31760 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 diff --git a/scripts/generate-contributors.sh b/scripts/generate-contributors.sh new file mode 100755 index 0000000..e892d0c --- /dev/null +++ b/scripts/generate-contributors.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -e + +# see also ".mailmap" for how email addresses and names are deduplicated +OUT="${1:-.}" +{ + # editorconfig-checker-disable + cat <<-'EOF' + # File @generated by scripts/generate-contributors.sh. DO NOT EDIT. + # This file lists all contributors to the repository. + # See scripts/generate-contributors.sh to make modifications. + EOF + # editorconfig-checker-enable + + echo + + # shellcheck disable=2312 + git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf + +} >"${OUT}/CONTRIBUTORS" +cat "${OUT}/CONTRIBUTORS" diff --git a/ssm-shell@2x.png b/ssm-shell@2x.png new file mode 100644 index 0000000..5af07d1 --- /dev/null +++ b/ssm-shell@2x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c268b53a3fb9c423867c258ea2105d85a0c2511c6ad865c32659954bffb9135c +size 155859 diff --git a/strings.go b/strings.go deleted file mode 100644 index 280cfe5..0000000 --- a/strings.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -var ( - // OSMap is a mapping of GOOS values to "friendly" values. - // https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63 - // https://github.com/golang/go/blob/master/src/go/build/syslist.go - OSMap = map[string]string{ - "aix": "AIX", - "android": "Android", - "darwin": "macOS", - "dragonfly": "DragonFly BSD", - "freebsd": "FreeBSD", - "hurd": "GNU Hurd", - "illumos": "illumos", - "ios": "iOS", - "js": "JavaScript", - "linux": "Linux", - "netbsd": "NetBSD", - "openbsd": "OpenBSD", - "plan9": "Plan 9", - "solaris": "Solaris", - "windows": "Windows", - "zos": "z/OS", - } - - // ArchMap is a mapping of GOARCH values to "friendly" values. - // https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63 - // https://github.com/golang/go/blob/master/src/go/build/syslist.go - ArchMap = map[string]string{ - "386": "Intel (32-bit)", - "amd64": "Intel (64-bit)", - "amd64p32": "Intel (64-bit)", - "arm": "ARM (32-bit)", - "arm64": "ARM (64-bit)", - "arm64be": "ARM (64-bit)", - "armbe": "ARM (32-bit)", - "mips": "MIPS (32-bit)", - "mips64": "MIPS (64-bit)", - "mips64le": "MIPS (64-bit)", - "mips64p32": "MIPS (64-bit)", - "mips64p32le": "MIPS (64-bit)", - "mipsle": "MIPS (32-bit)", - "ppc": "PowerPC (32-bit)", - "ppc64": "PowerPC (64-bit)", - "ppc64le": "PowerPC (64-bit)", - "riscv": "RISC-V (32-bit)", - "riscv64": "RISC-V (64-bit)", - "s390": "System/390 (32-bit)", - "s390x": "System/390 (64-bit)", - "sparc": "SPARC (32-bit)", - "sparc64": "SPARC (34-bit)", - "wasm": "WebAssembly", - } -) diff --git a/templates/usage.gohtml b/templates/usage.gohtml deleted file mode 100644 index 2bd3342..0000000 --- a/templates/usage.gohtml +++ /dev/null @@ -1,24 +0,0 @@ -# Command Usage - - - -![Usage](static/prompt.png) - ----- - -{{ with .Commands -}} -{{- range . -}} -## {{ .Cmd }} - -```plain -{{ .Help }} -``` - -{{ end -}} -{{- end -}} diff --git a/trivy-license.yaml b/trivy-license.yaml new file mode 100644 index 0000000..3033a02 --- /dev/null +++ b/trivy-license.yaml @@ -0,0 +1,201 @@ +--- +# This is a "full copy" file. Any manual changes will be overwritten in the next +# sync. If you want to make changes to this file, open a PR against the upstream +# file. Changes will be reflected in ALL repositories during the next sync. + +cache: + backend: fs + clear: false +db: + download-java-only: false + download-only: false + java-repository: ghcr.io/aquasecurity/trivy-java-db + java-skip-update: false + light: false + no-progress: false + repository: ghcr.io/aquasecurity/trivy-db + skip-update: false +# debug: false +dependency-tree: true +exit-code: 0 +format: table +ignore-policy: "" +ignorefile: .trivyignore +include-dev-deps: false +insecure: false +license: + confidencelevel: "0.9" + forbidden: + - AGPL-1.0 + - AGPL-3.0 + - CC-BY-NC-1.0 + - CC-BY-NC-2.0 + - CC-BY-NC-2.5 + - CC-BY-NC-3.0 + - CC-BY-NC-4.0 + - CC-BY-NC-ND-1.0 + - CC-BY-NC-ND-2.0 + - CC-BY-NC-ND-2.5 + - CC-BY-NC-ND-3.0 + - CC-BY-NC-ND-4.0 + - CC-BY-NC-SA-1.0 + - CC-BY-NC-SA-2.0 + - CC-BY-NC-SA-2.5 + - CC-BY-NC-SA-3.0 + - CC-BY-NC-SA-4.0 + - Commons-Clause + - Facebook-2-Clause + - Facebook-3-Clause + - Facebook-Examples + full: true + ignored: [] + notice: + - AFL-1.1 + - AFL-1.2 + - AFL-2.0 + - AFL-2.1 + - AFL-3.0 + - Apache-1.0 + - Apache-1.1 + - Apache-2.0 + - Artistic-1.0-cl8 + - Artistic-1.0-Perl + - Artistic-1.0 + - Artistic-2.0 + - BSL-1.0 + - BSD-2-Clause-FreeBSD + - BSD-2-Clause-NetBSD + - BSD-2-Clause + - BSD-3-Clause-Attribution + - BSD-3-Clause-Clear + - BSD-3-Clause-LBNL + - BSD-3-Clause + - BSD-4-Clause + - BSD-4-Clause-UC + - BSD-Protection + - CC-BY-1.0 + - CC-BY-2.0 + - CC-BY-2.5 + - CC-BY-3.0 + - CC-BY-4.0 + - FTL + - ISC + - ImageMagick + - Libpng + - Lil-1.0 + - Linux-OpenIB + - LPL-1.02 + - LPL-1.0 + - MS-PL + - MIT + - NCSA + - OpenSSL + - PHP-3.01 + - PHP-3.0 + - PIL + - Python-2.0 + - Python-2.0-complete + - PostgreSQL + - SGI-B-1.0 + - SGI-B-1.1 + - SGI-B-2.0 + - Unicode-DFS-2015 + - Unicode-DFS-2016 + - Unicode-TOU + - UPL-1.0 + - W3C-19980720 + - W3C-20150513 + - W3C + - X11 + - Xnet + - Zend-2.0 + - zlib-acknowledgement + - Zlib + - ZPL-1.1 + - ZPL-2.0 + - ZPL-2.1 + permissive: + - WTFPL + reciprocal: + - APSL-1.0 + - APSL-1.1 + - APSL-1.2 + - APSL-2.0 + - CDDL-1.0 + - CDDL-1.1 + - CPL-1.0 + - EPL-1.0 + - EPL-2.0 + - FreeImage + - IPL-1.0 + - MPL-1.0 + - MPL-1.1 + - MPL-2.0 + - Ruby + restricted: + - BCL + - CC-BY-ND-1.0 + - CC-BY-ND-2.0 + - CC-BY-ND-2.5 + - CC-BY-ND-3.0 + - CC-BY-ND-4.0 + - CC-BY-SA-1.0 + - CC-BY-SA-2.0 + - CC-BY-SA-2.5 + - CC-BY-SA-3.0 + - CC-BY-SA-4.0 + - GPL-1.0 + - GPL-2.0 + - GPL-2.0-with-autoconf-exception + - GPL-2.0-with-bison-exception + - GPL-2.0-with-classpath-exception + - GPL-2.0-with-font-exception + - GPL-2.0-with-GCC-exception + - GPL-3.0 + - GPL-3.0-with-autoconf-exception + - GPL-3.0-with-GCC-exception + - LGPL-2.0 + - LGPL-2.1 + - LGPL-3.0 + - NPL-1.0 + - NPL-1.1 + - OSL-1.0 + - OSL-1.1 + - OSL-2.0 + - OSL-2.1 + - OSL-3.0 + - QPL-1.0 + - Sleepycat + unencumbered: + - 0BSD + - CC0-1.0 + - Unlicense +list-all-pkgs: true +misconfiguration: + include-non-failures: false + policy-bundle-repository: ghcr.io/aquasecurity/defsec:0 + reset-policy-bundle: false +output: "" +quiet: true +report: all +reset: false +scan: + compliance: "" + file-patterns: [] + offline: false + rekor-url: https://rekor.sigstore.dev + sbom-sources: [] + scanners: + - license + skip-dirs: [] + skip-files: [] + slow: false +secret: + config: trivy-secret.yaml +severity: + # - UNKNOWN + # - LOW + # - MEDIUM + - HIGH + - CRITICAL +timeout: 5m0s diff --git a/trivy-vuln.yaml b/trivy-vuln.yaml new file mode 100644 index 0000000..24068e1 --- /dev/null +++ b/trivy-vuln.yaml @@ -0,0 +1,62 @@ +--- +# This is a "full copy" file. Any manual changes will be overwritten in the next +# sync. If you want to make changes to this file, open a PR against the upstream +# file. Changes will be reflected in ALL repositories during the next sync. + +cache: + backend: fs + clear: false +db: + download-java-only: false + download-only: false + java-repository: ghcr.io/aquasecurity/trivy-java-db + java-skip-update: false + light: false + no-progress: false + repository: ghcr.io/aquasecurity/trivy-db + skip-update: false +# debug: false +dependency-tree: true +exit-code: 1 +format: table +ignore-policy: "" +ignorefile: .trivyignore +include-dev-deps: false +insecure: false +list-all-pkgs: true +misconfiguration: + include-non-failures: false + policy-bundle-repository: ghcr.io/aquasecurity/defsec:0 + reset-policy-bundle: false +output: "" +quiet: true +report: all +reset: false +scan: + compliance: "" + file-patterns: [] + offline: false + rekor-url: https://rekor.sigstore.dev + sbom-sources: [] + scanners: + - vuln + - config + - secret + skip-dirs: [] + skip-files: [] + slow: false +secret: + config: trivy-secret.yaml +severity: + - UNKNOWN + - LOW + - MEDIUM + - HIGH + - CRITICAL +timeout: 5m0s +vulnerability: + ignore-status: [] + ignore-unfixed: true + type: + - os + - library diff --git a/versions.go b/versions.go deleted file mode 100644 index fdd1bd4..0000000 --- a/versions.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "fmt" -) - -func getFriendlyName(osStr, archStr string) string { - var osFriendly string - var archFriendly string - - osFriendly = osStr - archFriendly = archStr - - if osStr == "js" && archStr == "wasm" { - return "JavaScript as WebAssembly" - } - - if osStr == "darwin" && archStr == "arm64" { - return "macOS on Apple Silicon" - } - - if val, ok := OSMap[osStr]; ok { - osFriendly = val - } - - if val, ok := ArchMap[archStr]; ok { - archFriendly = val - } - - return fmt.Sprintf("%s on %s", osFriendly, archFriendly) -} diff --git a/versions_test.go b/versions_test.go deleted file mode 100644 index d50d0bc..0000000 --- a/versions_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import "testing" - -func TestGetFriendlyNameJS(t *testing.T) { - expected := "JavaScript as WebAssembly" - actual := getFriendlyName("js", "wasm") - - if actual != expected { - t.Errorf("Result was `%s` instead of `%s`.", actual, expected) - } -} - -func TestGetFriendlyNameASi(t *testing.T) { - expected := "macOS on Apple Silicon" - actual := getFriendlyName("darwin", "arm64") - - if actual != expected { - t.Errorf("Result was `%s` instead of `%s`.", actual, expected) - } -} - -func TestGetFriendlyNameLinux64(t *testing.T) { - expected := "Linux on Intel (64-bit)" - actual := getFriendlyName("linux", "amd64") - - if actual != expected { - t.Errorf("Result was `%s` instead of `%s`.", actual, expected) - } -} - -func TestGetFriendlyNameNoOS(t *testing.T) { - expected := "illumos on Intel (64-bit)" - actual := getFriendlyName("illumos", "amd64") - - if actual != expected { - t.Errorf("Result was `%s` instead of `%s`.", actual, expected) - } -} - -func TestGetFriendlyNameNoOSArch(t *testing.T) { - expected := "z/OS on System/390 (64-bit)" - actual := getFriendlyName("zos", "s390x") - - if actual != expected { - t.Errorf("Result was `%s` instead of `%s`.", actual, expected) - } -}