diff --git a/README.md b/README.md index 869e1c0..4a47270 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,6 @@ ## Features - Create GitHub releases with bumping Semantic Versioning tag -- Send pull requests which update docker image tag. -- Support `{{.Owner}}` and `{{.Repository}}` placeholder in environment variables and command-line options when setting configs. ## Installation @@ -20,7 +18,7 @@ If you use Windows or Linux, replace `windows_amd64` or `linux_amd64` instead of `darwin_amd64`. ```bash -$ VERSION=0.2.0 +$ VERSION=1.0.0 $ curl -O -L https://github.com/p1ass/mikku/releases/download/v${VERSION}/mikku_${VERSION}_darwin_amd64.tar.gz $ tar -zxvf mikku_${VERSION}_darwin_amd64.tar.gz $ chmod a+x mikku @@ -66,27 +64,6 @@ $ mikku release sample-repository patch Note that `mikku` doesn't build and push a docker image, so you have to do it using CI service such as CircleCI. -### Create a pull request updating docker image tag in Kubernetes manifest file - -Update image tag in Kubernetes manifest file to the latest version. -```bash -$ export MIKKU_MANIFEST_REPOSITORY=sample-manifest-repository -$ export MIKKU_MANIFEST_FILEPATH=manifests/{{.Repository}}/deployment.yml -$ export MIKKU_DOCKER_IMAGE_NAME={{.Owner}}/{{.Repository}} - -$ mikku pr sample-repository -``` - -```yaml -spec: - containers: - - name: sample-repository-container - image: p1ass/sample-repository:v1.0.0 - ↓ Replace - image: p1ass/sample-repository:v1.0.1 -``` - - ## Screenshots ### Release @@ -98,14 +75,6 @@ $ mikku release sample-repository v0.1.1 ![changelog](images/changelog.png) -### PullRequest - -```bash -$ mikku pr memoito-server -``` - -![diff](images/diff.png) - ## Commands #### `mikku release ` @@ -129,64 +98,6 @@ $ mikku release sample-repository minor # v1.0.1 → v1.1.0 $ mikku release sample-repository major # v1.1.0 → v2.0.0 ``` -#### `mikku pr [-m ] [-p ] [-i ] ` - -Create a pull request updating Docker image tag written in Kubernetes manifest file. - - -##### Options - -If you don't set environment variables, you must add options when executing commands. - -- `--manifest, -m` - - Specify a repository existing Kubernetes manifest file. - - Optional. - - Default : `MIKKU_MANIFEST_REPOSITORY` environment variable. - -- `--path, -p` - - File path where the target docker image is written. - - Optional. - - Default : `MIKKU_MANIFEST_FILEPATH` environment variable. - - You can use [text/template](https://golang.org/pkg/text/template/) in Go. - - Support variable : `{{.Owner}}`, `{{.Repository}}` - - Ex. `manifests/{{.Repository}}/deployment.yml` - -- `--image, -i` - - Docker image name. - - Optional. - - Default : `MIKKU_DOCKER_IMAGE_NAME` environment variable. - - You can use [text/template](https://golang.org/pkg/text/template/) in Go. - - Support variable : `{{.Owner}}`, `{{.Repository}}` - - Ex. `asia.gcr.io/{{.Owner}}/{{.Repository}}` - - - -##### Examples - -```bash -$ export MIKKU_GITHUB_ACCESS_TOKEN=[YOUR_ACCESS_TOKEN] -$ export MIKKU_GITHUB_OWNER=p1ass - -# You need to set environment variables. Otherwise, add options when executing commands -$ export MIKKU_MANIFEST_REPOSITORY=manifest-repository -$ export MIKKU_MANIFEST_FILEPATH=manifests/{{.Repository}}/deployment.yml -$ export MIKKU_DOCKER_IMAGE_NAME=asia.gcr.io/{{.Owner}}/{{.Repository}} - -# The most simple case -# When the latest tag name is `v1.0.1`, -# replace p1ass/sample-repository:v1.0.0 existing in manifest-repository to p1ass/sample-repository:v1.0.1. -$ mikku pr sample-repository - -# Override manifest repository -$ mikku pr --manifest other-manifest-repo sample-repository - -# Override Kubernetes manifest file -$ mikku pr --path {{.Owner}}/{{.Repository}}/deployment.yml sample-repository - -# Override docker image name -$ mikku pr --image docker.example.com/{{.Repository}} sample-repository -``` - ## For developers ### Build diff --git a/commands.go b/commands.go index 06ab7d9..f02d243 100644 --- a/commands.go +++ b/commands.go @@ -27,34 +27,6 @@ var commandRelease = &cli.Command{ Action: doRelease, } -var commandPullRequest = &cli.Command{ - Name: "pr", - Usage: "Create a pull request updating Docker image tag written in Kubernetes manifest file", - UsageText: ` - mikku pr [options...] - - Create a pull request updating Docker image tag written in Kubernetes manifest file. - `, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "manifest", - Aliases: []string{"m"}, - Usage: "Repository existing Kubernetes manifest file. It overrides MIKKU_MANIFEST_REPOSITORY environment variable", - }, - &cli.StringFlag{ - Name: "path", - Aliases: []string{"p"}, - Usage: "File path where the target docker image is written. It overrides MIKKU_MANIFEST_FILEPATH environment variable", - }, - &cli.StringFlag{ - Name: "image", - Aliases: []string{"i"}, - Usage: "Docker image name. It overrides MIKKU_DOCKER_IMAGE_NAME environment variable", - }, - }, - Action: doPullRequest, -} - func doRelease(c *cli.Context) error { if c.Args().Len() == 0 { _ = cli.ShowCommandHelp(c, "release") @@ -75,34 +47,11 @@ func doRelease(c *cli.Context) error { return nil } -func doPullRequest(c *cli.Context) error { - if c.Args().Len() == 0 { - _ = cli.ShowCommandHelp(c, "pr") - return nil - } - - if c.Args().Len() != 1 { - return fmt.Errorf("One argument is required: repository") - } - - repo := c.Args().Get(0) - manifestRepo := c.String("manifest") - pathToManifestFile := c.String("path") - image := c.String("image") - - if err := PullRequest(repo, manifestRepo, pathToManifestFile, image); err != nil { - return fmt.Errorf("Failed to execute pr: %v", err) - - } - - return nil -} - // Run runs commands depending on the given argument func Run(args []string) error { app := &cli.App{ Name: "mikku", - Usage: "Bump Semantic Versioning tag, create GitHub release and update Kubernetes manifest file", + Usage: "Bump Semantic Versioning tag andcreate GitHub release", Authors: []*cli.Author{ { Name: "p1ass", @@ -112,7 +61,6 @@ func Run(args []string) error { Version: mikkuVersion, Commands: []*cli.Command{ commandRelease, - commandPullRequest, }, } diff --git a/commands_test.go b/commands_test.go index 9b19861..d09161b 100644 --- a/commands_test.go +++ b/commands_test.go @@ -20,11 +20,6 @@ func TestRun(t *testing.T) { args: []string{"", "release", "mikku"}, wantErr: true, }, - { - name: "pr: no arguments", - args: []string{"", "pr"}, - wantErr: false, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/config.go b/config.go index cb8c38a..8a9d1a0 100644 --- a/config.go +++ b/config.go @@ -1,20 +1,15 @@ package mikku import ( - "bytes" "errors" "fmt" - "text/template" "github.com/kelseyhightower/envconfig" ) var ( - errEmptyGitHubAccessToken = errors.New("should be set MIKKU_GITHUB_ACCESS_TOKEN") - errEmptyGitHubOwner = errors.New("should be set MIKKU_GITHUB_OWNER") - errEmptyManifestRepository = errors.New("should be set MIKKU_MANIFEST_REPOSITORY or --manifest option") - errEmptyManifestFilePath = errors.New("should be set MIKKU_MANIFEST_FILEPATH or --path option") - errEmptyDockerImageName = errors.New("should be set MIKKU_DOCKER_IMAGE_NAME or --image option") + errEmptyGitHubAccessToken = errors.New("should be set MIKKU_GITHUB_ACCESS_TOKEN") + errEmptyGitHubOwner = errors.New("should be set MIKKU_GITHUB_OWNER") ) // Config represents config using all commands @@ -35,72 +30,6 @@ func (cfg *Config) validate() error { return nil } -// PRConfig represents config using pr command -type PRConfig struct { - ManifestRepository string `envconfig:"MIKKU_MANIFEST_REPOSITORY"` - ManifestFilepath string `envconfig:"MIKKU_MANIFEST_FILEPATH"` - DockerImageName string `envconfig:"MIKKU_DOCKER_IMAGE_NAME"` -} - -func (cfg *PRConfig) overrideConfig(manifestRepo, pathToManifestFile, imageName string) { - if manifestRepo != "" { - cfg.ManifestRepository = manifestRepo - } - if pathToManifestFile != "" { - cfg.ManifestFilepath = pathToManifestFile - } - if imageName != "" { - cfg.DockerImageName = imageName - } -} - -func (cfg *PRConfig) validate() error { - if cfg.ManifestRepository == "" { - return errEmptyManifestRepository - } - - if cfg.ManifestFilepath == "" { - return errEmptyManifestFilePath - } - - if cfg.DockerImageName == "" { - return errEmptyDockerImageName - } - return nil -} - -func (cfg *PRConfig) embedRepoInfo(owner, repo string) error { - info := map[string]interface{}{"Owner": owner, "Repository": repo} - - embedManifestFilepath, err := parse(cfg.ManifestFilepath, info) - if err != nil { - return fmt.Errorf("parse manifest filepath: %w", err) - } - cfg.ManifestFilepath = embedManifestFilepath - - embedDockerImageName, err := parse(cfg.DockerImageName, info) - if err != nil { - return fmt.Errorf("parse docker image name: %w", err) - } - cfg.DockerImageName = embedDockerImageName - - return nil -} - -func parse(text string, info map[string]interface{}) (string, error) { - tmpl, err := template.New("text").Parse(text) - if err != nil { - return "", fmt.Errorf("template parse error: %w", err) - } - - buff := bytes.NewBuffer(make([]byte, 0, 20)) - if err := tmpl.Execute(buff, info); err != nil { - return "", fmt.Errorf("template execute error: %w", err) - } - - return buff.String(), nil -} - func readConfig() (*Config, error) { cfg := &Config{} if err := envconfig.Process("", cfg); err != nil { @@ -108,11 +37,3 @@ func readConfig() (*Config, error) { } return cfg, nil } - -func readPRConfig() (*PRConfig, error) { - cfg := &PRConfig{} - if err := envconfig.Process("", cfg); err != nil { - return nil, fmt.Errorf("failed to read environment variables: %w", err) - } - return cfg, nil -} diff --git a/config_test.go b/config_test.go index 113845c..c238a31 100644 --- a/config_test.go +++ b/config_test.go @@ -119,276 +119,3 @@ func TestReadConfig(t *testing.T) { }) } } - -func TestReadPRConfig(t *testing.T) { - tests := []struct { - name string - setEnv func() func() - want *PRConfig - wantErr bool - }{ - { - name: "no environment variable set", - setEnv: func() func() { - return func() {} - }, - want: &PRConfig{}, - wantErr: false, - }, - { - name: "All env config be set", - setEnv: func() func() { - _ = os.Setenv("MIKKU_MANIFEST_REPOSITORY", "MIKKU_MANIFEST_REPOSITORY") - _ = os.Setenv("MIKKU_MANIFEST_FILEPATH", "MIKKU_MANIFEST_FILEPATH") - _ = os.Setenv("MIKKU_DOCKER_IMAGE_NAME", "MIKKU_DOCKER_IMAGE_NAME") - return func() { - _ = os.Unsetenv("MIKKU_MANIFEST_REPOSITORY") - _ = os.Unsetenv("MIKKU_MANIFEST_FILEPATH") - _ = os.Unsetenv("MIKKU_DOCKER_IMAGE_NAME") - } - }, - want: &PRConfig{ - ManifestRepository: "MIKKU_MANIFEST_REPOSITORY", - ManifestFilepath: "MIKKU_MANIFEST_FILEPATH", - DockerImageName: "MIKKU_DOCKER_IMAGE_NAME", - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer tt.setEnv()() - - got, err := readPRConfig() - if (err != nil) != tt.wantErr { - t.Errorf("readConfig() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !cmp.Equal(got, tt.want) { - t.Errorf("readConfig() diff=%s", cmp.Diff(got, tt.want)) - } - }) - } -} - -func TestPRConfig_overrideConfig(t *testing.T) { - t.Parallel() - - type args struct { - manifestRepo string - pathToManifestFile string - imageName string - } - tests := []struct { - name string - cfg *PRConfig - args args - want *PRConfig - }{ - { - name: "override manifest repository", - cfg: &PRConfig{ - ManifestRepository: "ManifestRepository", - ManifestFilepath: "ManifestFilepath", - DockerImageName: "DockerImageName", - }, - args: args{ - manifestRepo: "overrideManifestRepo", - pathToManifestFile: "", - imageName: "", - }, - want: &PRConfig{ - ManifestRepository: "overrideManifestRepo", - ManifestFilepath: "ManifestFilepath", - DockerImageName: "DockerImageName", - }, - }, - { - name: "override file path", - cfg: &PRConfig{ - ManifestRepository: "ManifestRepository", - ManifestFilepath: "ManifestFilepath", - DockerImageName: "DockerImageName", - }, - args: args{ - manifestRepo: "", - pathToManifestFile: "overridePathToManifestFile", - imageName: "", - }, - want: &PRConfig{ - ManifestRepository: "ManifestRepository", - ManifestFilepath: "overridePathToManifestFile", - DockerImageName: "DockerImageName", - }, - }, - { - name: "override docker image", - cfg: &PRConfig{ - ManifestRepository: "ManifestRepository", - ManifestFilepath: "ManifestFilepath", - DockerImageName: "DockerImageName", - }, - args: args{ - manifestRepo: "", - pathToManifestFile: "", - imageName: "overrideDockerImageName", - }, - want: &PRConfig{ - ManifestRepository: "ManifestRepository", - ManifestFilepath: "ManifestFilepath", - DockerImageName: "overrideDockerImageName", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.cfg.overrideConfig(tt.args.manifestRepo, tt.args.pathToManifestFile, tt.args.imageName); !cmp.Equal(tt.cfg, tt.want) { - t.Errorf("Config.overrideConfig() = %v, want %v", tt.cfg, tt.want) - } - }) - } -} - -func TestPRConfig_validate(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - ManifestRepository string - ManifestFilepath string - DockerImageName string - wantErr error - }{ - { - name: "all fields are filled", - ManifestRepository: "test-manifest-repo", - ManifestFilepath: "path/to/manifest/file", - DockerImageName: "test/docker/image", - wantErr: nil, - }, - { - name: "empty manifest repository", - ManifestRepository: "", - ManifestFilepath: "path/to/manifest/file", - DockerImageName: "test/docker/image", - wantErr: errEmptyManifestRepository, - }, - { - name: "empty manifest repository", - ManifestRepository: "test-manifest-repo", - ManifestFilepath: "", - DockerImageName: "test/docker/image", - wantErr: errEmptyManifestFilePath, - }, - { - name: "empty manifest repository", - ManifestRepository: "test-manifest-repo", - ManifestFilepath: "path/to/manifest/file", - DockerImageName: "", - wantErr: errEmptyDockerImageName, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cfg := &PRConfig{ - ManifestRepository: tt.ManifestRepository, - ManifestFilepath: tt.ManifestFilepath, - DockerImageName: tt.DockerImageName, - } - err := cfg.validate() - if (tt.wantErr == nil && err != nil) || (tt.wantErr != nil && !errors.As(err, &tt.wantErr)) { - t.Errorf("PRConfig.validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestPRConfig_embedRepoInfo(t *testing.T) { - t.Parallel() - - type fields struct { - ManifestRepository string - ManifestFilepath string - DockerImageName string - } - - tests := []struct { - name string - fields fields - owner string - repo string - want *PRConfig - wantErr bool - }{ - { - name: "parse all fields", - fields: fields{ - ManifestFilepath: "{{.Owner}}/{{.Repository}}/manifest.yml", - DockerImageName: "{{.Owner}}/{{.Repository}}", - }, - owner: "test-owner", - repo: "test-repo", - want: &PRConfig{ - ManifestFilepath: "test-owner/test-repo/manifest.yml", - DockerImageName: "test-owner/test-repo", - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cfg := &PRConfig{ - ManifestRepository: tt.fields.ManifestRepository, - ManifestFilepath: tt.fields.ManifestFilepath, - DockerImageName: tt.fields.DockerImageName, - } - - if err := cfg.embedRepoInfo(tt.owner, tt.repo); (err != nil) != tt.wantErr { - t.Errorf("PRConfig.embedRepoInfo() error = %v, wantErr %v", err, tt.wantErr) - } - - if !cmp.Equal(cfg, tt.want) { - t.Errorf("PRConfig.embedRepoInfo() cfg = %v, want %v", cfg, tt.want) - } - }) - } -} - -func Test_parse(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - text string - info map[string]interface{} - want string - wantErr bool - }{ - { - name: "no parsed field", - text: "test-text", - info: map[string]interface{}{"Owner": "test-owner"}, - want: "test-text", - wantErr: false, - }, - { - name: "parse field", - text: "{{.Owner}}", - info: map[string]interface{}{"Owner": "test-owner"}, - want: "test-owner", - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := parse(tt.text, tt.info) - if (err != nil) != tt.wantErr { - t.Errorf("parse() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("parse() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/github.go b/github.go index e51df5e..89d12a2 100644 --- a/github.go +++ b/github.go @@ -19,9 +19,6 @@ const ( var ( // errReleaseNotFound represents error that the release does not found errReleaseNotFound = errors.New("release not found") - - errFileORRepoNotFound = errors.New("file or repository not found") - errContentIsDirectory = errors.New("content is directory, not file") ) //go:generate mockgen -source=$GOFILE -destination=mock_$GOFILE -package=$GOPACKAGE @@ -30,9 +27,6 @@ var ( type gitHubRepositoriesClient interface { CreateRelease(ctx context.Context, owner, repo string, release *github.RepositoryRelease) (*github.RepositoryRelease, *github.Response, error) GetLatestRelease(ctx context.Context, owner, repo string) (*github.RepositoryRelease, *github.Response, error) - - GetContents(ctx context.Context, owner, repo, path string, opt *github.RepositoryContentGetOptions) (fileContent *github.RepositoryContent, directoryContent []*github.RepositoryContent, resp *github.Response, err error) - UpdateFile(ctx context.Context, owner, repo, path string, opt *github.RepositoryContentFileOptions) (*github.RepositoryContentResponse, *github.Response, error) } // gitHubPullRequestsClient is a interface for calling GitHub API about pull requests @@ -42,17 +36,11 @@ type gitHubPullRequestsClient interface { Create(ctx context.Context, owner string, repo string, pull *github.NewPullRequest) (*github.PullRequest, *github.Response, error) } -type gitHubGitClient interface { - GetRef(ctx context.Context, owner string, repo string, ref string) (*github.Reference, *github.Response, error) - CreateRef(ctx context.Context, owner string, repo string, ref *github.Reference) (*github.Reference, *github.Response, error) -} - // githubClient handles application logic using GitHub API type githubClient struct { owner string repoCli gitHubRepositoriesClient prCli gitHubPullRequestsClient - gitCli gitHubGitClient } // newGitHubClientUsingEnv returns a pointer of githubClient @@ -65,15 +53,14 @@ func newGitHubClientUsingEnv(owner, accessToken string) *githubClient { tc := oauth2.NewClient(ctx, ts) client := github.NewClient(tc) - return newGitHubClient(owner, client.Repositories, client.PullRequests, client.Git) + return newGitHubClient(owner, client.Repositories, client.PullRequests) } -func newGitHubClient(owner string, repoCli gitHubRepositoriesClient, prCli gitHubPullRequestsClient, gitCli gitHubGitClient) *githubClient { +func newGitHubClient(owner string, repoCli gitHubRepositoriesClient, prCli gitHubPullRequestsClient) *githubClient { return &githubClient{ owner: owner, repoCli: repoCli, prCli: prCli, - gitCli: gitCli, } } @@ -149,84 +136,6 @@ func (s *githubClient) getMergedPRsAfter(repo string, after time.Time) ([]*githu return prList, nil } -// getFile fetches the file from GitHub and return content and hash -func (s *githubClient) getFile(repo, filePath string) (content string, hash string, err error) { - ctx := context.Background() - file, _, resp, err := s.repoCli.GetContents(ctx, s.owner, repo, filePath, &github.RepositoryContentGetOptions{ - Ref: baseBranch, - }) - if err != nil { - if resp != nil && resp.StatusCode == http.StatusNotFound { - return "", "", fmt.Errorf("%s: %w", filePath, errFileORRepoNotFound) - } - return "", "", fmt.Errorf("call getting contents api: %w", err) - } - - if file == nil { - return "", "", errContentIsDirectory - } - - content, err = file.GetContent() - if err != nil { - return "", "", fmt.Errorf("failed to decode encoded file: %w", err) - } - - hash = file.GetSHA() - return content, hash, nil -} - -// pushFile pushes the updated file -func (s *githubClient) pushFile(repo, filePath, branch, commitMessage, commitSHA string, body []byte) error { - ctx := context.Background() - _, _, err := s.repoCli.UpdateFile(ctx, s.owner, repo, filePath, &github.RepositoryContentFileOptions{ - Message: github.String(commitMessage), - Content: body, - SHA: github.String(commitSHA), - Branch: github.String(branch), - Committer: &github.CommitAuthor{ - Name: github.String("mikku"), - Email: github.String("mikku@p1ass.com"), - }, - }) - if err != nil { - return fmt.Errorf("call updating file api: %w", err) - } - - return nil -} - -// createPullRequest creates a pull request -func (s *githubClient) createPullRequest(repo, branch, title, body string) (*github.PullRequest, error) { - ctx := context.Background() - pr, _, err := s.prCli.Create(ctx, s.owner, repo, &github.NewPullRequest{ - Title: github.String(title), - Head: github.String(branch), - Base: github.String(baseBranch), - Body: github.String(body), - }) - if err != nil { - return nil, fmt.Errorf("call create pull request api: %w", err) - } - - return pr, nil -} - -// createBranch creates new branch -func (s *githubClient) createBranch(repo, branch string) error { - ctx := context.Background() - ref, _, err := s.gitCli.GetRef(ctx, s.owner, repo, fmt.Sprintf("heads/%s", baseBranch)) - if err != nil { - return fmt.Errorf("call getting reference api :%w", err) - } - - ref.Ref = github.String("refs/heads/" + branch) - if _, _, err := s.gitCli.CreateRef(ctx, s.owner, repo, ref); err != nil { - return fmt.Errorf("call creating reference api: %w", err) - } - - return nil -} - // extractMergedPRsAfter extract merged PRs after a given time // Return PRs and boolean whether we got all PRs we want func extractMergedPRsAfter(prs []*github.PullRequest, after time.Time) ([]*github.PullRequest, bool) { diff --git a/github_test.go b/github_test.go index b166e8e..890be25 100644 --- a/github_test.go +++ b/github_test.go @@ -81,7 +81,7 @@ func TestGitHubClient_createRelease(t *testing.T) { cli := NewMockgitHubRepositoriesClient(ctrl) cli = tt.injector(cli) - s := newGitHubClient("test-owner", cli, nil, nil) + s := newGitHubClient("test-owner", cli, nil) got, err := s.createRelease(tt.args.repo, tt.args.tagName, tt.args.body) if (err != nil) != tt.wantErr { @@ -186,7 +186,7 @@ func TestGitHubService_getLatestRelease(t *testing.T) { cli := NewMockgitHubRepositoriesClient(ctrl) cli = tt.injector(cli) - s := newGitHubClient("test-owner", cli, nil, nil) + s := newGitHubClient("test-owner", cli, nil) got, err := s.getLatestRelease(tt.repo) fmt.Printf("%#v\n", got) @@ -353,7 +353,7 @@ func TestGitHubService_getMergedPRsAfter(t *testing.T) { cli := NewMockgitHubPullRequestsClient(ctrl) cli = tt.injector(cli) - s := newGitHubClient("test-owner", nil, cli, nil) + s := newGitHubClient("test-owner", nil, cli) got, err := s.getMergedPRsAfter(tt.repo, tt.after) if (err != nil) != tt.wantErr { t.Errorf("githubClient.getMergedPRsAfter() error = %v, wantErr %v", err, tt.wantErr) @@ -366,415 +366,6 @@ func TestGitHubService_getMergedPRsAfter(t *testing.T) { } } -func TestGitHubService_getFile(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - repo string - filePath string - injector func(cli *MockgitHubRepositoriesClient) *MockgitHubRepositoriesClient - wantContent string - wantHash string - wantErr error - }{ - { - name: "success in getting file", - repo: "test-repo", - filePath: "manifests/repo/deployment.yml", - injector: func(cli *MockgitHubRepositoriesClient) *MockgitHubRepositoriesClient { - cli.EXPECT().GetContents(gomock.Any(), "test-owner", "test-repo", "manifests/repo/deployment.yml", &github.RepositoryContentGetOptions{ - Ref: baseBranch, - }).Return(&github.RepositoryContent{ - Encoding: github.String("base64"), - Content: github.String("dGVzdC1jb250ZW50"), - SHA: github.String("test-hash"), - }, nil, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusCreated, - }, - }, nil) - return cli - }, - wantContent: "test-content", - wantHash: "test-hash", - wantErr: nil, - }, - { - name: "file not found", - repo: "test-repo", - filePath: "manifests/repo/deployment.yml", - injector: func(cli *MockgitHubRepositoriesClient) *MockgitHubRepositoriesClient { - cli.EXPECT().GetContents(gomock.Any(), "test-owner", "test-repo", "manifests/repo/deployment.yml", &github.RepositoryContentGetOptions{ - Ref: baseBranch, - }).Return(nil, nil, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusNotFound, - }, - }, errors.New("file not found")) - return cli - }, - wantContent: "", - wantHash: "", - wantErr: errFileORRepoNotFound, - }, - { - name: "unknown error", - repo: "test-repo", - filePath: "manifests/repo/deployment.yml", - injector: func(cli *MockgitHubRepositoriesClient) *MockgitHubRepositoriesClient { - cli.EXPECT().GetContents(gomock.Any(), "test-owner", "test-repo", "manifests/repo/deployment.yml", &github.RepositoryContentGetOptions{ - Ref: baseBranch, - }).Return(nil, nil, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusInternalServerError, - }, - }, errors.New("unknown error")) - return cli - }, - wantContent: "", - wantHash: "", - wantErr: errors.New("unknown error"), - }, - { - name: "getting content is directory, not file", - repo: "test-repo", - filePath: "manifests/repo/deployment.yml", - injector: func(cli *MockgitHubRepositoriesClient) *MockgitHubRepositoriesClient { - cli.EXPECT().GetContents(gomock.Any(), "test-owner", "test-repo", "manifests/repo/deployment.yml", &github.RepositoryContentGetOptions{ - Ref: baseBranch, - }).Return(nil, []*github.RepositoryContent{ - { - Encoding: github.String("base64"), - Content: github.String("dGVzdC1jb250ZW50"), - }, - }, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusCreated, - }, - }, nil) - return cli - }, - wantContent: "", - wantHash: "", - wantErr: errContentIsDirectory, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - cli := NewMockgitHubRepositoriesClient(ctrl) - cli = tt.injector(cli) - - s := newGitHubClient("test-owner", cli, nil, nil) - - gotContent, gotHash, err := s.getFile(tt.repo, tt.filePath) - if (tt.wantErr == nil && err != nil) || (tt.wantErr != nil && !errors.As(err, &tt.wantErr)) { - t.Errorf("githubClient.getFile() error = %v, wantErr %v", err, tt.wantErr) - return - } - if gotContent != tt.wantContent { - t.Errorf("githubClient.getFile() = %v, wantContent %v", gotContent, tt.wantContent) - } - if gotHash != tt.wantHash { - t.Errorf("githubClient.getFile() = %v, wantHash %v", gotHash, tt.wantHash) - } - }) - } -} - -func TestGitHubService_pushFile(t *testing.T) { - t.Parallel() - - type args struct { - repo string - filePath string - branch string - commitMessage string - commitSHA string - body []byte - } - tests := []struct { - name string - args args - injector func(cli *MockgitHubRepositoriesClient) *MockgitHubRepositoriesClient - - wantErr bool - }{ - { - name: "success in pushing a file", - args: args{ - repo: "test-repo", - filePath: "test-file-path", - branch: "test-branch", - commitMessage: "test-commit-message", - commitSHA: "test-commit-sha", - body: []byte("test-body"), - }, - injector: func(cli *MockgitHubRepositoriesClient) *MockgitHubRepositoriesClient { - cli.EXPECT().UpdateFile(gomock.Any(), "test-owner", "test-repo", "test-file-path", - &github.RepositoryContentFileOptions{ - Message: github.String("test-commit-message"), - Content: []byte("test-body"), - SHA: github.String("test-commit-sha"), - Branch: github.String("test-branch"), - Committer: &github.CommitAuthor{ - Name: github.String("mikku"), - Email: github.String("mikku@p1ass.com"), - }, - }).Return(&github.RepositoryContentResponse{}, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusCreated, - }, - }, nil) - return cli - }, - wantErr: false, - }, - { - name: "unknown error", - args: args{ - repo: "test-repo", - filePath: "test-file-path", - branch: "test-branch", - commitMessage: "test-commit-message", - commitSHA: "test-commit-sha", - body: []byte("test-body"), - }, - injector: func(cli *MockgitHubRepositoriesClient) *MockgitHubRepositoriesClient { - cli.EXPECT().UpdateFile(gomock.Any(), "test-owner", "test-repo", "test-file-path", - &github.RepositoryContentFileOptions{ - Message: github.String("test-commit-message"), - Content: []byte("test-body"), - SHA: github.String("test-commit-sha"), - Branch: github.String("test-branch"), - Committer: &github.CommitAuthor{ - Name: github.String("mikku"), - Email: github.String("mikku@p1ass.com"), - }, - }).Return(nil, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusInternalServerError, - }, - }, errors.New("unknown error")) - return cli - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - cli := NewMockgitHubRepositoriesClient(ctrl) - cli = tt.injector(cli) - - s := newGitHubClient("test-owner", cli, nil, nil) - - if err := s.pushFile(tt.args.repo, tt.args.filePath, tt.args.branch, tt.args.commitMessage, tt.args.commitSHA, tt.args.body); (err != nil) != tt.wantErr { - t.Errorf("githubClient.pushFile() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestGitHubService_createPullRequest(t *testing.T) { - t.Parallel() - - type args struct { - repo string - branch string - title string - body string - } - tests := []struct { - name string - args args - injector func(cli *MockgitHubPullRequestsClient) *MockgitHubPullRequestsClient - want *github.PullRequest - wantErr bool - }{ - { - name: "success in creating a pull request", - args: args{ - repo: "test-repo", - branch: "test-branch", - title: "test-title", - body: "test-body", - }, - injector: func(cli *MockgitHubPullRequestsClient) *MockgitHubPullRequestsClient { - cli.EXPECT().Create(gomock.Any(), "test-owner", "test-repo", &github.NewPullRequest{ - Title: github.String("test-title"), - Head: github.String("test-branch"), - Base: github.String("master"), - Body: github.String("test-body"), - }).Return(&github.PullRequest{ - ID: github.Int64(1), - }, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusCreated, - }, - }, nil) - return cli - }, - want: &github.PullRequest{ - ID: github.Int64(1), - }, - wantErr: false, - }, - { - name: "failed to create a pull request", - args: args{ - repo: "test-repo", - branch: "test-branch", - title: "test-title", - body: "test-body", - }, - injector: func(cli *MockgitHubPullRequestsClient) *MockgitHubPullRequestsClient { - cli.EXPECT().Create(gomock.Any(), "test-owner", "test-repo", &github.NewPullRequest{ - Title: github.String("test-title"), - Head: github.String("test-branch"), - Base: github.String("master"), - Body: github.String("test-body"), - }).Return(nil, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusInternalServerError, - }, - }, errors.New("some error")) - return cli - }, - want: nil, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - cli := NewMockgitHubPullRequestsClient(ctrl) - cli = tt.injector(cli) - - s := newGitHubClient("test-owner", nil, cli, nil) - - got, err := s.createPullRequest(tt.args.repo, tt.args.branch, tt.args.title, tt.args.body) - if (err != nil) != tt.wantErr { - t.Errorf("githubClient.createPullRequest() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !cmp.Equal(got, tt.want) { - t.Errorf("githubClient.createPullRequest() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestGitHubService_createBranch(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - repo string - branch string - injector func(cli *MockgitHubGitClient) *MockgitHubGitClient - wantErr bool - }{ - { - name: "success in creating a branch", - repo: "test-repo", - branch: "test-branch", - injector: func(cli *MockgitHubGitClient) *MockgitHubGitClient { - cli.EXPECT().GetRef(gomock.Any(), "test-owner", "test-repo", "heads/master"). - Return(&github.Reference{ - Ref: github.String("refs/heads/master"), - Object: &github.GitObject{ - Type: github.String("commit"), - SHA: github.String("sha-hash"), - }, - }, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusOK, - }, - }, nil) - cli.EXPECT().CreateRef(gomock.Any(), "test-owner", "test-repo", &github.Reference{ - Ref: github.String("refs/heads/test-branch"), - Object: &github.GitObject{ - Type: github.String("commit"), - SHA: github.String("sha-hash"), - }, - }).Return(&github.Reference{ - Ref: github.String("refs/heads/test-branch"), - Object: &github.GitObject{ - Type: github.String("commit"), - SHA: github.String("sha-hash"), - }, - }, &github.Response{Response: &http.Response{StatusCode: http.StatusOK}}, nil) - return cli - }, - wantErr: false, - }, - { - name: "failed to create reference", - repo: "test-repo", - branch: "test-branch", - injector: func(cli *MockgitHubGitClient) *MockgitHubGitClient { - cli.EXPECT().GetRef(gomock.Any(), "test-owner", "test-repo", "heads/master"). - Return(&github.Reference{ - Ref: github.String("refs/heads/master"), - Object: &github.GitObject{ - Type: github.String("commit"), - SHA: github.String("sha-hash"), - }, - }, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusOK, - }, - }, nil) - cli.EXPECT().CreateRef(gomock.Any(), "test-owner", "test-repo", &github.Reference{ - Ref: github.String("refs/heads/test-branch"), - Object: &github.GitObject{ - Type: github.String("commit"), - SHA: github.String("sha-hash"), - }, - }).Return(nil, &github.Response{ - Response: &http.Response{StatusCode: http.StatusInternalServerError}, - }, errors.New("some error")) - return cli - }, - wantErr: true, - }, - { - name: "failed to get reference", - repo: "test-repo", - branch: "test-branch", - injector: func(cli *MockgitHubGitClient) *MockgitHubGitClient { - cli.EXPECT().GetRef(gomock.Any(), "test-owner", "test-repo", "heads/master"). - Return(nil, &github.Response{ - Response: &http.Response{ - StatusCode: http.StatusInternalServerError, - }, - }, errors.New("some error")) - return cli - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - cli := NewMockgitHubGitClient(ctrl) - cli = tt.injector(cli) - - s := newGitHubClient("test-owner", nil, nil, cli) - - if err := s.createBranch(tt.repo, tt.branch); (err != nil) != tt.wantErr { - t.Errorf("githubClient.createBranch() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - func Test_extractMergedPRsAfter(t *testing.T) { t.Parallel() diff --git a/go.mod b/go.mod index 3ea70b2..13bbc51 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,12 @@ module github.com/p1ass/mikku go 1.13 require ( - github.com/go-yaml/yaml v2.1.0+incompatible github.com/golang/mock v1.4.4 github.com/google/go-cmp v0.5.4 github.com/google/go-github/v32 v32.1.0 github.com/kelseyhightower/envconfig v1.4.0 - github.com/kr/pretty v0.1.0 // indirect github.com/urfave/cli/v2 v2.3.0 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.2.7 // indirect + ) diff --git a/go.sum b/go.sum index 49b4646..75d99d0 100644 --- a/go.sum +++ b/go.sum @@ -2,38 +2,27 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= -github.com/google/go-github/v32 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= -github.com/google/go-github/v32 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4= -github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -47,8 +36,6 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -62,9 +49,4 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/images/diff.png b/images/diff.png deleted file mode 100644 index af8d016..0000000 Binary files a/images/diff.png and /dev/null differ diff --git a/mikku.go b/mikku.go index dc14f1e..0447c10 100644 --- a/mikku.go +++ b/mikku.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "os" - "strings" ) var ( @@ -61,73 +60,3 @@ func Release(repo string, bumpTyp string) error { return nil } - -// PullRequest is the entry point of `mikku pr` command -func PullRequest(repo, manifestRepo, pathToManifestFile, imageName string) error { - cfg, err := readConfig() - if err != nil { - return fmt.Errorf("release: %w", err) - } - if err := cfg.validate(); err != nil { - return fmt.Errorf("invalid config: %w", err) - } - prCfg, err := readPRConfig() - if err != nil { - return fmt.Errorf("release: %w", err) - } - prCfg.overrideConfig(manifestRepo, pathToManifestFile, imageName) - - if err := prCfg.validate(); err != nil { - return fmt.Errorf("invalid pr config: %w", err) - } - - if err := prCfg.embedRepoInfo(cfg.GitHubOwner, repo); err != nil { - return fmt.Errorf("embed owner and repository info: %w", err) - } - - svc := newGitHubClientUsingEnv(cfg.GitHubOwner, cfg.GitHubAccessToken) - - manifest, hash, err := svc.getFile(prCfg.ManifestRepository, prCfg.ManifestFilepath) - if err != nil { - return fmt.Errorf("failed to get manifest file: %w", err) - } - - release, err := svc.getLatestRelease(repo) - if err != nil { - return fmt.Errorf("failed to get latest release: %w", err) - } - tag := release.GetTagName() - - replacedFile, err := replaceTag(manifest, prCfg.DockerImageName, tag) - if err != nil { - return fmt.Errorf("failed to replace tag: %w", err) - } - - branch := fmt.Sprintf("bump-%s-to-%s", prCfg.DockerImageName, tag) - if err := svc.createBranch(prCfg.ManifestRepository, branch); err != nil { - return fmt.Errorf("failed to create branch: %w", err) - } - - commitMessage := fmt.Sprintf("Bump %s to %s", prCfg.DockerImageName, tag) - if err := svc.pushFile(prCfg.ManifestRepository, prCfg.ManifestFilepath, branch, commitMessage, hash, []byte(replacedFile)); err != nil { - return fmt.Errorf("failed to push updated the manifest file: %w", err) - } - - title := fmt.Sprintf("Bump %s to %s", prCfg.DockerImageName, tag) - body := fmt.Sprintf("Bump %s to %s", prCfg.DockerImageName, tag) - pr, err := svc.createPullRequest(prCfg.ManifestRepository, branch, title, body) - if err != nil { - return fmt.Errorf("failed to create a pull request: %w", err) - } - _, _ = fmt.Fprintf(os.Stdout, "Pull request created.\n %s\n", pr.GetHTMLURL()) - - return nil -} - -func replaceTag(manifest, imageName, tag string) (string, error) { - currentTag, err := getCurrentTag(manifest, imageName) - if err != nil { - return "", fmt.Errorf("get current tag in yaml file: %w", err) - } - return strings.ReplaceAll(manifest, imageName+":"+currentTag, imageName+":"+tag), nil -} diff --git a/mock_github.go b/mock_github.go index 458e8f0..efc7683 100644 --- a/mock_github.go +++ b/mock_github.go @@ -6,10 +6,9 @@ package mikku import ( context "context" - reflect "reflect" - gomock "github.com/golang/mock/gomock" github "github.com/google/go-github/v32/github" + reflect "reflect" ) // MockgitHubRepositoriesClient is a mock of gitHubRepositoriesClient interface @@ -67,39 +66,6 @@ func (mr *MockgitHubRepositoriesClientMockRecorder) GetLatestRelease(ctx, owner, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestRelease", reflect.TypeOf((*MockgitHubRepositoriesClient)(nil).GetLatestRelease), ctx, owner, repo) } -// GetContents mocks base method -func (m *MockgitHubRepositoriesClient) GetContents(ctx context.Context, owner, repo, path string, opt *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetContents", ctx, owner, repo, path, opt) - ret0, _ := ret[0].(*github.RepositoryContent) - ret1, _ := ret[1].([]*github.RepositoryContent) - ret2, _ := ret[2].(*github.Response) - ret3, _ := ret[3].(error) - return ret0, ret1, ret2, ret3 -} - -// GetContents indicates an expected call of GetContents -func (mr *MockgitHubRepositoriesClientMockRecorder) GetContents(ctx, owner, repo, path, opt interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContents", reflect.TypeOf((*MockgitHubRepositoriesClient)(nil).GetContents), ctx, owner, repo, path, opt) -} - -// UpdateFile mocks base method -func (m *MockgitHubRepositoriesClient) UpdateFile(ctx context.Context, owner, repo, path string, opt *github.RepositoryContentFileOptions) (*github.RepositoryContentResponse, *github.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateFile", ctx, owner, repo, path, opt) - ret0, _ := ret[0].(*github.RepositoryContentResponse) - ret1, _ := ret[1].(*github.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// UpdateFile indicates an expected call of UpdateFile -func (mr *MockgitHubRepositoriesClientMockRecorder) UpdateFile(ctx, owner, repo, path, opt interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFile", reflect.TypeOf((*MockgitHubRepositoriesClient)(nil).UpdateFile), ctx, owner, repo, path, opt) -} - // MockgitHubPullRequestsClient is a mock of gitHubPullRequestsClient interface type MockgitHubPullRequestsClient struct { ctrl *gomock.Controller @@ -154,58 +120,3 @@ func (mr *MockgitHubPullRequestsClientMockRecorder) Create(ctx, owner, repo, pul mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockgitHubPullRequestsClient)(nil).Create), ctx, owner, repo, pull) } - -// MockgitHubGitClient is a mock of gitHubGitClient interface -type MockgitHubGitClient struct { - ctrl *gomock.Controller - recorder *MockgitHubGitClientMockRecorder -} - -// MockgitHubGitClientMockRecorder is the mock recorder for MockgitHubGitClient -type MockgitHubGitClientMockRecorder struct { - mock *MockgitHubGitClient -} - -// NewMockgitHubGitClient creates a new mock instance -func NewMockgitHubGitClient(ctrl *gomock.Controller) *MockgitHubGitClient { - mock := &MockgitHubGitClient{ctrl: ctrl} - mock.recorder = &MockgitHubGitClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockgitHubGitClient) EXPECT() *MockgitHubGitClientMockRecorder { - return m.recorder -} - -// GetRef mocks base method -func (m *MockgitHubGitClient) GetRef(ctx context.Context, owner, repo, ref string) (*github.Reference, *github.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRef", ctx, owner, repo, ref) - ret0, _ := ret[0].(*github.Reference) - ret1, _ := ret[1].(*github.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetRef indicates an expected call of GetRef -func (mr *MockgitHubGitClientMockRecorder) GetRef(ctx, owner, repo, ref interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRef", reflect.TypeOf((*MockgitHubGitClient)(nil).GetRef), ctx, owner, repo, ref) -} - -// CreateRef mocks base method -func (m *MockgitHubGitClient) CreateRef(ctx context.Context, owner, repo string, ref *github.Reference) (*github.Reference, *github.Response, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateRef", ctx, owner, repo, ref) - ret0, _ := ret[0].(*github.Reference) - ret1, _ := ret[1].(*github.Response) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// CreateRef indicates an expected call of CreateRef -func (mr *MockgitHubGitClientMockRecorder) CreateRef(ctx, owner, repo, ref interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRef", reflect.TypeOf((*MockgitHubGitClient)(nil).CreateRef), ctx, owner, repo, ref) -} diff --git a/yaml.go b/yaml.go deleted file mode 100644 index b16a796..0000000 --- a/yaml.go +++ /dev/null @@ -1,55 +0,0 @@ -package mikku - -import ( - "errors" - "fmt" - "strings" - - "github.com/go-yaml/yaml" -) - -var ( - errImageNotFoundInYAML = errors.New("image not found in yaml file") - errInvalidYAML = errors.New("invalid yaml file") -) - -func getCurrentTag(yamlStr, imageName string) (string, error) { - var obj interface{} - if err := yaml.Unmarshal([]byte(yamlStr), &obj); err != nil { - return "", fmt.Errorf("yaml unmarshal: %w", err) - } - - if v, ok := obj.(map[interface{}]interface{}); ok { - tag := traverse(v, imageName) - if tag == "" { - return "", fmt.Errorf("%s: %w", imageName, errImageNotFoundInYAML) - } - return tag, nil - } - return "", errInvalidYAML -} - -func traverse(node map[interface{}]interface{}, imageName string) string { - for key, value := range node { - switch val := value.(type) { - case string: - if strings.Contains(val, imageName) && key == "image" { - split := strings.Split(val, ":") - return split[len(split)-1] - } - case map[interface{}]interface{}: - if tag := traverse(val, imageName); tag != "" { - return tag - } - case []interface{}: - for _, v := range val { - if convert, ok := v.(map[interface{}]interface{}); ok { - if tag := traverse(convert, imageName); tag != "" { - return tag - } - } - } - } - } - return "" -} diff --git a/yaml_test.go b/yaml_test.go deleted file mode 100644 index 5db908c..0000000 --- a/yaml_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package mikku - -import ( - "errors" - "testing" -) - -func Test_getCurrentTag(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - yamlStr string - imageName string - want string - wantErr error - }{ - { - name: "contains image", - yamlStr: ` -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: test-deployment - labels: - app: test-app -spec: - replicas: 2 - selector: - matchLabels: - app: test-app - template: - metadata: - labels: - app: test-app - spec: - containers: - - name: test-container - image: asia.gcr.io/test/test-container:v1.0.0 - ports: - - containerPort: 8080 -`, - imageName: "asia.gcr.io/test/test-container", - want: "v1.0.0", - wantErr: nil, - }, - { - name: "not contain image", - yamlStr: ` -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: test-deployment - labels: - app: test-app -spec: - replicas: 2 - selector: - matchLabels: - app: test-app - template: - metadata: - labels: - app: test-app - spec: - containers: - - name: test-container - image: asia.gcr.io/test/test-container:v1.0.0 - ports: - - containerPort: 8080 -`, - imageName: "asia.gcr.io/test/not-exist-container", - want: "", - wantErr: errImageNotFoundInYAML, - }, - { - name: "invalid yaml", - yamlStr: ``, - imageName: "asia.gcr.io/test/not-exist-container", - want: "", - wantErr: errImageNotFoundInYAML, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := getCurrentTag(tt.yamlStr, tt.imageName) - if (tt.wantErr == nil && err != nil) || (tt.wantErr != nil && !errors.As(err, &tt.wantErr)) { - t.Errorf("getCurrentTag() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("getCurrentTag() = %v, wantContent %v", got, tt.want) - } - }) - } -}