mirror of https://gitlab.com/gitlab-org/cli.git
feat(ci): add compile command to show full ci config
This commit is contained in:
parent
b0ef650202
commit
323ee63bd1
|
@ -2,6 +2,7 @@ package ci
|
|||
|
||||
import (
|
||||
jobArtifactCmd "gitlab.com/gitlab-org/cli/commands/ci/artifact"
|
||||
ciConfigCmd "gitlab.com/gitlab-org/cli/commands/ci/config"
|
||||
pipeDeleteCmd "gitlab.com/gitlab-org/cli/commands/ci/delete"
|
||||
pipeGetCmd "gitlab.com/gitlab-org/cli/commands/ci/get"
|
||||
legacyCICmd "gitlab.com/gitlab-org/cli/commands/ci/legacyci"
|
||||
|
@ -40,5 +41,6 @@ func NewCmdCI(f *cmdutils.Factory) *cobra.Command {
|
|||
ciCmd.AddCommand(pipeTriggerCmd.NewCmdTrigger(f))
|
||||
ciCmd.AddCommand(jobArtifactCmd.NewCmdRun(f))
|
||||
ciCmd.AddCommand(pipeGetCmd.NewCmdGet(f))
|
||||
ciCmd.AddCommand(ciConfigCmd.NewCmdConfig(f))
|
||||
return ciCmd
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package compile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gitlab.com/gitlab-org/cli/api"
|
||||
"gitlab.com/gitlab-org/cli/commands/cmdutils"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdConfigCompile(f *cmdutils.Factory) *cobra.Command {
|
||||
configCompileCmd := &cobra.Command{
|
||||
Use: "compile",
|
||||
Short: "View the fully expanded CI/CD configuration.",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Example: heredoc.Doc(`
|
||||
# Uses .gitlab-ci.yml in the current directory
|
||||
$ glab ci config compile
|
||||
|
||||
$ glab ci config compile .gitlab-ci.yml
|
||||
|
||||
$ glab ci config compile path/to/.gitlab-ci.yml
|
||||
`),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
path := ".gitlab-ci.yml"
|
||||
if len(args) == 1 {
|
||||
path = args[0]
|
||||
}
|
||||
return compileRun(f, path)
|
||||
},
|
||||
}
|
||||
|
||||
configCompileCmd.SetHelpFunc(func(command *cobra.Command, strings []string) {
|
||||
// Hide "repo"-flag for this command, because it cannot be used on repositories but only on gitlab-ci files
|
||||
_ = configCompileCmd.Flags().MarkHidden("repo")
|
||||
|
||||
configCompileCmd.Parent().HelpFunc()(command, strings)
|
||||
})
|
||||
|
||||
return configCompileCmd
|
||||
}
|
||||
|
||||
func compileRun(f *cmdutils.Factory, path string) error {
|
||||
var err error
|
||||
|
||||
apiClient, err := f.HttpClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repo, err := f.BaseRepo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("You must be in a GitLab project repository for this action: %w", err)
|
||||
}
|
||||
|
||||
project, err := repo.Project(apiClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("You must be in a GitLab project repository for this action: %w", err)
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading ci config at %s: %w", path, err)
|
||||
}
|
||||
|
||||
compiledResult, err := api.ProjectNamespaceLint(apiClient, project.ID, string(content), "", false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !compiledResult.Valid {
|
||||
errorsStr := strings.Join(compiledResult.Errors, ", ")
|
||||
return fmt.Errorf("could not compile %s: %s", path, errorsStr)
|
||||
}
|
||||
|
||||
fmt.Print(compiledResult.MergedYaml)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package compile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"gitlab.com/gitlab-org/cli/commands/cmdtest"
|
||||
"gitlab.com/gitlab-org/cli/internal/glrepo"
|
||||
"gitlab.com/gitlab-org/cli/pkg/httpmock"
|
||||
"gitlab.com/gitlab-org/cli/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_compileRun(t *testing.T) {
|
||||
type httpMock struct {
|
||||
method string
|
||||
path string
|
||||
status int
|
||||
body string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
testFile string
|
||||
StdOut string
|
||||
wantErr bool
|
||||
errMsg string
|
||||
httpMocks []httpMock
|
||||
showHaveBaseRepo bool
|
||||
}{
|
||||
{
|
||||
name: "with invalid path specified",
|
||||
testFile: "WRONG_PATH",
|
||||
StdOut: "",
|
||||
wantErr: true,
|
||||
errMsg: "WRONG_PATH: no such file or directory",
|
||||
showHaveBaseRepo: true,
|
||||
httpMocks: []httpMock{
|
||||
{
|
||||
http.MethodGet,
|
||||
"/api/v4/projects/OWNER/REPO",
|
||||
http.StatusOK,
|
||||
`{
|
||||
"id": 123,
|
||||
"iid": 123
|
||||
}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "without base repo",
|
||||
testFile: ".gitlab.ci.yml",
|
||||
StdOut: "",
|
||||
wantErr: true,
|
||||
errMsg: "You must be in a GitLab project repository for this action: no base repository present",
|
||||
showHaveBaseRepo: false,
|
||||
httpMocks: []httpMock{},
|
||||
},
|
||||
{
|
||||
name: "when a valid path is specified and yaml is valid",
|
||||
testFile: ".gitlab-ci.yml",
|
||||
StdOut: "",
|
||||
wantErr: false,
|
||||
errMsg: "",
|
||||
showHaveBaseRepo: true,
|
||||
httpMocks: []httpMock{
|
||||
{
|
||||
http.MethodGet,
|
||||
"/api/v4/projects/OWNER/REPO",
|
||||
http.StatusOK,
|
||||
`{
|
||||
"id": 123,
|
||||
"iid": 123
|
||||
}`,
|
||||
},
|
||||
{
|
||||
http.MethodPost,
|
||||
"/api/v4/projects/123/ci/lint",
|
||||
http.StatusOK,
|
||||
`{
|
||||
"valid": true
|
||||
}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fakeHTTP := httpmock.New()
|
||||
defer fakeHTTP.Verify(t)
|
||||
|
||||
for _, mock := range tt.httpMocks {
|
||||
fakeHTTP.RegisterResponder(mock.method, mock.path, httpmock.NewStringResponse(mock.status, mock.body))
|
||||
}
|
||||
|
||||
args := path.Join(cmdtest.ProjectPath, "test/testdata", tt.testFile)
|
||||
|
||||
result, err := runCommand(t, fakeHTTP, false, args, tt.showHaveBaseRepo)
|
||||
if tt.wantErr {
|
||||
require.Contains(t, err.Error(), tt.errMsg)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.StdOut, result.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(t *testing.T, rt http.RoundTripper, isTTY bool, cli string, showHaveBaseRepo bool) (*test.CmdOut, error) {
|
||||
ios, _, stdout, stderr := cmdtest.InitIOStreams(isTTY, "")
|
||||
|
||||
factory := cmdtest.InitFactory(ios, rt)
|
||||
|
||||
if !showHaveBaseRepo {
|
||||
factory.BaseRepo = func() (glrepo.Interface, error) {
|
||||
return nil, fmt.Errorf("no base repository present")
|
||||
}
|
||||
}
|
||||
|
||||
_, err := factory.HttpClient()
|
||||
require.Nil(t, err)
|
||||
|
||||
cmd := NewCmdConfigCompile(factory)
|
||||
return cmdtest.ExecuteCommand(cmd, cli, stdout, stderr)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
ConfigCompileCmd "gitlab.com/gitlab-org/cli/commands/ci/config/compile"
|
||||
"gitlab.com/gitlab-org/cli/commands/cmdutils"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdConfig(f *cmdutils.Factory) *cobra.Command {
|
||||
ConfigCmd := &cobra.Command{
|
||||
Use: "config <command> [flags]",
|
||||
Short: `Work with GitLab CI/CD configuration.`,
|
||||
Long: ``,
|
||||
}
|
||||
ConfigCmd.AddCommand(ConfigCompileCmd.NewCmdConfigCompile(f))
|
||||
return ConfigCmd
|
||||
}
|
|
@ -29,7 +29,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
projectPath string
|
||||
ProjectPath string
|
||||
GlabBinaryPath string
|
||||
CachedTestFactory *cmdutils.Factory
|
||||
)
|
||||
|
@ -47,9 +47,9 @@ func init() {
|
|||
if err != nil {
|
||||
log.Fatalln("Failed to get root directory: ", err)
|
||||
}
|
||||
projectPath = strings.TrimSuffix(path.String(), "\n")
|
||||
if !strings.HasSuffix(projectPath, "/") {
|
||||
projectPath += "/"
|
||||
ProjectPath = strings.TrimSuffix(path.String(), "\n")
|
||||
if !strings.HasSuffix(ProjectPath, "/") {
|
||||
ProjectPath += "/"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,11 +57,11 @@ func InitTest(m *testing.M, suffix string) {
|
|||
// Build a glab binary with test symbols. If the parent test binary was run
|
||||
// with coverage enabled, enable coverage on the child binary, too.
|
||||
var err error
|
||||
GlabBinaryPath, err = filepath.Abs(os.ExpandEnv(projectPath + "testdata/glab.test"))
|
||||
GlabBinaryPath, err = filepath.Abs(os.ExpandEnv(ProjectPath + "testdata/glab.test"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
testCmd := []string{"test", "-c", "-o", GlabBinaryPath, projectPath + "cmd/glab"}
|
||||
testCmd := []string{"test", "-c", "-o", GlabBinaryPath, ProjectPath + "cmd/glab"}
|
||||
if coverMode := testing.CoverMode(); coverMode != "" {
|
||||
testCmd = append(testCmd, "-covermode", coverMode, "-coverpkg", "./...")
|
||||
}
|
||||
|
@ -187,11 +187,11 @@ func CopyTestRepo(log fatalLogger, name string) string {
|
|||
if name == "" {
|
||||
name = strconv.Itoa(int(rand.Uint64()))
|
||||
}
|
||||
dest, err := filepath.Abs(os.ExpandEnv(projectPath + "test/testdata-" + name))
|
||||
dest, err := filepath.Abs(os.ExpandEnv(ProjectPath + "test/testdata-" + name))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
src, err := filepath.Abs(os.ExpandEnv(projectPath + "test/testdata"))
|
||||
src, err := filepath.Abs(os.ExpandEnv(ProjectPath + "test/testdata"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Code Review
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
<!--
|
||||
This documentation is auto generated by a script.
|
||||
Please do not edit this file directly. Run `make gen-docs` instead.
|
||||
-->
|
||||
|
||||
# `glab ci config compile`
|
||||
|
||||
View the fully expanded CI/CD configuration.
|
||||
|
||||
```plaintext
|
||||
glab ci config compile [flags]
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```plaintext
|
||||
# Uses .gitlab-ci.yml in the current directory
|
||||
$ glab ci config compile
|
||||
|
||||
$ glab ci config compile .gitlab-ci.yml
|
||||
|
||||
$ glab ci config compile path/to/.gitlab-ci.yml
|
||||
|
||||
```
|
||||
|
||||
## Options inherited from parent commands
|
||||
|
||||
```plaintext
|
||||
--help Show help for command
|
||||
-R, --repo OWNER/REPO Select another repository using the OWNER/REPO or `GROUP/NAMESPACE/REPO` format or full URL or git URL
|
||||
```
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Code Review
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
<!--
|
||||
This documentation is auto generated by a script.
|
||||
Please do not edit this file directly. Run `make gen-docs` instead.
|
||||
-->
|
||||
|
||||
# `glab ci config`
|
||||
|
||||
Work with GitLab CI/CD configuration.
|
||||
|
||||
## Options inherited from parent commands
|
||||
|
||||
```plaintext
|
||||
--help Show help for command
|
||||
-R, --repo OWNER/REPO Select another repository using the OWNER/REPO or `GROUP/NAMESPACE/REPO` format or full URL or git URL
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
- [`compile`](compile.md)
|
|
@ -36,6 +36,7 @@ pipeline
|
|||
|
||||
- [`artifact`](artifact.md)
|
||||
- [`ci`](ci/index.md)
|
||||
- [`config`](config/index.md)
|
||||
- [`delete`](delete.md)
|
||||
- [`get`](get.md)
|
||||
- [`lint`](lint.md)
|
||||
|
|
Loading…
Reference in New Issue