mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-29 21:31:02 +00:00
Ignore pipelines without config (#2949)
Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
parent
65c62e987f
commit
619858e0e9
8 changed files with 85 additions and 27 deletions
|
@ -5,6 +5,7 @@ Some versions need some changes to the server configuration or the pipeline conf
|
||||||
## `next`
|
## `next`
|
||||||
|
|
||||||
- Removed `WOODPECKER_ROOT_PATH` and `WOODPECKER_ROOT_URL` config variables. Use `WOODPECKER_HOST` with a path instead
|
- Removed `WOODPECKER_ROOT_PATH` and `WOODPECKER_ROOT_URL` config variables. Use `WOODPECKER_HOST` with a path instead
|
||||||
|
- Pipelines without a config file will now be skipped instead of failing
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ func handlePipelineErr(c *gin.Context, err error) {
|
||||||
} else if errors.Is(err, &pipeline.ErrBadRequest{}) {
|
} else if errors.Is(err, &pipeline.ErrBadRequest{}) {
|
||||||
c.String(http.StatusBadRequest, "%s", err)
|
c.String(http.StatusBadRequest, "%s", err)
|
||||||
} else if errors.Is(err, pipeline.ErrFiltered) {
|
} else if errors.Is(err, pipeline.ErrFiltered) {
|
||||||
|
// for debugging purpose we add a header
|
||||||
|
c.Writer.Header().Add("Pipeline-Filtered", "true")
|
||||||
c.Status(http.StatusNoContent)
|
c.Status(http.StatusNoContent)
|
||||||
} else {
|
} else {
|
||||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
|
|
@ -106,7 +106,7 @@ func (cf *configFetcher) fetch(c context.Context, timeout time.Duration, config
|
||||||
// could be adapted to allow the user to supply a list like we do in the defaults
|
// could be adapted to allow the user to supply a list like we do in the defaults
|
||||||
configs := []string{config}
|
configs := []string{config}
|
||||||
|
|
||||||
fileMeta, err := cf.getFirstAvailableConfig(ctx, configs, true)
|
fileMeta, err := cf.getFirstAvailableConfig(ctx, configs)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fileMeta, err
|
return fileMeta, err
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ func (cf *configFetcher) fetch(c context.Context, timeout time.Duration, config
|
||||||
|
|
||||||
log.Trace().Msgf("ConfigFetch[%s]: user did not define own config, following default procedure", cf.repo.FullName)
|
log.Trace().Msgf("ConfigFetch[%s]: user did not define own config, following default procedure", cf.repo.FullName)
|
||||||
// for the order see shared/constants/constants.go
|
// for the order see shared/constants/constants.go
|
||||||
fileMeta, err := cf.getFirstAvailableConfig(ctx, constant.DefaultConfigOrder[:], false)
|
fileMeta, err := cf.getFirstAvailableConfig(ctx, constant.DefaultConfigOrder[:])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fileMeta, err
|
return fileMeta, err
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ func filterPipelineFiles(files []*types.FileMeta) []*types.FileMeta {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cf *configFetcher) checkPipelineFile(c context.Context, config string) (fileMeta []*types.FileMeta, found bool) {
|
func (cf *configFetcher) checkPipelineFile(c context.Context, config string) ([]*types.FileMeta, error) {
|
||||||
file, err := cf.forge.File(c, cf.user, cf.repo, cf.pipeline, config)
|
file, err := cf.forge.File(c, cf.user, cf.repo, cf.pipeline, config)
|
||||||
|
|
||||||
if err == nil && len(file) != 0 {
|
if err == nil && len(file) != 0 {
|
||||||
|
@ -150,40 +150,47 @@ func (cf *configFetcher) checkPipelineFile(c context.Context, config string) (fi
|
||||||
return []*types.FileMeta{{
|
return []*types.FileMeta{{
|
||||||
Name: config,
|
Name: config,
|
||||||
Data: file,
|
Data: file,
|
||||||
}}, true
|
}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, false
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
func (cf *configFetcher) getFirstAvailableConfig(c context.Context, configs []string, userDefined bool) ([]*types.FileMeta, error) {
|
|
||||||
userDefinedLog := ""
|
|
||||||
if userDefined {
|
|
||||||
userDefinedLog = "user defined"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cf *configFetcher) getFirstAvailableConfig(c context.Context, configs []string) ([]*types.FileMeta, error) {
|
||||||
|
var forgeErr []error
|
||||||
for _, fileOrFolder := range configs {
|
for _, fileOrFolder := range configs {
|
||||||
if strings.HasSuffix(fileOrFolder, "/") {
|
if strings.HasSuffix(fileOrFolder, "/") {
|
||||||
// config is a folder
|
// config is a folder
|
||||||
files, err := cf.forge.Dir(c, cf.user, cf.repo, cf.pipeline, strings.TrimSuffix(fileOrFolder, "/"))
|
files, err := cf.forge.Dir(c, cf.user, cf.repo, cf.pipeline, strings.TrimSuffix(fileOrFolder, "/"))
|
||||||
// if folder is not supported we will get a "Not implemented" error and continue
|
// if folder is not supported we will get a "Not implemented" error and continue
|
||||||
if err != nil && !errors.Is(err, types.ErrNotImplemented) {
|
if err != nil {
|
||||||
|
if !(errors.Is(err, types.ErrNotImplemented) || errors.Is(err, &types.ErrConfigNotFound{})) {
|
||||||
log.Error().Err(err).Str("repo", cf.repo.FullName).Str("user", cf.user.Login).Msg("could not get folder from forge")
|
log.Error().Err(err).Str("repo", cf.repo.FullName).Str("user", cf.user.Login).Msg("could not get folder from forge")
|
||||||
|
forgeErr = append(forgeErr, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
files = filterPipelineFiles(files)
|
files = filterPipelineFiles(files)
|
||||||
if err == nil && len(files) != 0 {
|
if len(files) != 0 {
|
||||||
log.Trace().Msgf("ConfigFetch[%s]: found %d %s files in '%s'", cf.repo.FullName, len(files), userDefinedLog, fileOrFolder)
|
log.Trace().Msgf("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), fileOrFolder)
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// config is a file
|
// config is a file
|
||||||
if fileMeta, found := cf.checkPipelineFile(c, fileOrFolder); found {
|
if fileMeta, err := cf.checkPipelineFile(c, fileOrFolder); err == nil {
|
||||||
log.Trace().Msgf("ConfigFetch[%s]: found %s file: '%s'", cf.repo.FullName, userDefinedLog, fileOrFolder)
|
log.Trace().Msgf("ConfigFetch[%s]: found file: '%s'", cf.repo.FullName, fileOrFolder)
|
||||||
return fileMeta, nil
|
return fileMeta, nil
|
||||||
|
} else if !errors.Is(err, &types.ErrConfigNotFound{}) {
|
||||||
|
forgeErr = append(forgeErr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nothing found
|
// got unexpected errors
|
||||||
return nil, fmt.Errorf("%s configs not found searched: %s", userDefinedLog, strings.Join(configs, ", "))
|
if len(forgeErr) != 0 {
|
||||||
|
return nil, errors.Join(forgeErr...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing found
|
||||||
|
return nil, &types.ErrConfigNotFound{Configs: configs}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/common"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
||||||
|
@ -289,7 +290,10 @@ func (c *Gitea) File(ctx context.Context, u *model.User, r *model.Repo, b *model
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, _, err := client.GetFile(r.Owner, r.Name, b.Commit, f)
|
cfg, resp, err := client.GetFile(r.Owner, r.Name, b.Commit, f)
|
||||||
|
if err != nil && resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||||
|
return nil, errors.Join(err, &types.ErrConfigNotFound{Configs: []string{f}})
|
||||||
|
}
|
||||||
return cfg, err
|
return cfg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,6 +318,9 @@ func (c *Gitea) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.
|
||||||
if m, _ := filepath.Match(f, e.Path); m && e.Type == "blob" {
|
if m, _ := filepath.Match(f, e.Path); m && e.Type == "blob" {
|
||||||
data, err := c.File(ctx, u, r, b, e.Path)
|
data, err := c.File(ctx, u, r, b, e.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, &types.ErrConfigNotFound{}) {
|
||||||
|
return nil, fmt.Errorf("git tree reported existence of file but we got: %s", err.Error())
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("multi-pipeline cannot get %s: %w", e.Path, err)
|
return nil, fmt.Errorf("multi-pipeline cannot get %s: %w", e.Path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ package github
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -32,6 +33,7 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/common"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
||||||
|
@ -226,7 +228,10 @@ func (c *client) File(ctx context.Context, u *model.User, r *model.Repo, b *mode
|
||||||
|
|
||||||
opts := new(github.RepositoryContentGetOptions)
|
opts := new(github.RepositoryContentGetOptions)
|
||||||
opts.Ref = b.Commit
|
opts.Ref = b.Commit
|
||||||
content, _, _, err := client.Repositories.GetContents(ctx, r.Owner, r.Name, f, opts)
|
content, _, resp, err := client.Repositories.GetContents(ctx, r.Owner, r.Name, f, opts)
|
||||||
|
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||||
|
return nil, errors.Join(err, &types.ErrConfigNotFound{Configs: []string{f}})
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -242,7 +247,10 @@ func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model
|
||||||
|
|
||||||
opts := new(github.RepositoryContentGetOptions)
|
opts := new(github.RepositoryContentGetOptions)
|
||||||
opts.Ref = b.Commit
|
opts.Ref = b.Commit
|
||||||
_, data, _, err := client.Repositories.GetContents(ctx, r.Owner, r.Name, f, opts)
|
_, data, resp, err := client.Repositories.GetContents(ctx, r.Owner, r.Name, f, opts)
|
||||||
|
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||||
|
return nil, errors.Join(err, &types.ErrConfigNotFound{Configs: []string{f}})
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -254,6 +262,9 @@ func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model
|
||||||
go func(path string) {
|
go func(path string) {
|
||||||
content, err := c.File(ctx, u, r, b, path)
|
content, err := c.File(ctx, u, r, b, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, &types.ErrConfigNotFound{}) {
|
||||||
|
err = fmt.Errorf("git tree reported existence of file but we got: %s", err.Error())
|
||||||
|
}
|
||||||
errc <- err
|
errc <- err
|
||||||
} else {
|
} else {
|
||||||
fc <- &forge_types.FileMeta{
|
fc <- &forge_types.FileMeta{
|
||||||
|
|
|
@ -18,6 +18,7 @@ package gitlab
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -33,6 +34,7 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/common"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
||||||
|
@ -351,7 +353,10 @@ func (g *GitLab) File(ctx context.Context, user *model.User, repo *model.Repo, p
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
file, _, err := client.RepositoryFiles.GetRawFile(_repo.ID, fileName, &gitlab.GetRawFileOptions{Ref: &pipeline.Commit}, gitlab.WithContext(ctx))
|
file, resp, err := client.RepositoryFiles.GetRawFile(_repo.ID, fileName, &gitlab.GetRawFileOptions{Ref: &pipeline.Commit}, gitlab.WithContext(ctx))
|
||||||
|
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||||
|
return nil, errors.Join(err, &types.ErrConfigNotFound{Configs: []string{fileName}})
|
||||||
|
}
|
||||||
return file, err
|
return file, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,6 +393,9 @@ func (g *GitLab) Dir(ctx context.Context, user *model.User, repo *model.Repo, pi
|
||||||
}
|
}
|
||||||
data, err := g.File(ctx, user, repo, pipeline, batch[i].Path)
|
data, err := g.File(ctx, user, repo, pipeline, batch[i].Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, &types.ErrConfigNotFound{}) {
|
||||||
|
return nil, fmt.Errorf("git tree reported existence of file but we got: %s", err.Error())
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
files = append(files, &forge_types.FileMeta{
|
files = append(files, &forge_types.FileMeta{
|
||||||
|
|
|
@ -18,6 +18,7 @@ package types
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthError represents forge authentication error.
|
// AuthError represents forge authentication error.
|
||||||
|
@ -56,3 +57,16 @@ func (*ErrIgnoreEvent) Is(target error) bool {
|
||||||
_, ok := target.(*ErrIgnoreEvent) //nolint:errorlint
|
_, ok := target.(*ErrIgnoreEvent) //nolint:errorlint
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrConfigNotFound struct {
|
||||||
|
Configs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ErrConfigNotFound) Error() string {
|
||||||
|
return fmt.Sprintf("configs not found: %s", strings.Join(m.Configs, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ErrConfigNotFound) Is(target error) bool {
|
||||||
|
_, ok := target.(*ErrConfigNotFound) //nolint:errorlint
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
|
@ -16,14 +16,16 @@ package pipeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
pipeline_errors "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
|
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
||||||
)
|
)
|
||||||
|
@ -64,18 +66,24 @@ func Create(ctx context.Context, _store store.Store, repo *model.Repo, pipeline
|
||||||
// fetch the pipeline file from the forge
|
// fetch the pipeline file from the forge
|
||||||
configFetcher := forge.NewConfigFetcher(server.Config.Services.Forge, server.Config.Services.Timeout, server.Config.Services.ConfigService, repoUser, repo, pipeline)
|
configFetcher := forge.NewConfigFetcher(server.Config.Services.Forge, server.Config.Services.Timeout, server.Config.Services.ConfigService, repoUser, repo, pipeline)
|
||||||
forgeYamlConfigs, configFetchErr := configFetcher.Fetch(ctx)
|
forgeYamlConfigs, configFetchErr := configFetcher.Fetch(ctx)
|
||||||
|
if errors.Is(configFetchErr, &forge_types.ErrConfigNotFound{}) {
|
||||||
if configFetchErr != nil {
|
|
||||||
log.Debug().Str("repo", repo.FullName).Err(configFetchErr).Msgf("cannot find config '%s' in '%s' with user: '%s'", repo.Config, pipeline.Ref, repoUser.Login)
|
log.Debug().Str("repo", repo.FullName).Err(configFetchErr).Msgf("cannot find config '%s' in '%s' with user: '%s'", repo.Config, pipeline.Ref, repoUser.Login)
|
||||||
|
if err := _store.DeletePipeline(pipeline); err != nil {
|
||||||
|
log.Error().Str("repo", repo.FullName).Err(err).Msg("failed to delete pipeline without config")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrFiltered
|
||||||
|
} else if configFetchErr != nil {
|
||||||
|
log.Debug().Str("repo", repo.FullName).Err(configFetchErr).Msgf("error while fetching config '%s' in '%s' with user: '%s'", repo.Config, pipeline.Ref, repoUser.Login)
|
||||||
return nil, updatePipelineWithErr(ctx, _store, pipeline, repo, repoUser, fmt.Errorf("pipeline definition not found in %s", repo.FullName))
|
return nil, updatePipelineWithErr(ctx, _store, pipeline, repo, repoUser, fmt.Errorf("pipeline definition not found in %s", repo.FullName))
|
||||||
}
|
}
|
||||||
|
|
||||||
pipelineItems, parseErr := parsePipeline(_store, pipeline, repoUser, repo, forgeYamlConfigs, nil)
|
pipelineItems, parseErr := parsePipeline(_store, pipeline, repoUser, repo, forgeYamlConfigs, nil)
|
||||||
if errors.HasBlockingErrors(parseErr) {
|
if pipeline_errors.HasBlockingErrors(parseErr) {
|
||||||
log.Debug().Str("repo", repo.FullName).Err(parseErr).Msg("failed to parse yaml")
|
log.Debug().Str("repo", repo.FullName).Err(parseErr).Msg("failed to parse yaml")
|
||||||
return nil, updatePipelineWithErr(ctx, _store, pipeline, repo, repoUser, parseErr)
|
return nil, updatePipelineWithErr(ctx, _store, pipeline, repo, repoUser, parseErr)
|
||||||
} else if parseErr != nil {
|
} else if parseErr != nil {
|
||||||
pipeline.Errors = errors.GetPipelineErrors(parseErr)
|
pipeline.Errors = pipeline_errors.GetPipelineErrors(parseErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(pipelineItems) == 0 {
|
if len(pipelineItems) == 0 {
|
||||||
|
|
Loading…
Reference in a new issue