Skip to content

Commit

Permalink
feat: add options for where input comes from and where output goes to (
Browse files Browse the repository at this point in the history
…akuity#261)

Signed-off-by: Kent Rancourt <kent.rancourt@gmail.com>
  • Loading branch information
krancour authored Mar 22, 2024
1 parent 4812fe0 commit ea602bb
Show file tree
Hide file tree
Showing 17 changed files with 777 additions and 130 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
restore-keys: |
${{ runner.os }}-go-
- name: Run unit tests
env:
TEST_GIT_CLIENT_WITH_AUTH: true
run: make test-unit
- name: Upload coverage reports
uses: codecov/codecov-action@v3
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ ENV XDG_CONFIG_HOME=/tmp/.config
ENV XDG_CACHE_HOME=/tmp/.cache
ENV XDG_DATA_HOME=/tmp/.local/share

CMD ["/usr/local/bin/kargo-render"]
ENTRYPOINT ["/usr/local/bin/kargo-render"]
8 changes: 8 additions & 0 deletions branches.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,18 @@ func switchToTargetBranch(rc requestContext) error {

if targetBranchExists {
logger.Debug("target branch exists on remote")
if err = rc.repo.Fetch(); err != nil {
return fmt.Errorf("error fetching from remote: %w", err)
}
logger.Debug("fetched from remote")
if err = rc.repo.Checkout(rc.request.TargetBranch); err != nil {
return fmt.Errorf("error checking out target branch: %w", err)
}
logger.Debug("checked out target branch")
if err = rc.repo.Pull(rc.request.TargetBranch); err != nil {
return fmt.Errorf("error pulling from remote: %w", err)
}
logger.Debug("pulled from remote")
return nil
}

Expand Down
10 changes: 5 additions & 5 deletions cmd/action_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,24 +87,24 @@ func (o *actionOptions) run(_ context.Context, out io.Writer) error {
return nil
}

func request() (render.Request, error) {
req := render.Request{
func request() (*render.Request, error) {
req := &render.Request{
RepoCreds: render.RepoCredentials{
Username: "git",
},
Images: libOS.GetStringSliceFromEnvVar("INPUT_IMAGES", nil),
}
repo, err := libOS.GetRequiredEnvVar("GITHUB_REPOSITORY")
if err != nil {
return req, err
return nil, err
}
req.RepoURL = fmt.Sprintf("https://github.com/%s", repo)
if req.RepoCreds.Password, err =
libOS.GetRequiredEnvVar("INPUT_PERSONALACCESSTOKEN"); err != nil {
return req, err
return nil, err
}
if req.Ref, err = libOS.GetRequiredEnvVar("GITHUB_SHA"); err != nil {
return req, err
return nil, err
}
req.TargetBranch, err = libOS.GetRequiredEnvVar("INPUT_TARGETBRANCH")
return req, err
Expand Down
14 changes: 7 additions & 7 deletions cmd/action_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestRequest(t *testing.T) {
testImage1 = "krancour/foo:blue"
testImage2 = "krancour/foo:green"
)
testReq := render.Request{
testReq := &render.Request{
RepoURL: fmt.Sprintf("https://github.com/%s", testRepo),
RepoCreds: render.RepoCredentials{
Username: "git",
Expand All @@ -35,11 +35,11 @@ func TestRequest(t *testing.T) {
testCases := []struct {
name string
setup func()
assertions func(*testing.T, render.Request, error)
assertions func(*testing.T, *render.Request, error)
}{
{
name: "GITHUB_REPOSITORY not specified",
assertions: func(t *testing.T, _ render.Request, err error) {
assertions: func(t *testing.T, _ *render.Request, err error) {
require.Error(t, err)
require.Contains(t, err.Error(), "value not found for")
require.Contains(t, err.Error(), "GITHUB_REPOSITORY")
Expand All @@ -50,7 +50,7 @@ func TestRequest(t *testing.T) {
setup: func() {
t.Setenv("GITHUB_REPOSITORY", testRepo)
},
assertions: func(t *testing.T, _ render.Request, err error) {
assertions: func(t *testing.T, _ *render.Request, err error) {
require.Error(t, err)
require.Contains(t, err.Error(), "value not found for")
require.Contains(t, err.Error(), "INPUT_PERSONALACCESSTOKEN")
Expand All @@ -61,7 +61,7 @@ func TestRequest(t *testing.T) {
setup: func() {
t.Setenv("INPUT_PERSONALACCESSTOKEN", testReq.RepoCreds.Password)
},
assertions: func(t *testing.T, _ render.Request, err error) {
assertions: func(t *testing.T, _ *render.Request, err error) {
require.Error(t, err)
require.Contains(t, err.Error(), "value not found for")
require.Contains(t, err.Error(), "GITHUB_SHA")
Expand All @@ -72,7 +72,7 @@ func TestRequest(t *testing.T) {
setup: func() {
t.Setenv("GITHUB_SHA", testReq.Ref)
},
assertions: func(t *testing.T, _ render.Request, err error) {
assertions: func(t *testing.T, _ *render.Request, err error) {
require.Error(t, err)
require.Contains(t, err.Error(), "value not found for")
require.Contains(t, err.Error(), "INPUT_TARGETBRANCH")
Expand All @@ -86,7 +86,7 @@ func TestRequest(t *testing.T) {
"INPUT_IMAGES",
fmt.Sprintf("%s,%s", testImage1, testImage2))
},
assertions: func(t *testing.T, req render.Request, err error) {
assertions: func(t *testing.T, req *render.Request, err error) {
require.NoError(t, err)
require.Equal(t, testReq, req)
},
Expand Down
3 changes: 3 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ const (
flagCommitMessage = "commit-message"
flagDebug = "debug"
flagImage = "image"
flagLocalInPath = "local-in-path"
flagLocalOutPath = "local-out-path"
flagOutput = "output"
flagOutputJSON = "json"
flagOutputYAML = "yaml"
flagRef = "ref"
flagRepo = "repo"
flagRepoPassword = "repo-password"
flagRepoUsername = "repo-username"
flagStdout = "stdout"
flagTargetBranch = "target-branch"
)
102 changes: 65 additions & 37 deletions cmd/root_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package main

import (
"context"
"errors"
"fmt"
"io"
"os"
"sort"
"strings"

"github.com/spf13/cobra"
Expand All @@ -15,14 +15,16 @@ import (
)

type rootOptions struct {
render.Request
*render.Request
commitMessage string
debug bool
outputFormat string
}

func newRootCommand() *cobra.Command {
cmdOpts := &rootOptions{}
cmdOpts := &rootOptions{
Request: &render.Request{},
}

cmd := &cobra.Command{
Use: "kargo-render",
Expand All @@ -36,9 +38,6 @@ func newRootCommand() *cobra.Command {
Args: cobra.NoArgs,
PreRun: cmdOpts.preRun,
RunE: func(cmd *cobra.Command, _ []string) error {
if err := cmdOpts.validate(); err != nil {
return err
}
return cmdOpts.run(cmd.Context(), cmd.OutOrStdout())
},
}
Expand Down Expand Up @@ -88,6 +87,21 @@ func (o *rootOptions) addFlags(cmd *cobra.Command) {
"used more than once.",
)

cmd.Flags().StringVar(
&o.LocalInPath,
flagLocalInPath,
"",
"Read input from the specified path instead of the remote gitops repository.",
)

cmd.Flags().StringVar(
&o.LocalOutPath,
flagLocalOutPath,
"",
"Write rendered manifests to the specified path instead of the remote "+
"gitops repository. The path must NOT already exist.",
)

cmd.Flags().StringVarP(
&o.outputFormat,
flagOutput,
Expand All @@ -112,9 +126,6 @@ func (o *rootOptions) addFlags(cmd *cobra.Command) {
"",
"The URL of a remote gitops repository.",
)
if err := cmd.MarkFlagRequired(flagRepo); err != nil {
panic(fmt.Errorf("could not mark %s flag as required", flagRepo))
}

cmd.Flags().StringVarP(
&o.RepoCreds.Password,
Expand All @@ -125,9 +136,6 @@ func (o *rootOptions) addFlags(cmd *cobra.Command) {
"repository. Can alternatively be specified using the "+
"KARGO_RENDER_REPO_PASSWORD environment variable.",
)
if err := cmd.MarkFlagRequired(flagRepoPassword); err != nil {
panic(fmt.Errorf("could not mark %s flag as required", flagRepoPassword))
}

cmd.Flags().StringVarP(
&o.RepoCreds.Username,
Expand All @@ -138,9 +146,13 @@ func (o *rootOptions) addFlags(cmd *cobra.Command) {
"Can alternatively be specified using the KARGO_RENDER_REPO_USERNAME "+
"environment variable.",
)
if err := cmd.MarkFlagRequired(flagRepoUsername); err != nil {
panic(fmt.Errorf("could not mark %s flag as required", flagRepoUsername))
}

cmd.Flags().BoolVar(
&o.Stdout,
flagStdout,
false,
"Write rendered manifests to stdout instead of the remote gitops repo.",
)

cmd.Flags().StringVarP(
&o.TargetBranch,
Expand All @@ -152,28 +164,15 @@ func (o *rootOptions) addFlags(cmd *cobra.Command) {
if err := cmd.MarkFlagRequired(flagTargetBranch); err != nil {
panic(fmt.Errorf("could not mark %s flag as required", flagTargetBranch))
}
}

// validate performs validation of the options. If the options are invalid, an
// error is returned.
func (o *rootOptions) validate() error {
var errs []error
// While these flags are marked as required, a user could still provide an
// empty string for any of them. This is a check to ensure that required flags
// are not empty.
if o.RepoURL == "" {
errs = append(errs, fmt.Errorf("the --%s flag is required", flagRepo))
}
if o.RepoCreds.Password == "" {
errs = append(errs, fmt.Errorf("the --%s flag is required", flagRepoPassword))
}
if o.RepoCreds.Username == "" {
errs = append(errs, fmt.Errorf("the --%s flag is required", flagRepoUsername))
}
if o.TargetBranch == "" {
errs = append(errs, fmt.Errorf("the --%s flag is required", flagTargetBranch))
}
return errors.Join(errs...)
// Make sure input source is specified and unambiguous.
cmd.MarkFlagsOneRequired(flagRepo, flagLocalInPath)
cmd.MarkFlagsMutuallyExclusive(flagRepo, flagLocalInPath)
// And the ref flag cannot be combined with the local input path..
cmd.MarkFlagsMutuallyExclusive(flagRef, flagLocalInPath)

// Make sure output destination is unambiguous.
cmd.MarkFlagsMutuallyExclusive(flagCommitMessage, flagLocalOutPath, flagStdout)
}

func (o *rootOptions) preRun(cmd *cobra.Command, _ []string) {
Expand Down Expand Up @@ -224,6 +223,9 @@ func (o *rootOptions) run(ctx context.Context, out io.Writer) error {
if o.outputFormat == "" {
switch res.ActionTaken {
case render.ActionTakenNone:
if o.Stdout {
return manifestsToStdout(res.Manifests, out)
}
fmt.Fprintln(
out,
"\nThis request would not change any state. No action was taken.",
Expand All @@ -242,7 +244,17 @@ func (o *rootOptions) run(ctx context.Context, out io.Writer) error {
o.TargetBranch,
)
case render.ActionTakenUpdatedPR:

fmt.Fprintf(
out,
"\nUpdated PR %s\n",
res.PullRequestURL,
)
case render.ActionTakenWroteToLocalPath:
fmt.Fprintf(
out,
"\nWrote rendered manifests to %s\n",
o.LocalOutPath,
)
}
} else {
if err := output(res, out, o.outputFormat); err != nil {
Expand All @@ -252,3 +264,19 @@ func (o *rootOptions) run(ctx context.Context, out io.Writer) error {

return nil
}

func manifestsToStdout(manifests map[string][]byte, out io.Writer) error {
apps := make([]string, 0, len(manifests))
for k := range manifests {
apps = append(apps, k)
}
sort.StringSlice(apps).Sort()
for _, app := range apps {
const sep = "--------------------------------------------------"
fmt.Fprintln(out, sep)
fmt.Fprintf(out, "App: %s\n", app)
fmt.Fprintln(out, sep)
fmt.Fprintln(out, string(manifests[app]))
}
return nil
}
2 changes: 1 addition & 1 deletion context.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

type requestContext struct {
logger *log.Entry
request Request
request *Request
repo git.Repo
source sourceContext
intermediate intermediateContext
Expand Down
3 changes: 1 addition & 2 deletions docs/docs/30-how-to-guides/30-docker-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ easiest option for experimenting locally with Kargo Render!
Example usage:

```shell
docker run -it ghcr.io/akuity/kargo-render:v0.1.0-rc.34 \
kargo-render render \
docker run -it ghcr.io/akuity/kargo-render:v0.1.0-rc.36 \
--repo https://github.com/<your GitHub handle>/kargo-render-demo-deploy \
--repo-username <your GitHub handle> \
--repo-password <a GitHub personal access token> \
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ require k8s.io/apiserver v0.24.17 // indirect
require (
github.com/argoproj/argo-cd/v2 v2.9.9
github.com/google/go-github/v47 v47.1.0
github.com/sosedoff/gitkit v0.4.0
github.com/xeipuuv/gojsonschema v1.2.0
)

Expand Down Expand Up @@ -95,6 +96,7 @@ require (
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-redis/cache/v9 v9.0.0 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic v0.6.9 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
Expand Down Expand Up @@ -621,6 +623,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sosedoff/gitkit v0.4.0 h1:opyQJ/h9xMRLsz2ca/2CRXtstePcpldiZN8DpLLF8Os=
github.com/sosedoff/gitkit v0.4.0/go.mod h1:V3EpGZ0nvCBhXerPsbDeqtyReNb48cwP9KtkUYTKT5I=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
Expand Down Expand Up @@ -749,6 +753,7 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
Expand Down
Loading

0 comments on commit ea602bb

Please sign in to comment.