Skip to content

Commit

Permalink
add docs option generation into the pipeline (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
djeebus authored Aug 11, 2023
1 parent 01f65f1 commit 415b63d
Show file tree
Hide file tree
Showing 13 changed files with 443 additions and 96 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/on_pull-request_docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: docs ci
on:
pull_request:
paths:
- '.github/workflows/on_pull_request_docs.yaml'
- 'Earthfile'
- '*/**.go'
- '*.go'
- 'go.mod'
- 'go.sum'
- 'docs/usage.md*'
jobs:
lint-docs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3

- uses: wistia/[email protected]

- uses: earthly/actions-setup@v1
with: { version: "${{ env.EARTHLY_TOOL_VERSION }}" }

- name: rebuild the docs
run: |
earthly \
+rebuild-docs \
--GOLANG_VERSION="${{ env.GOLANG_TOOL_VERSION }}"
- name: verify that the checked in file has not changed
run: |
#!/usr/bin/env bash
exitCode=0
# Log the actual diff for debugging purposes
git diff --name-only | cat
if ! git diff --exit-code --quiet; then
echo "Please run 'earthly +rebuild-docs' and commit the results to this PR"
exitCode=1
fi
exit $exitCode
3 changes: 0 additions & 3 deletions .github/workflows/on_pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ on:
- '!.gitignore'

# TODO: Move this to repo config
env:
EARTHLY_TOOL_VERSION: 0.7
GOLANG_TOOL_VERSION: 1.19.3
jobs:
build:
runs-on: ubuntu-22.04
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/on_pull_request_go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ on:
- '*.go'
- 'go.mod'
- 'go.sum'
env:
EARTHLY_TOOL_VERSION: 0.7
GOLANG_TOOL_VERSION: 1.19.3
jobs:
ci-golang:
runs-on: ubuntu-22.04
Expand All @@ -22,4 +19,4 @@ jobs:
- uses: earthly/actions-setup@v1
with: { version: "v${{ env.EARTHLY_TOOL_VERSION }}" }

- run: earthly +ci-golang --GOLANG_VERSION=${{ env.GOLANG_TOOL_VERSION }}
- run: earthly +ci-golang --GOLANG_VERSION=${{ env.GOLANG_TOOL_VERSION }}
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
earthly 0.7.12
earthly 0.7.14
golang 1.19.11
helm 3.12.2
helm-ct 3.8.0
Expand Down
13 changes: 10 additions & 3 deletions Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ test:
BUILD +ci-helm

ci-golang:
# This should be enabled at some point
BUILD +lint-golang
BUILD +validate-golang
BUILD +test-golang
Expand Down Expand Up @@ -34,6 +33,14 @@ go-deps:
SAVE ARTIFACT go.mod AS LOCAL go.mod
SAVE ARTIFACT go.sum AS LOCAL go.sum

rebuild-docs:
FROM +go-deps

COPY . /src
RUN go run hacks/env-to-docs.go

SAVE ARTIFACT ./docs/usage.md AS LOCAL ./docs/usage.md

validate-golang:
FROM +go-deps

Expand Down Expand Up @@ -139,13 +146,13 @@ docker-debug:
SAVE IMAGE --push $CI_REGISTRY_IMAGE

lint-golang:
ARG STATICCHECK_VERSION="0.3.3"
ARG STATICCHECK_VERSION="2023.1.3"

FROM +go-deps

# install staticcheck
RUN FILE=staticcheck.tgz \
&& URL=https://github.com/dominikh/go-tools/releases/download/v$STATICCHECK_VERSION/staticcheck_linux_amd64.tar.gz \
&& URL=https://github.com/dominikh/go-tools/releases/download/$STATICCHECK_VERSION/staticcheck_linux_amd64.tar.gz \
&& wget ${URL} \
--output-document ${FILE} \
&& tar \
Expand Down
53 changes: 23 additions & 30 deletions cmd/controller_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import (
"github.com/zapier/kubechecks/pkg/server"
)

// controllerCmd represents the run command
var controllerCmd = &cobra.Command{
// ControllerCmd represents the run command
var ControllerCmd = &cobra.Command{
Use: "controller",
Short: "Start the VCS Webhook handler.",
Long: ``,
Expand Down Expand Up @@ -61,35 +61,28 @@ var controllerCmd = &cobra.Command{
},
}

func panicIfError(err error) {
if err != nil {
panic(err)
}
}

func init() {
rootCmd.AddCommand(controllerCmd)
RootCmd.AddCommand(ControllerCmd)

flags := controllerCmd.Flags()
flags.String("fallback-k8s-version", "1.23.0", "Fallback target Kubernetes version for schema / upgrade checks (KUBECHECKS_FALLBACK_K8S_VERSION).")
flags.Bool("show-debug-info", false, "Set to true to print debug info to the footer of MR comments (KUBECHECKS_SHOW_DEBUG_INFO).")
flags.Bool("enable-conftest", false, "Set to true to enable conftest policy checking of manifests (KUBECHECKS_ENABLE_CONFTEST).")
flags.String("label-filter", "", "(Optional) If set, The label that must be set on an MR (as \"kubechecks:<value>\") for kubechecks to process the merge request webhook (KUBECHECKS_LABEL_FILTER).")
flags.String("openai-api-token", "", "OpenAI API Token (KUBECHECKS_OPENAI_API_TOKEN).")
flags.String("webhook-url-base", "", "The URL where KubeChecks receives webhooks from Gitlab")
flags.String("webhook-url-prefix", "", "If your application is running behind a proxy that uses path based routing, set this value to match the path prefix.")
flags.String("webhook-secret", "", "Optional secret key for validating the source of incoming webhooks.")
flags.Bool("monitor-all-applications", false, "Monitor all applications in argocd automatically")
flags.Bool("ensure-webhooks", false, "Ensure that webhooks are created in repositories referenced by argo")
flags := ControllerCmd.Flags()
stringFlag(flags, "fallback-k8s-version", "Fallback target Kubernetes version for schema / upgrade checks.",
newStringOpts().
withDefault("1.23.0"))
boolFlag(flags, "show-debug-info", "Set to true to print debug info to the footer of MR comments.")
boolFlag(flags, "enable-conftest", "Set to true to enable conftest policy checking of manifests.")
stringFlag(flags, "label-filter", `(Optional) If set, The label that must be set on an MR (as "kubechecks:<value>") for kubechecks to process the merge request webhook.`)
stringFlag(flags, "openai-api-token", "OpenAI API Token.")
stringFlag(flags, "webhook-url-base", "The URL where KubeChecks receives webhooks from Gitlab.")
stringFlag(flags, "webhook-url-prefix", "If your application is running behind a proxy that uses path based routing, set this value to match the path prefix.")
stringFlag(flags, "webhook-secret", "Optional secret key for validating the source of incoming webhooks.")
boolFlag(flags, "monitor-all-applications", "Monitor all applications in argocd automatically.")
boolFlag(flags, "ensure-webhooks", "Ensure that webhooks are created in repositories referenced by argo.")

// Map viper to cobra flags, so we can get these parameters from Environment variables if set.
panicIfError := func(err error) {
if err != nil {
panic(err)
}
}
panicIfError(viper.BindPFlag("enable-conftest", flags.Lookup("enable-conftest")))
panicIfError(viper.BindPFlag("fallback-k8s-version", flags.Lookup("fallback-k8s-version")))
panicIfError(viper.BindPFlag("show-debug-info", flags.Lookup("show-debug-info")))
panicIfError(viper.BindPFlag("label-filter", flags.Lookup("label-filter")))
panicIfError(viper.BindPFlag("openai-api-token", flags.Lookup("openai-api-token")))
panicIfError(viper.BindPFlag("webhook-url-base", flags.Lookup("webhook-url-base")))
panicIfError(viper.BindPFlag("webhook-url-prefix", flags.Lookup("webhook-url-prefix")))
panicIfError(viper.BindPFlag("webhook-secret", flags.Lookup("webhook-secret")))
panicIfError(viper.BindPFlag("ensure-webhooks", flags.Lookup("ensure-webhooks")))
panicIfError(viper.BindPFlag("monitor-all-applications", flags.Lookup("monitor-all-applications")))
panicIfError(viper.BindPFlags(flags))
}
98 changes: 98 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package cmd

import (
"fmt"
"strings"

"github.com/spf13/pflag"
)

type DocOpt[D any] struct {
choices []string
defaultValue *D
shorthand *string
}

func combine[D any](dst *DocOpt[D], src DocOpt[D]) {
if src.choices != nil {
dst.choices = src.choices
}
if src.defaultValue != nil {
dst.defaultValue = src.defaultValue
}
if src.shorthand != nil {
dst.shorthand = src.shorthand
}
}

func ViperNameToEnv(s string) string {
s = envKeyReplacer.Replace(s)
s = fmt.Sprintf("%s_%s", envPrefix, s)
s = strings.ToUpper(s)
return s
}

func boolFlag(flags *pflag.FlagSet, name, usage string, opts ...DocOpt[bool]) {
addFlag(name, usage, opts, flags.Bool, flags.BoolP)
}

func newStringOpts() DocOpt[string] {
return DocOpt[string]{}
}

func stringFlag(flags *pflag.FlagSet, name, usage string, opts ...DocOpt[string]) {
addFlag(name, usage, opts, flags.String, flags.StringP)
}

func addFlag[D any](
name, usage string,
opts []DocOpt[D],
onlyLong func(string, D, string) *D,
longAndShort func(string, string, D, string) *D,
) {
var opt DocOpt[D]
for _, o := range opts {
combine(&opt, o)
}

usage = generateUsage(opt, usage, name)
var defaultValue D
if opt.defaultValue != nil {
defaultValue = *opt.defaultValue
}

if opt.shorthand != nil {
longAndShort(name, *opt.shorthand, defaultValue, usage)
} else {
onlyLong(name, defaultValue, usage)
}
}

func generateUsage[D any](opt DocOpt[D], usage string, name string) string {
if !strings.HasSuffix(usage, ".") {
panic(fmt.Sprintf("usage for %q must end with a period.", name))
}

if opt.choices != nil {
usage = fmt.Sprintf("%s One of %s.", usage, strings.Join(opt.choices, ", "))
}

envVar := ViperNameToEnv(name)
usage = fmt.Sprintf("%s (%s)", usage, envVar)
return usage
}

func (d DocOpt[D]) withDefault(def D) DocOpt[D] {
d.defaultValue = &def
return d
}

func (d DocOpt[D]) withShortHand(short string) DocOpt[D] {
d.shorthand = &short
return d
}

func (d DocOpt[D]) withChoices(choices ...string) DocOpt[D] {
d.choices = choices
return d
}
53 changes: 53 additions & 0 deletions cmd/flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package cmd

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestStringUsages(t *testing.T) {
tests := map[string]struct {
expected string
name string
opt DocOpt[any]
usage string
}{
"string with choices": {
name: "simple-string",
opt: DocOpt[any]{
choices: []string{
"blah",
"test",
},
},
usage: "This is a test.",
expected: "This is a test. One of blah, test. (KUBECHECKS_SIMPLE_STRING)",
},
"string with out of order choices": {
name: "simple-string",
opt: DocOpt[any]{
choices: []string{
"test",
"blah",
},
},
usage: "This is a test.",
expected: "This is a test. One of test, blah. (KUBECHECKS_SIMPLE_STRING)",
},
"string with no choices": {
name: "string",
opt: DocOpt[any]{},
usage: "This is a test.",
expected: "This is a test. (KUBECHECKS_STRING)",
},
}

for testName, test := range tests {
t.Run(testName, func(t *testing.T) {

actual := generateUsage(test.opt, test.usage, test.name)
assert.Equal(t, test.expected, actual)
})
}
}
Loading

0 comments on commit 415b63d

Please sign in to comment.