From a6de84573c990f28b1d923cff45074d4f2c3c303 Mon Sep 17 00:00:00 2001 From: Mykhailo Bobrovskyi Date: Sat, 3 Aug 2024 10:08:32 +0300 Subject: [PATCH] feat(provider): support gitlab-ci --- README.md | 1 + examples/gitlabci.yml | 18 +++ pkg/cicd/gitlabci/gitlabci.go | 76 ++++++++++ pkg/cicd/gitlabci/gitlabci_test.go | 228 +++++++++++++++++++++++++++++ pkg/cicd/provider.go | 7 +- 5 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 examples/gitlabci.yml create mode 100644 pkg/cicd/gitlabci/gitlabci.go create mode 100644 pkg/cicd/gitlabci/gitlabci_test.go diff --git a/README.md b/README.md index fadd690..1ff6c09 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ jobs: - [multiple plugins published from one repo](https://github.com/ahmetb/kubectx/blob/master/.github/workflows/release.yml) - [circle-ci](examples/circleci.yml) - [travis-ci](examples/travis.yml) +- [gitlab-ci](examples/gitabci.yml) # Testing the template file diff --git a/examples/gitlabci.yml b/examples/gitlabci.yml new file mode 100644 index 0000000..f05e67c --- /dev/null +++ b/examples/gitlabci.yml @@ -0,0 +1,18 @@ +variables: + ## KREW_RELEASE_BOT_WEBHOOK_URL env helps you test your setup without actually publishing to kubernetes-sigs/krew-index + ## remove this env when you are ready for real release + KREW_RELEASE_BOT_WEBHOOK_URL: "https://krew-release-bot-dryrun.rajatjindal.com/github-action-webhook2" + KREW_RELEASE_BOT_VERSION: "v0.0.46" + +stages: + - release + +update-krew-index: + stage: release + script: + - echo "using krew-release-bot version ${KREW_RELEASE_BOT_VERSION}" + - curl -LO https://github.com/rajatjindal/krew-release-bot/releases/download/${KREW_RELEASE_BOT_VERSION}/krew-release-bot_${KREW_RELEASE_BOT_VERSION}_linux_amd64.tar.gz + - tar -xvf krew-release-bot_${KREW_RELEASE_BOT_VERSION}_linux_amd64.tar.gz + - ./krew-release-bot action + rules: + - if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9-\.]+)?$/' \ No newline at end of file diff --git a/pkg/cicd/gitlabci/gitlabci.go b/pkg/cicd/gitlabci/gitlabci.go new file mode 100644 index 0000000..f458173 --- /dev/null +++ b/pkg/cicd/gitlabci/gitlabci.go @@ -0,0 +1,76 @@ +package gitlabci + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +// Provider implements provider interface +type Provider struct{} + +func (p *Provider) IsPreRelease(owner, repo, tag string) (bool, error) { + // gitlab doesn't have Prerelease. + return false, nil +} + +// GetTag returns tag +func (p *Provider) GetTag() (string, error) { + ref := os.Getenv("CI_COMMIT_TAG") + if ref == "" { + return "", fmt.Errorf("CI_COMMIT_TAG env variable not found") + } + + return ref, nil +} + +// GetOwnerAndRepo gets the owner and repo from the env +func (p *Provider) GetOwnerAndRepo() (string, string, error) { + owner := os.Getenv("CI_PROJECT_NAMESPACE") + if owner == "" { + return "", "", fmt.Errorf("env CI_PROJECT_NAMESPACE not set") + } + + repo := os.Getenv("CI_PROJECT_NAME") + if repo == "" { + return "", "", fmt.Errorf("env CI_PROJECT_NAME not set") + } + + return owner, repo, nil +} + +// GetActor gets the owner and repo from the env +func (p *Provider) GetActor() (string, error) { + actor := os.Getenv("GITLAB_USER_LOGIN") + if actor == "" { + return "", fmt.Errorf("env GITLAB_USER_LOGIN not set") + } + + return actor, nil +} + +// getInputForAction gets input to action +func getInputForAction(key string) string { + return os.Getenv(fmt.Sprintf("INPUT_%s", strings.ToUpper(key))) +} + +// GetWorkDirectory gets workdir +func (p *Provider) GetWorkDirectory() string { + workdirInput := getInputForAction("workdir") + if workdirInput != "" { + return workdirInput + } + + return os.Getenv("CI_PROJECT_DIR") +} + +// GetTemplateFile returns the template file +func (p *Provider) GetTemplateFile() string { + templateFile := getInputForAction("krew_template_file") + if templateFile != "" { + return filepath.Join(p.GetWorkDirectory(), templateFile) + } + + return filepath.Join(p.GetWorkDirectory(), ".krew.yaml") +} diff --git a/pkg/cicd/gitlabci/gitlabci_test.go b/pkg/cicd/gitlabci/gitlabci_test.go new file mode 100644 index 0000000..ed22c0d --- /dev/null +++ b/pkg/cicd/gitlabci/gitlabci_test.go @@ -0,0 +1,228 @@ +package gitlabci + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetOwnerAndRepo(t *testing.T) { + testcases := []struct { + name string + setup func() + expectedOwner string + expectedRepo string + expectedError string + }{ + { + name: "CI_PROJECT_NAMESPACE and CI_PROJECT_NAME is set as expected", + setup: func() { + os.Setenv("CI_PROJECT_NAMESPACE", "foo-bar") + os.Setenv("CI_PROJECT_NAME", "my-awesome-repo") + }, + expectedOwner: "foo-bar", + expectedRepo: "my-awesome-repo", + }, + { + name: "CI_PROJECT_NAMESPACE is not set", + setup: func() { + os.Unsetenv("CI_PROJECT_NAMESPACE") + os.Setenv("CI_PROJECT_NAME", "my-awesome-repo") + }, + expectedError: `env CI_PROJECT_NAMESPACE not set`, + }, + { + name: "CI_PROJECT_NAME is not set", + setup: func() { + os.Setenv("CI_PROJECT_NAMESPACE", "foo-bar") + os.Unsetenv("CI_PROJECT_NAME") + }, + expectedError: `env CI_PROJECT_NAME not set`, + }, + } + + p := &Provider{} + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + os.Clearenv() + + if tc.setup != nil { + tc.setup() + } + + owner, repo, err := p.GetOwnerAndRepo() + + assert.Equal(t, tc.expectedOwner, owner) + assert.Equal(t, tc.expectedRepo, repo) + assertError(t, tc.expectedError, err) + }) + } +} + +func TestGetActionActor(t *testing.T) { + testcases := []struct { + name string + setup func() + expectedActor string + expectedError string + }{ + { + name: "env GITLAB_USER_LOGIN is set as expected", + setup: func() { + os.Setenv("GITLAB_USER_LOGIN", "foo-bar") + }, + expectedActor: "foo-bar", + }, + { + name: "env GITLAB_USER_LOGIN is not set", + expectedError: "env GITLAB_USER_LOGIN not set", + }, + } + + p := &Provider{} + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + os.Clearenv() + + if tc.setup != nil { + tc.setup() + } + + actor, err := p.GetActor() + assert.Equal(t, tc.expectedActor, actor) + assertError(t, tc.expectedError, err) + }) + } +} + +func TestGetTag(t *testing.T) { + testcases := []struct { + name string + setup func() + expectedTag string + expectedError string + }{ + { + name: "env CI_COMMIT_TAG is setup", + setup: func() { + os.Setenv("CI_COMMIT_TAG", "v5.0.0") + }, + expectedTag: "v5.0.0", + }, + { + name: "CI_COMMIT_TAG is not set", + setup: func() { + os.Unsetenv("CI_COMMIT_TAG") + }, + expectedError: `CI_COMMIT_TAG env variable not found`, + }, + } + + p := &Provider{} + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + os.Clearenv() + + if tc.setup != nil { + tc.setup() + } + + tag, err := p.GetTag() + assert.Equal(t, tc.expectedTag, tag) + assertError(t, tc.expectedError, err) + }) + } +} + +func assertError(t *testing.T, expectedError string, err error) { + if expectedError == "" { + assert.Nil(t, err) + } + + if expectedError != "" { + assert.NotNil(t, err) + if err != nil { + assert.Equal(t, expectedError, err.Error()) + } + } +} + +func TestGetWorkingDirectory(t *testing.T) { + testcases := []struct { + name string + setup func() + expectedDir string + }{ + { + name: "env CI_PROJECT_DIR is setup", + setup: func() { + os.Setenv("CI_PROJECT_DIR", "/builds/foo-bar/my-awesome-repo") + }, + expectedDir: "/builds/foo-bar/my-awesome-repo", + }, { + name: "env CI_PROJECT_DIR is setup", + setup: func() { + os.Setenv("CI_PROJECT_DIR", "/builds/foo-bar/my-awesome-repo") + os.Setenv("INPUT_WORKDIR", "/my-workdir") + }, + expectedDir: "/my-workdir", + }, { + name: "env CI_PROJECT_DIR is not set", + setup: func() { + os.Unsetenv("CI_PROJECT_DIR") + }, + expectedDir: "", + }, + } + + p := &Provider{} + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + os.Clearenv() + + if tc.setup != nil { + tc.setup() + } + + dir := p.GetWorkDirectory() + assert.Equal(t, tc.expectedDir, dir) + }) + } +} + +func TestGetTemplateFile(t *testing.T) { + testcases := []struct { + name string + setup func() + expectFile string + }{ + { + name: "env INPUT_KREW_TEMPLATE_FILE is setup", + setup: func() { + os.Setenv("INPUT_KREW_TEMPLATE_FILE", "my-awesome-plugin.yaml") + }, + expectFile: "my-awesome-plugin.yaml", + }, { + name: "env INPUT_KREW_TEMPLATE_FILE is not set", + setup: func() { + os.Unsetenv("INPUT_KREW_TEMPLATE_FILE") + }, + expectFile: ".krew.yaml", + }, + } + + p := &Provider{} + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + os.Clearenv() + + if tc.setup != nil { + tc.setup() + } + + dir := p.GetTemplateFile() + assert.Equal(t, tc.expectFile, dir) + }) + } +} diff --git a/pkg/cicd/provider.go b/pkg/cicd/provider.go index fad7d88..7967993 100644 --- a/pkg/cicd/provider.go +++ b/pkg/cicd/provider.go @@ -5,6 +5,7 @@ import ( "github.com/rajatjindal/krew-release-bot/pkg/cicd/circleci" "github.com/rajatjindal/krew-release-bot/pkg/cicd/github" + "github.com/rajatjindal/krew-release-bot/pkg/cicd/gitlabci" "github.com/rajatjindal/krew-release-bot/pkg/cicd/travisci" ) @@ -19,8 +20,12 @@ type Provider interface { } // GetProvider returns the CI/CD provider -// e.g. github-actions or circle-ci +// e.g. github-actions, travis-ci, circle-ci or gitlab-ci func GetProvider() Provider { + if os.Getenv("GITLAB_CI") == "true" { + return &gitlabci.Provider{} + } + if os.Getenv("GITHUB_ACTIONS") == "true" { return &github.Actions{} }