diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..fb6d4e2 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,26 @@ +version: 2.1 + +jobs: + ## Regular build running unit tests and linters + build: + working_directory: /go/src/github.com/bartoszmajsak/template-golang + docker: + - image: circleci/golang:1.12.9 + steps: + - checkout + - restore_cache: + keys: + - vendor-cache-{{ checksum "Gopkg.lock" }} + - run: + name: "Runs the build" + command: make build-ci + - save_cache: + key: vendor-cache-{{ checksum "Gopkg.lock" }} + paths: + - ./vendor + +workflows: + version: 2.1 + circleci_build: + jobs: + - build diff --git a/.dependabot/config.yml b/.dependabot/config.yml new file mode 100644 index 0000000..d6ba456 --- /dev/null +++ b/.dependabot/config.yml @@ -0,0 +1,9 @@ +# Configures dependabot dependency updates +# For details, see: https://dependabot.com/docs/config-file/ +version: 1 +update_configs: + - package_manager: "go:dep" + directory: "/" + update_schedule: "weekly" + version_requirement_updates: "increase_versions" + default_reviewers: [ bartoszmajsak ] diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..cb130e1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +; http://editorconfig.org/ + +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +max_line_length = off + +[*.{adoc, md}] +trim_trailing_whitespace = false + +[{*.go, Makefile}] +indent_style = tab +indent_size = 4 diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..2c587a3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,47 @@ +--- +name: Bug report +about: Use this issue type to report anything unusual observed while using this project. +title: '' +labels: bug +assignees: '' + +--- + + + +##### Issue Overview + +Tell us briefly what the problem is about. + +##### Expected Behaviour + +##### Current Behaviour + +##### Steps To Reproduce + +1. [step 1] +2. [step 2] + +##### Additional Information + +Anything relevant to help us resolve the problem. For example: + + * console log or any other relevant information from additional tools such as browser developer tools + * .... + +For long outputs such as stack traces please use HTML5 `
` + +``` +
+ $ uname -a +Darwin Bartoszs-MBP 17.7.0 +Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018 +root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64 +
+``` diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..730db9a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Use this issue type to share your ideas about improving user experience and functionalities. +title: '' +labels: enhancement +assignees: '' + +--- + + + +##### What can be improved + +Tell us briefly what the idea is about. + +##### Why + +Why do you find this important or useful? diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..e0292c8 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ + + +#### Short description of what this resolves: + + +#### Changes proposed in this pull request: + +- +- +- + +Fixes # diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef576e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,128 @@ +# Test results +*ginkgo-test-results.xml + +# Temporary Files +bin/ +build/_output +build/_test +telepresence.log + +# Vendor +vendor/ +.vendor-new/ + +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with 'go test -c' +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +### Vim ### +# swap +.sw[a-p] +.*.sw[a-p] +# session +Session.vim +# temporary +.netrwhist +# auto-generated tag files +tags + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile +projectile-bookmarks.eld + +# directory configuration +.dir-locals.el + +# saveplace +places + +# url cache +url/cache/ + +# cedet +ede-projects.el + +# smex +smex-items + +# company-statistics +company-statistics-cache.el + +# anaconda-mode +anaconda-mode/ + +### VisualStudioCode ### +.vscode +.history + +### JetBrains IDEs (Goland) ### + +.idea/ +*.iws +*.ipr +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + + diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..3c4ddd9 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,65 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 12 + dupl: + threshold: 128 + goconst: + min-len: 2 + min-occurrences: 2 + depguard: + list-type: blacklist + packages: + # logging is allowed only by "sigs.k8s.io/controller-runtime/pkg/runtime/log" + - github.com/sirupsen/logrus + misspell: + locale: US + ignore-words: + - istio + - k8s + lll: + line-length: 180 + goimports: + local-prefixes: github.com/bartoszmajsak/template-golang + gocritic: + enabled-tags: + - performance + - style + - experimental + disabled-checks: + - wrapperFunc + - commentFormatting # https://github.com/go-critic/go-critic/issues/755 + unused: + check-exported: true + +linters: + enable-all: true + disable: + - gochecknoinits # k8s/istio generated APIs are using init() + - gochecknoglobals # TODO discuss + - gofmt # We use goimports and when using them both leads to contradicting errors + +run: + deadline: 10m + skip-dirs: + - ./pkg/apis + - ./pkg/assets/generated + - ./pkg/client/clientset + +issues: + exclude-rules: + - path: pkg/openshift/ + linters: + - dupl + - path: pkg/k8s/ + linters: + - dupl + +service: + project-path: github.com/bartoszmajsak/template-golang + golangci-lint-version: 1.17.x # Locks the version to avoid newly introduces linters + prepare: + - make lint-prepare diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..a6048f1 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,345 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:1b91ae0dc69a41d4c2ed23ea5cffb721ea63f5037ca4b81e6d6771fbb8f45129" + name = "github.com/fsnotify/fsnotify" + packages = ["."] + pruneopts = "NT" + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + version = "v1.4.7" + +[[projects]] + digest = "1:6e9d2128f23114e87a0ac87ce6fd3f51556adfc1a6247c0632fbf44a4c34f846" + name = "github.com/google/go-github" + packages = ["github"] + pruneopts = "NT" + revision = "9686ff0746200cf521ce225525b421e13b4eac1a" + version = "v28.1.1" + +[[projects]] + digest = "1:a63cff6b5d8b95638bfe300385d93b2a6d9d687734b863da8e09dc834510a690" + name = "github.com/google/go-querystring" + packages = ["query"] + pruneopts = "NT" + revision = "44c6ddd0a2342c386950e880b658017258da92fc" + version = "v1.0.0" + +[[projects]] + digest = "1:7313d6b9095eb86581402557bbf3871620cf82adf41853c5b9bee04b894290c7" + name = "github.com/h2non/parth" + packages = ["."] + pruneopts = "NT" + revision = "b4df798d65426f8c8ab5ca5f9987aec5575d26c9" + version = "v2.0.1" + +[[projects]] + digest = "1:f0d9d74edbd40fdeada436d5ac9cb5197407899af3fef85ff0137077ffe8ae19" + name = "github.com/hashicorp/errwrap" + packages = ["."] + pruneopts = "NT" + revision = "8a6fb523712970c966eefc6b39ed2c5e74880354" + version = "v1.0.0" + +[[projects]] + digest = "1:2ed138049ab373f696db2081ca48f15c5abdf20893803612a284f2bdce2bf443" + name = "github.com/hashicorp/go-multierror" + packages = ["."] + pruneopts = "NT" + revision = "886a7fbe3eb1c874d46f623bfa70af45f425b3d1" + version = "v1.0.0" + +[[projects]] + digest = "1:62ba88d64dabadbbad170cf6b0d3bfd1f50c36b1566583a28d99304eb08a9bd2" + name = "github.com/hashicorp/hcl" + packages = [ + ".", + "hcl/ast", + "hcl/parser", + "hcl/printer", + "hcl/scanner", + "hcl/strconv", + "hcl/token", + "json/parser", + "json/scanner", + "json/token", + ] + pruneopts = "NT" + revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" + version = "v1.0.0" + +[[projects]] + digest = "1:76efa3b55850d9caa14f8c0b3a951797f9bc2ffc283526073dcad1b06b6e02d3" + name = "github.com/hpcloud/tail" + packages = [ + ".", + "ratelimiter", + "util", + "watch", + "winfile", + ] + pruneopts = "NT" + revision = "a30252cb686a21eb2d0b98132633053ec2f7f1e5" + version = "v1.0.0" + +[[projects]] + digest = "1:406338ad39ab2e37b7f4452906442a3dbf0eb3379dd1f06aafb5c07e769a5fbb" + name = "github.com/inconshreveable/mousetrap" + packages = ["."] + pruneopts = "NT" + revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" + version = "v1.0" + +[[projects]] + digest = "1:fecb18d8d2d2b9964ea3ac289f05e86615e2f90842063d4871e9d87049b61ffa" + name = "github.com/magiconair/properties" + packages = ["."] + pruneopts = "NT" + revision = "de8848e004dd33dc07a2947b3d76f618a7fc7ef1" + version = "v1.8.1" + +[[projects]] + digest = "1:a45ae66dea4c899d79fceb116accfa1892105c251f0dcd9a217ddc276b42ec68" + name = "github.com/mitchellh/mapstructure" + packages = ["."] + pruneopts = "NT" + revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" + version = "v1.1.2" + +[[projects]] + digest = "1:b19a2d6df8585d12467dd8058e49d103b0f86ed92709ec26d33cd31932e9dd9f" + name = "github.com/onsi/ginkgo" + packages = [ + ".", + "config", + "internal/codelocation", + "internal/containernode", + "internal/failer", + "internal/leafnodes", + "internal/remote", + "internal/spec", + "internal/spec_iterator", + "internal/specrunner", + "internal/suite", + "internal/testingtproxy", + "internal/writer", + "reporters", + "reporters/stenographer", + "reporters/stenographer/support/go-colorable", + "reporters/stenographer/support/go-isatty", + "types", + ] + pruneopts = "NT" + revision = "974566c482abc93ffd3df6f4626e79076c7ed290" + version = "v1.10.1" + +[[projects]] + digest = "1:2ea4049c70f105105777583d180b3ff3f27a970d07230882c45590793919ed3a" + name = "github.com/onsi/gomega" + packages = [ + ".", + "format", + "internal/assertion", + "internal/asyncassertion", + "internal/oraclematcher", + "internal/testingtsupport", + "matchers", + "matchers/support/goraph/bipartitegraph", + "matchers/support/goraph/edge", + "matchers/support/goraph/node", + "matchers/support/goraph/util", + "types", + ] + pruneopts = "NT" + revision = "bdebf9e0ece900259084cfa4121b97ce1a540939" + version = "v1.7.0" + +[[projects]] + digest = "1:5e2ce9b90fcb9747d5ead0fefec4221c898ee7e2f6516ad7590bd999c808b834" + name = "github.com/pelletier/go-toml" + packages = ["."] + pruneopts = "NT" + revision = "728039f679cbcd4f6a54e080d2219a4c4928c546" + version = "v1.4.0" + +[[projects]] + digest = "1:49b6e0d199dc20969bf7c9d14647313e1dd1b102178fad4ca999d8a9584edfab" + name = "github.com/spf13/afero" + packages = [ + ".", + "mem", + ] + pruneopts = "NT" + revision = "588a75ec4f32903aa5e39a2619ba6a4631e28424" + version = "v1.2.2" + +[[projects]] + digest = "1:c5e6b121ef3d2043505edaf4c80e5a008cec2513dc8804795eb0479d1555bcf7" + name = "github.com/spf13/cast" + packages = ["."] + pruneopts = "NT" + revision = "8c9545af88b134710ab1cd196795e7f2388358d7" + version = "v1.3.0" + +[[projects]] + digest = "1:37a91e5093cb260946900c75198c293f3439187e43f48e1a3ae10b4aa55bd7ad" + name = "github.com/spf13/cobra" + packages = ["."] + pruneopts = "NT" + revision = "f2b07da1e2c38d5f12845a4f607e2e1018cbb1f5" + version = "v0.0.5" + +[[projects]] + digest = "1:3d72352adb74e79d6d5a43d6f51bfd2d0bd0c9b5f3c00cf5a4b1636d8d3b9d92" + name = "github.com/spf13/jwalterweatherman" + packages = ["."] + pruneopts = "NT" + revision = "94f6ae3ed3bceceafa716478c5fbf8d29ca601a1" + version = "v1.1.0" + +[[projects]] + digest = "1:9d8420bbf131d1618bde6530af37c3799340d3762cc47210c1d9532a4c3a2779" + name = "github.com/spf13/pflag" + packages = ["."] + pruneopts = "NT" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" + +[[projects]] + digest = "1:8a2c8828a6fc03841448c29c80eeb6c6f2e2b22c2ddcb93d125bf7dac56c136b" + name = "github.com/spf13/viper" + packages = ["."] + pruneopts = "NT" + revision = "b5bf975e5823809fb22c7644d008757f78a4259e" + version = "v1.4.0" + +[[projects]] + branch = "master" + digest = "1:47437e18d25c3559e119434fe8b67346b217397c4106d6e1660515f2d0745a79" + name = "go.uber.org/goleak" + packages = [ + ".", + "internal/stack", + ] + pruneopts = "NT" + revision = "227bd74c34825787d52452ecf0916929863b787c" + +[[projects]] + branch = "master" + digest = "1:0a93ab1b9f6515272587680f1743cc02f3b140b59f29b34816844856bc9c1e2b" + name = "golang.org/x/crypto" + packages = [ + "cast5", + "openpgp", + "openpgp/armor", + "openpgp/elgamal", + "openpgp/errors", + "openpgp/packet", + "openpgp/s2k", + ] + pruneopts = "NT" + revision = "227b76d455e791cb042b03e633e2f7fbcfdf74a5" + +[[projects]] + branch = "master" + digest = "1:d52ab007568fe1a0a2721dd27e7784eec0e16cd6f60b1d073d0d6b2bccef7bfd" + name = "golang.org/x/net" + packages = [ + "context", + "html", + "html/atom", + "html/charset", + ] + pruneopts = "NT" + revision = "c8589233b77dde5edd2205ba8a4fb5c9c2472556" + +[[projects]] + branch = "master" + digest = "1:6d271769f4cbc4a05e63dd169ff02b2171dbc7ba2b4ad3e24354a1bfa5aed336" + name = "golang.org/x/sys" + packages = ["unix"] + pruneopts = "NT" + revision = "c3b328c6e5a763a2bfc82a48b4bd855037210934" + +[[projects]] + digest = "1:348fa8283a7c60b5b71ce04d27b37f7c0fce552d4d0b463b5b3ebbd1840d3f1a" + name = "golang.org/x/text" + packages = [ + "encoding", + "encoding/charmap", + "encoding/htmlindex", + "encoding/internal", + "encoding/internal/identifier", + "encoding/japanese", + "encoding/korean", + "encoding/simplifiedchinese", + "encoding/traditionalchinese", + "encoding/unicode", + "internal/gen", + "internal/language", + "internal/language/compact", + "internal/tag", + "internal/triegen", + "internal/ucd", + "internal/utf8internal", + "language", + "runes", + "transform", + "unicode/cldr", + "unicode/norm", + ] + pruneopts = "NT" + revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475" + version = "v0.3.2" + +[[projects]] + digest = "1:1b91ae0dc69a41d4c2ed23ea5cffb721ea63f5037ca4b81e6d6771fbb8f45129" + name = "gopkg.in/fsnotify.v1" + packages = ["."] + pruneopts = "NT" + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + source = "https://github.com/fsnotify/fsnotify.git" + version = "v1.4.7" + +[[projects]] + digest = "1:721323966f12bfa5186e02ad807424f78a28b04606970105dbf6a5e9b62205e7" + name = "gopkg.in/h2non/gock.v1" + packages = ["."] + pruneopts = "NT" + revision = "3ffff9b1aa8200275a5eb219c5f9c62bd27acb31" + version = "v1.0.15" + +[[projects]] + branch = "v1" + digest = "1:8fb1ccb16a6cfecbfdfeb84d8ea1cc7afa8f9ef16526bc2326f72d993e32cef1" + name = "gopkg.in/tomb.v1" + packages = ["."] + pruneopts = "NT" + revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" + +[[projects]] + digest = "1:18108594151654e9e696b27b181b953f9a90b16bf14d253dd1b397b025a1487f" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "NT" + revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" + version = "v2.2.2" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/google/go-github/github", + "github.com/hashicorp/go-multierror", + "github.com/onsi/ginkgo", + "github.com/onsi/ginkgo/reporters", + "github.com/onsi/gomega", + "github.com/spf13/cobra", + "github.com/spf13/pflag", + "github.com/spf13/viper", + "go.uber.org/goleak", + "golang.org/x/net/context", + "gopkg.in/h2non/gock.v1", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..4ceb279 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,40 @@ +[prune] + go-tests = true + non-go = true + +[[constraint]] + name = "github.com/spf13/viper" + version = "=v1.4.0" + +[[constraint]] + name = "github.com/spf13/cobra" + version = "=v0.0.5" + +[[constraint]] + name = "github.com/google/go-github" + version = "=v28.1.1" + +# Workaround for https://github.com/golang/dep/issues/1799 +# Otherwise ginkgo/gomega won't be pulled in +[[override]] + name = "gopkg.in/fsnotify.v1" + source = "https://github.com/fsnotify/fsnotify.git" + +[[constraint]] + name = "github.com/onsi/gomega" + version = "=1.7.0" + +[[constraint]] + name = "github.com/onsi/ginkgo" + version = "=1.10.1" + +[[constraint]] + name = "go.uber.org/goleak" + branch = "master" + +[[constraint]] + name = "gopkg.in/h2non/gock.v1" + version = "=v1.0.15" + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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/Makefile b/Makefile new file mode 100644 index 0000000..595ec6d --- /dev/null +++ b/Makefile @@ -0,0 +1,112 @@ +PROJECT_NAME:=template-golang +PACKAGE_NAME:=github.com/bartoszmajsak/$(PROJECT_NAME) + +PROJECT_DIR:=$(shell pwd) +BUILD_DIR:=$(PROJECT_DIR)/build +BINARY_DIR:=$(PROJECT_DIR)/dist +BINARY_NAME:=binary + +# Call this function with $(call header,"Your message") to see underscored green text +define header = +@echo -e "\n\e[92m\e[4m\e[1m$(1)\e[0m\n" +endef + +##@ Default target (all you need - just run "make") +.DEFAULT_GOAL:=all +.PHONY: all +all: deps format lint compile test ## Runs 'deps format lint test compile' targets + +##@ Build + +.PHONY: build-ci +build-ci: deps format compile test # Like 'all', but without linter which is executed as separated PR check + +.PHONY: compile +compile: $(BINARY_DIR)/$(BINARY_NAME) ## Compiles binaries + +.PHONY: test +test: ## Runs tests + $(call header,"Running tests") + ginkgo -r -v ${args} + +.PHONY: clean +clean: ## Removes build artifacts + rm -rf $(BINARY_DIR) $(PROJECT_DIR)/bin/ + +.PHONY: deps +deps: check-tools ## Fetches all dependencies + $(call header,"Fetching dependencies") + dep ensure -v + +.PHONY: format +format: ## Removes unneeded imports and formats source code + $(call header,"Formatting code") + goimports -l -w ./pkg/ ./cmd/ ./test/ + +.PHONY: lint-prepare +lint-prepare: deps + +.PHONY: lint +lint: lint-prepare ## Concurrently runs a whole bunch of static analysis tools + $(call header,"Running a whole bunch of static analysis tools") + golangci-lint run + +# ########################################################################## +# Build configuration +# ########################################################################## + +OS:=$(shell uname -s) +GOOS?=$(shell echo $(OS) | awk '{print tolower($$0)}') +GOARCH:=amd64 + +BUILD_TIME=$(shell date -u '+%Y-%m-%dT%H:%M:%SZ') +GITUNTRACKEDCHANGES:=$(shell git status --porcelain --untracked-files=no) +COMMIT:=$(shell git rev-parse --short HEAD) +ifneq ($(GITUNTRACKEDCHANGES),) + COMMIT:=$(COMMIT)-dirty +endif + +BINARY_VERSION?=$(shell git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") +GIT_TAG:=$(shell git describe --tags --abbrev=0 --exact-match > /dev/null 2>&1; echo $$?) +ifneq ($(GIT_TAG),0) + BINARY_VERSION:=$(BINARY_VERSION)-next-$(COMMIT) +else ifneq ($(GITUNTRACKEDCHANGES),) + BINARY_VERSION:=$(BINARY_VERSION)-dirty +endif + +GOBUILD:=GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0 +RELEASE?=false +LDFLAGS="-w -X ${PACKAGE_NAME}/version.Release=${RELEASE} -X ${PACKAGE_NAME}/version.Version=${BINARY_VERSION} -X ${PACKAGE_NAME}/version.Commit=${COMMIT} -X ${PACKAGE_NAME}/version.BuildTime=${BUILD_TIME}" +SRCS=$(shell find ./pkg -name "*.go") $(shell find ./cmd -name "*.go") $(shell find ./version -name "*.go") + +$(BINARY_DIR): + [ -d $@ ] || mkdir -p $@ + +$(BINARY_DIR)/$(BINARY_NAME): $(BINARY_DIR) $(SRCS) + $(call header,"Compiling... carry on!") + ${GOBUILD} go build -ldflags ${LDFLAGS} -o $@ ./cmd + +##@ Setup + +.PHONY: tools +tools: ## Installs required go tools + $(call header,"Installing required tools") + go get -u github.com/golang/dep/cmd/dep + go get -u github.com/golangci/golangci-lint/cmd/golangci-lint + go get -u golang.org/x/tools/cmd/goimports + go get -u github.com/onsi/ginkgo/ginkgo + +EXECUTABLES:=dep golangci-lint goimports ginkgo +CHECK:=$(foreach exec,$(EXECUTABLES),\ + $(if $(shell which $(exec) 2>/dev/null),,"install")) +.PHONY: check-tools +check-tools: + $(call header,"Checking required tools") + @$(if $(strip $(CHECK)),$(MAKE) -f $(THIS_MAKEFILE) tools,echo "'$(EXECUTABLES)' are installed") + +##@ Helpers + +.PHONY: help +help: ## Displays this help \o/ + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-24s\033[0m\033[2m %s\033[0m\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + @cat $(MAKEFILE_LIST) | grep "[A-Z]?=" | sort | awk 'BEGIN {FS="?="; printf "\n\n\033[1mEnvironment variables\033[0m\n"} {printf " \033[36m%-25s\033[0m\033[2m %s\033[0m\n", $$1, $$2}' diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..d9a3b7f --- /dev/null +++ b/README.adoc @@ -0,0 +1,20 @@ += Template Repository - Golang + +Basic structure for golang projects. + +Code: + +- Folder structure +- Sample `dep` configurtion +- Basic `Makefile` +- `.editorconfig` +- `.gitignore` + +Services: + +- CircleCI +- GolangCI linter +- Dependabot + + +GitHub issues / pr templates (don't forget to customize the content!) diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..a35e7fe --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "fmt" + "strings" + "time" + + "github.com/bartoszmajsak/template-golang/pkg/cmd/version" + "github.com/bartoszmajsak/template-golang/pkg/config" + "github.com/bartoszmajsak/template-golang/pkg/format" + v "github.com/bartoszmajsak/template-golang/version" + + "github.com/spf13/cobra" +) + +func main() { + rootCmd := newCmd() + + rootCmd.AddCommand(version.NewCmd()) + + if err := rootCmd.Execute(); err != nil { + panic(err) + } +} + +func newCmd() *cobra.Command { + var configFile string + releaseInfo := make(chan string, 1) + + rootCmd := &cobra.Command{ + Use: "cmd", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { //nolint[:unparam] + if v.Released() { + go func() { + latestRelease, _ := version.LatestRelease() + if !version.IsLatestRelease(latestRelease) { + releaseInfo <- fmt.Sprintf("WARN: you are using %s which is not the latest release (newest is %s).\n"+ + "Follow release notes for update info https://github.com/Maistra/istio-workspace/releases/latest", v.Version, latestRelease) + } else { + releaseInfo <- "" + } + }() + } + return config.SetupConfigSources(configFile, cmd.Flag("config").Changed) + }, + RunE: func(cmd *cobra.Command, args []string) error { //nolint[:unparam] + shouldPrintVersion, _ := cmd.Flags().GetBool("version") + if shouldPrintVersion { + version.PrintVersion() + } else { + fmt.Print(cmd.UsageString()) + } + return nil + }, + PersistentPostRunE: func(cmd *cobra.Command, args []string) error { + if v.Released() { + timer := time.NewTimer(2 * time.Second) + select { + case release := <-releaseInfo: + fmt.Println(release) + case <-timer.C: + // do nothing, just timeout + } + } + close(releaseInfo) + return nil + }, + } + + rootCmd.PersistentFlags(). + StringVarP(&configFile, "config", "c", ".ike.config.yaml", + fmt.Sprintf("config file (supported formats: %s)", strings.Join(config.SupportedExtensions(), ", "))) + rootCmd.Flags().Bool("version", false, "prints the version number of ike cli") + rootCmd.PersistentFlags().String("help-format", "standard", "prints help in asciidoc table") + if err := rootCmd.PersistentFlags().MarkHidden("help-format"); err != nil { + fmt.Printf("failed while trying to hide a flag: %s\n", err) + } + + format.EnhanceHelper(rootCmd) + format.RegisterTemplateFuncs() + + return rootCmd +} diff --git a/init.sh b/init.sh new file mode 100755 index 0000000..ec8ac63 --- /dev/null +++ b/init.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +die () { + echo >&2 "$@" + exit 1 +} + +defaultName=$(git remote -v | cut -d':' -f 2 | cut -d'.' -f 1 | uniq) +read -p "Repo name (${defaultName}): " name +test -z "$name" && name=${defaultName} + +find . -type f -not -path "./vendor/*" -not -path "./.git/*" -not -path "./init.sh" -exec sed -i "s|bartoszmajsak/template-golang|${name}|g" '{}' \; + +read -p "Project name: " project +test -z "$name" && { + die "You must specify project name" +} +sed -i "s|PROJECT_NAME:=template-golang|PROJECT_NAME:=${project}|g" Makefile + +read -p "Executable name: " binary +test -z "$name" && { + die "You must specify executable name" +} +sed -i "s|PROJECT_NAME:=template-golang|PROJECT_NAME:=${binary}|g" Makefile diff --git a/pkg/cmd/version/cmd.go b/pkg/cmd/version/cmd.go new file mode 100644 index 0000000..794c577 --- /dev/null +++ b/pkg/cmd/version/cmd.go @@ -0,0 +1,32 @@ +package version + +import ( + "fmt" + "runtime" + + "github.com/bartoszmajsak/template-golang/version" + + "github.com/spf13/cobra" +) + +// NewCmd creates version cmd which prints version and Build details of the executed binary +func NewCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Prints the version number of tool", + Long: "All software has versions. This is ours", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { //nolint[:unparam] + PrintVersion() + return nil + }, + } +} + +func PrintVersion() { + fmt.Printf("Binary Version: %s\n", version.Version) + fmt.Printf("Go Version: %s\n", runtime.Version()) + fmt.Printf("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH) + fmt.Printf("Build Commit: %v\n", version.Commit) + fmt.Printf("Build Time: %v\n", version.BuildTime) +} diff --git a/pkg/cmd/version/fixtures/latest_release_is_v.0.0.2.json b/pkg/cmd/version/fixtures/latest_release_is_v.0.0.2.json new file mode 100644 index 0000000..a5c8d37 --- /dev/null +++ b/pkg/cmd/version/fixtures/latest_release_is_v.0.0.2.json @@ -0,0 +1,210 @@ +{ + "url": "https://api.github.com/repos/bartoszmajsak/template-golang/releases/18367686", + "assets_url": "https://api.github.com/repos/bartoszmajsak/template-golang/releases/18367686/assets", + "upload_url": "https://uploads.github.com/repos/bartoszmajsak/template-golang/releases/18367686/assets{?name,label}", + "html_url": "https://github.com/bartoszmajsak/template-golang/releases/tag/v0.0.2", + "id": 18367686, + "node_id": "MDc6UmVsZWFzZTE4MzY3Njg2", + "tag_name": "v0.0.2", + "target_commitish": "master", + "name": "v0.0.2", + "draft": false, + "author": { + "login": "bartoszmajsak", + "id": 719616, + "node_id": "MDQ6VXNlcjcxOTYxNg==", + "avatar_url": "https://avatars1.githubusercontent.com/u/719616?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/bartoszmajsak", + "html_url": "https://github.com/bartoszmajsak", + "followers_url": "https://api.github.com/users/bartoszmajsak/followers", + "following_url": "https://api.github.com/users/bartoszmajsak/following{/other_user}", + "gists_url": "https://api.github.com/users/bartoszmajsak/gists{/gist_id}", + "starred_url": "https://api.github.com/users/bartoszmajsak/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/bartoszmajsak/subscriptions", + "organizations_url": "https://api.github.com/users/bartoszmajsak/orgs", + "repos_url": "https://api.github.com/users/bartoszmajsak/repos", + "events_url": "https://api.github.com/users/bartoszmajsak/events{/privacy}", + "received_events_url": "https://api.github.com/users/bartoszmajsak/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": false, + "created_at": "2019-07-02T18:03:17Z", + "published_at": "2019-07-02T18:31:57Z", + "assets": [ + { + "url": "https://api.github.com/repos/bartoszmajsak/template-golang/releases/assets/13516502", + "id": 13516502, + "node_id": "MDEyOlJlbGVhc2VBc3NldDEzNTE2NTAy", + "name": "checksums.txt", + "label": "", + "uploader": { + "login": "bartoszmajsak", + "id": 719616, + "node_id": "MDQ6VXNlcjcxOTYxNg==", + "avatar_url": "https://avatars1.githubusercontent.com/u/719616?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/bartoszmajsak", + "html_url": "https://github.com/bartoszmajsak", + "followers_url": "https://api.github.com/users/bartoszmajsak/followers", + "following_url": "https://api.github.com/users/bartoszmajsak/following{/other_user}", + "gists_url": "https://api.github.com/users/bartoszmajsak/gists{/gist_id}", + "starred_url": "https://api.github.com/users/bartoszmajsak/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/bartoszmajsak/subscriptions", + "organizations_url": "https://api.github.com/users/bartoszmajsak/orgs", + "repos_url": "https://api.github.com/users/bartoszmajsak/repos", + "events_url": "https://api.github.com/users/bartoszmajsak/events{/privacy}", + "received_events_url": "https://api.github.com/users/bartoszmajsak/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "text/plain; charset=utf-8", + "state": "uploaded", + "size": 382, + "download_count": 0, + "created_at": "2019-07-02T18:31:57Z", + "updated_at": "2019-07-02T18:31:57Z", + "browser_download_url": "https://github.com/bartoszmajsak/template-golang/releases/download/v0.0.2/checksums.txt" + }, + { + "url": "https://api.github.com/repos/bartoszmajsak/template-golang/releases/assets/13516504", + "id": 13516504, + "node_id": "MDEyOlJlbGVhc2VBc3NldDEzNTE2NTA0", + "name": "ike_0.0.1_Darwin_i386.tar.gz", + "label": "", + "uploader": { + "login": "bartoszmajsak", + "id": 719616, + "node_id": "MDQ6VXNlcjcxOTYxNg==", + "avatar_url": "https://avatars1.githubusercontent.com/u/719616?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/bartoszmajsak", + "html_url": "https://github.com/bartoszmajsak", + "followers_url": "https://api.github.com/users/bartoszmajsak/followers", + "following_url": "https://api.github.com/users/bartoszmajsak/following{/other_user}", + "gists_url": "https://api.github.com/users/bartoszmajsak/gists{/gist_id}", + "starred_url": "https://api.github.com/users/bartoszmajsak/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/bartoszmajsak/subscriptions", + "organizations_url": "https://api.github.com/users/bartoszmajsak/orgs", + "repos_url": "https://api.github.com/users/bartoszmajsak/repos", + "events_url": "https://api.github.com/users/bartoszmajsak/events{/privacy}", + "received_events_url": "https://api.github.com/users/bartoszmajsak/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/gzip", + "state": "uploaded", + "size": 11306269, + "download_count": 0, + "created_at": "2019-07-02T18:31:57Z", + "updated_at": "2019-07-02T18:31:58Z", + "browser_download_url": "https://github.com/bartoszmajsak/template-golang/releases/download/v0.0.2/ike_0.0.1_Darwin_i386.tar.gz" + }, + { + "url": "https://api.github.com/repos/bartoszmajsak/template-golang/releases/assets/13516505", + "id": 13516505, + "node_id": "MDEyOlJlbGVhc2VBc3NldDEzNTE2NTA1", + "name": "ike_0.0.1_Darwin_x86_64.tar.gz", + "label": "", + "uploader": { + "login": "bartoszmajsak", + "id": 719616, + "node_id": "MDQ6VXNlcjcxOTYxNg==", + "avatar_url": "https://avatars1.githubusercontent.com/u/719616?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/bartoszmajsak", + "html_url": "https://github.com/bartoszmajsak", + "followers_url": "https://api.github.com/users/bartoszmajsak/followers", + "following_url": "https://api.github.com/users/bartoszmajsak/following{/other_user}", + "gists_url": "https://api.github.com/users/bartoszmajsak/gists{/gist_id}", + "starred_url": "https://api.github.com/users/bartoszmajsak/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/bartoszmajsak/subscriptions", + "organizations_url": "https://api.github.com/users/bartoszmajsak/orgs", + "repos_url": "https://api.github.com/users/bartoszmajsak/repos", + "events_url": "https://api.github.com/users/bartoszmajsak/events{/privacy}", + "received_events_url": "https://api.github.com/users/bartoszmajsak/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/gzip", + "state": "uploaded", + "size": 11888121, + "download_count": 0, + "created_at": "2019-07-02T18:31:57Z", + "updated_at": "2019-07-02T18:31:58Z", + "browser_download_url": "https://github.com/bartoszmajsak/template-golang/releases/download/v0.0.2/ike_0.0.1_Darwin_x86_64.tar.gz" + }, + { + "url": "https://api.github.com/repos/bartoszmajsak/template-golang/releases/assets/13516501", + "id": 13516501, + "node_id": "MDEyOlJlbGVhc2VBc3NldDEzNTE2NTAx", + "name": "ike_0.0.1_Linux_i386.tar.gz", + "label": "", + "uploader": { + "login": "bartoszmajsak", + "id": 719616, + "node_id": "MDQ6VXNlcjcxOTYxNg==", + "avatar_url": "https://avatars1.githubusercontent.com/u/719616?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/bartoszmajsak", + "html_url": "https://github.com/bartoszmajsak", + "followers_url": "https://api.github.com/users/bartoszmajsak/followers", + "following_url": "https://api.github.com/users/bartoszmajsak/following{/other_user}", + "gists_url": "https://api.github.com/users/bartoszmajsak/gists{/gist_id}", + "starred_url": "https://api.github.com/users/bartoszmajsak/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/bartoszmajsak/subscriptions", + "organizations_url": "https://api.github.com/users/bartoszmajsak/orgs", + "repos_url": "https://api.github.com/users/bartoszmajsak/repos", + "events_url": "https://api.github.com/users/bartoszmajsak/events{/privacy}", + "received_events_url": "https://api.github.com/users/bartoszmajsak/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/gzip", + "state": "uploaded", + "size": 10713509, + "download_count": 0, + "created_at": "2019-07-02T18:31:57Z", + "updated_at": "2019-07-02T18:31:58Z", + "browser_download_url": "https://github.com/bartoszmajsak/template-golang/releases/download/v0.0.2/ike_0.0.1_Linux_i386.tar.gz" + }, + { + "url": "https://api.github.com/repos/bartoszmajsak/template-golang/releases/assets/13516503", + "id": 13516503, + "node_id": "MDEyOlJlbGVhc2VBc3NldDEzNTE2NTAz", + "name": "ike_0.0.1_Linux_x86_64.tar.gz", + "label": "", + "uploader": { + "login": "bartoszmajsak", + "id": 719616, + "node_id": "MDQ6VXNlcjcxOTYxNg==", + "avatar_url": "https://avatars1.githubusercontent.com/u/719616?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/bartoszmajsak", + "html_url": "https://github.com/bartoszmajsak", + "followers_url": "https://api.github.com/users/bartoszmajsak/followers", + "following_url": "https://api.github.com/users/bartoszmajsak/following{/other_user}", + "gists_url": "https://api.github.com/users/bartoszmajsak/gists{/gist_id}", + "starred_url": "https://api.github.com/users/bartoszmajsak/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/bartoszmajsak/subscriptions", + "organizations_url": "https://api.github.com/users/bartoszmajsak/orgs", + "repos_url": "https://api.github.com/users/bartoszmajsak/repos", + "events_url": "https://api.github.com/users/bartoszmajsak/events{/privacy}", + "received_events_url": "https://api.github.com/users/bartoszmajsak/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/gzip", + "state": "uploaded", + "size": 11307955, + "download_count": 37, + "created_at": "2019-07-02T18:31:57Z", + "updated_at": "2019-07-02T18:31:58Z", + "browser_download_url": "https://github.com/bartoszmajsak/template-golang/releases/download/v0.0.2/ike_0.0.1_Linux_x86_64.tar.gz" + } + ], + "tarball_url": "https://api.github.com/repos/bartoszmajsak/template-golang/tarball/v0.0.2", + "zipball_url": "https://api.github.com/repos/bartoszmajsak/template-golang/zipball/v0.0.2", + "body": "# Highlights of v0.0.2 release\n\nInspired by @bobbytables talk from [OSCON 2018](https://youtu.be/yhvR02UofZE) we started hacking on open source tooling realizing the idea of safe development on production.\nNow, after long weeks in the making, we are extremely happy to announce the first release of `istio-workspace` project.\n\nWith this toolkit, we strive to enable developers to:\n\n> Develop their microservices locally, using the tools they love!\n>\n> Connect to other services in the cluster and test their changes!\n>\n> All of that without interfering with other developers and users!\n\nAll of it based on [CNCF](https://www.cncf.io/) open source projects such as [Istio](https://istio.io) and [Telepresence](https://telepresence.io).\n\n## How does it work?\n\n- `ike develop` wraps [Telepresence](https://telepresence.io) and let you start local development while being connected to the real services in the cluster\n\n - in addition, it will restart the process when any files have been changed (see `--watch` flag)\n\n- `istio-workspace` operator takes care of configuring routes in Istio so that only you can hit your service under development\n\nTo learn more head over to the [official docs](https://istio-workspace-docs.netlify.com/istio-workspace/v0.0.2/index.html).\n\n" +} diff --git a/pkg/cmd/version/releases.go b/pkg/cmd/version/releases.go new file mode 100644 index 0000000..02ba31a --- /dev/null +++ b/pkg/cmd/version/releases.go @@ -0,0 +1,27 @@ +package version + +import ( + "net/http" + + "github.com/bartoszmajsak/template-golang/version" + + "github.com/google/go-github/github" + "golang.org/x/net/context" +) + +func LatestRelease() (string, error) { + httpClient := http.Client{} + defer httpClient.CloseIdleConnections() + + client := github.NewClient(&httpClient) + latestRelease, _, err := client.Repositories. + GetLatestRelease(context.Background(), "bartoszmajsak", "template-golang") + if err != nil { + return "", err + } + return *latestRelease.Name, nil +} + +func IsLatestRelease(latestRelease string) bool { + return latestRelease == version.Version +} diff --git a/pkg/cmd/version/releases_test.go b/pkg/cmd/version/releases_test.go new file mode 100644 index 0000000..de7bd15 --- /dev/null +++ b/pkg/cmd/version/releases_test.go @@ -0,0 +1,60 @@ +package version_test + +import ( + "gopkg.in/h2non/gock.v1" + + "github.com/bartoszmajsak/template-golang/pkg/cmd/version" + v "github.com/bartoszmajsak/template-golang/version" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Fetching latest release", func() { + + BeforeEach(func() { + gock.New("https://api.github.com"). + Get("/repos/bartoszmajsak/template-golang/releases/latest"). + Reply(200). + File("fixtures/latest_release_is_v.0.0.2.json") + }) + + AfterEach(func() { + gock.Off() + }) + + It("should get latest release", func() { + // when + release, err := version.LatestRelease() + + // then + Expect(err).ToNot(HaveOccurred()) + Expect(release).To(Equal("v0.0.2")) + }) + + It("should determine that v0.0.0 is not latest release", func() { + // given + latestRelease, err := version.LatestRelease() + Expect(err).ToNot(HaveOccurred()) + + // when + latest := version.IsLatestRelease(latestRelease) + + // then + Expect(latest).To(BeFalse()) + }) + + It("should determine that v0.0.2 is latest release", func() { + // given + v.Version = "v0.0.2" + latestRelease, err := version.LatestRelease() + Expect(err).ToNot(HaveOccurred()) + + // when + latest := version.IsLatestRelease(latestRelease) + + // then + Expect(latest).To(BeTrue()) + }) + +}) diff --git a/pkg/cmd/version/version_suite_test.go b/pkg/cmd/version/version_suite_test.go new file mode 100644 index 0000000..e15663d --- /dev/null +++ b/pkg/cmd/version/version_suite_test.go @@ -0,0 +1,23 @@ +package version_test + +import ( + "testing" + + "go.uber.org/goleak" + + . "github.com/bartoszmajsak/template-golang/test" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestVersion(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecWithJUnitReporter(t, "Version Suite") +} + +var _ = SynchronizedAfterSuite(func() {}, func() { + goleak.VerifyNone(GinkgoT(), + goleak.IgnoreTopFunction("github.com/bartoszmajsak/template-golang/vendor/github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).registerForInterrupts"), + ) +}) diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..7bc49ad --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,110 @@ +package config + +import ( + "fmt" + "path" + "strings" + + "github.com/hashicorp/go-multierror" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" +) + +// SetupConfigSources sets up Viper configuration. +// +// If specific file path is provided but fails when loading it will return an error. +// +// In case of default config location it will not fail if file does not exist, +// but will in any other case such as parse error. +// +// Config precedence (each item takes precedence over the item below it): +// . Flags +// . Env variables +// . Config file +// +// Environment variables are prefixed with `IKE` and have fully qualified names, for example +// in case of `develop` command and its `port` flag corresponding environment variable is +// `IKE_DEVELOP_PORT`. +func SetupConfigSources(configFile string, notDefault bool) error { + viper.Reset() + viper.SetEnvPrefix("BINARY_NAME") + viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.SetTypeByDefaultValue(true) + + ext := path.Ext(configFile) + viper.SetConfigName(strings.TrimSuffix(path.Base(configFile), ext)) + if !contains(SupportedExtensions(), strings.TrimPrefix(path.Ext(ext), ".")) { + return fmt.Errorf("'%s' extension is not supported. Use one of [%s]", ext, strings.Join(SupportedExtensions(), ", ")) + } + viper.SetConfigType(ext[1:]) + viper.AddConfigPath(path.Dir(configFile)) + + if err := viper.ReadInConfig(); err != nil { + if notDefault { + return err + } + + if _, fileDoesNotExist := err.(viper.ConfigFileNotFoundError); !fileDoesNotExist { + return err + } + } + return nil +} + +// SupportedExtensions returns a slice of all supported config format (as file extensions) +func SupportedExtensions() []string { + return viper.SupportedExts +} + +func contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +// SyncFlag ensures that if configuration provides a value for a given cmd.flag it will be set back to the flag itself, +// but only if the flag was not set through CLI. +// +// This way we can make flags required but still have their values provided by the configuration source +func SyncFlag(cmd *cobra.Command, flagName string) error { //nolint[:unused] + value := viper.GetString(cmd.Name() + "." + flagName) + if value != "" && !cmd.Flag(flagName).Changed { + return cmd.Flags().Set(flagName, value) + } + return nil +} + +// SyncFlags ensures that if configuration provide a value for any of defined flags it will be set +// back to the flag itself. +// +// This function iterates over all flags defined for cobra.Command and accumulates errors if they occur while +// calling SyncFlag for every flag. +func SyncFlags(cmd *cobra.Command) error { //nolint[:unused] + var accErrors *multierror.Error + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + syncFlagErr := SyncFlag(cmd, flag.Name) + accErrors = multierror.Append(accErrors, syncFlagErr) + }) + return accErrors.ErrorOrNil() +} + +// BindFullyQualifiedFlag ensures that each flag used in commands is bound to a key using fully qualified name +// which has a following form: +// +// commandName.flagName +// +// This lets us keep structure of yaml file: +// +// commandName: +// flagName: value +func BindFullyQualifiedFlag(cmd *cobra.Command) func(flag *pflag.Flag) { //nolint[:unused] + return func(flag *pflag.Flag) { + _ = viper.BindPFlag(cmd.Name()+"."+flag.Name, flag) + } +} diff --git a/pkg/format/help_format.go b/pkg/format/help_format.go new file mode 100644 index 0000000..7584622 --- /dev/null +++ b/pkg/format/help_format.go @@ -0,0 +1,71 @@ +package format + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// EnhanceHelper wraps helper function with alternative formatting +// and templates (e.g. asciidoc) based on --helper-format flag +// Applies to all subcommands. +// This can be useful when automatically generating documentation for CLI +func EnhanceHelper(command *cobra.Command) { + originalHelpFunc := command.HelpFunc() + command.SetHelpFunc(func(cmd *cobra.Command, i []string) { + defer originalHelpFunc(cmd, i) // always delegate to original helper function + helpFormat, err := cmd.Flags().GetString("help-format") + if err != nil { + return + } + + if helpFormat == "standard" { + return + } + + cmd.SetHelpTemplate(OnlyUsageString) + if helpFormat == "adoc" { + cmd.SetUsageTemplate(ADocHelpTable) + } else { + fmt.Printf("unknown help format: [%s]. using standard one\n", helpFormat) + } + }) +} + +func RegisterTemplateFuncs() { + cobra.AddTemplateFunc("localFlagsSlice", func(set *pflag.FlagSet) []pflag.Flag { + flags := make([]pflag.Flag, 0) + set.VisitAll(func(flag *pflag.Flag) { + if flag.Name != "help" { + flags = append(flags, *flag) + } + }) + return flags + }) + cobra.AddTemplateFunc("type", func(flag *pflag.Flag) string { + flagType := flag.Value.Type() + if strings.Contains(flagType, "Slice") { + return "comma-separated list of " + strings.Replace(flagType, "Slice", "", 1) + "s" + } + return "`" + flagType + "`" + }) + +} + +const ( + OnlyUsageString = "{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}" + ADocHelpTable = `{{if .HasAvailableLocalFlags}}{{ $tick := "` + "`" + `" }} +[cols="2,4,2,1"] +|=== +|Option|Purpose|Format|Default +{{range localFlagsSlice .LocalFlags}}{{ if not .Hidden }} +|{{$tick}}--{{.Name}}{{$tick}} {{if .Shorthand}}({{$tick}}-{{.Shorthand}}{{$tick}}){{end}} +|{{.Usage}} +|{{type .}} +|{{if .DefValue}}{{$tick}}{{.DefValue | trimTrailingWhitespaces}}{{$tick}}{{end}}{{end}} +{{end}} +|=== +{{end}}` +) diff --git a/test/ginkgo_custom_reporter.go b/test/ginkgo_custom_reporter.go new file mode 100644 index 0000000..2953711 --- /dev/null +++ b/test/ginkgo_custom_reporter.go @@ -0,0 +1,14 @@ +package test + +import ( + "testing" + + "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/reporters" +) + +// RunSpecWithJUnitReporter calls custom ginkgo junit reporter +func RunSpecWithJUnitReporter(t *testing.T, description string) { + junitReporter := reporters.NewJUnitReporter("ginkgo-test-results.xml") + ginkgo.RunSpecsWithDefaultAndCustomReporters(t, description, []ginkgo.Reporter{junitReporter}) +} diff --git a/version/version.go b/version/version.go new file mode 100644 index 0000000..8faf93b --- /dev/null +++ b/version/version.go @@ -0,0 +1,19 @@ +package version + +import "strconv" + +var ( + // Version hold a semantic version of the running binary + Version = "v0.0.0" + // Commit holds the commit hash against which the binary build was ran + Commit string + // BuildTime holds timestamp when the binary build was ran + BuildTime string + // Release indicates if built binary is an official release + Release = "false" +) + +func Released() bool { + released, _ := strconv.ParseBool(Release) + return released +}