mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-08-08 14:02:29 +00:00
Resolve built-in variables for global when filter (#1790)
addresses
bd461477bd
close #1244, close #1580
---------
Co-authored-by: Anbraten <anton@ju60.de>
This commit is contained in:
parent
c919f32e0b
commit
ea895baf83
@ -32,7 +32,6 @@ import (
|
|||||||
"github.com/woodpecker-ci/woodpecker/pipeline/backend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/backend"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
"github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
||||||
backendTypes "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
backendTypes "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/compiler"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/compiler"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/linter"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/linter"
|
||||||
@ -232,81 +231,6 @@ func execWithAxis(c *cli.Context, file, repoPath string, axis matrix.Axis) error
|
|||||||
).Run(c.Context)
|
).Run(c.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the metadata from the cli context.
|
|
||||||
func metadataFromContext(c *cli.Context, axis matrix.Axis) frontend.Metadata {
|
|
||||||
platform := c.String("system-platform")
|
|
||||||
if platform == "" {
|
|
||||||
platform = runtime.GOOS + "/" + runtime.GOARCH
|
|
||||||
}
|
|
||||||
|
|
||||||
return frontend.Metadata{
|
|
||||||
Repo: frontend.Repo{
|
|
||||||
Name: c.String("repo-name"),
|
|
||||||
Link: c.String("repo-link"),
|
|
||||||
CloneURL: c.String("repo-clone-url"),
|
|
||||||
Private: c.Bool("repo-private"),
|
|
||||||
},
|
|
||||||
Curr: frontend.Pipeline{
|
|
||||||
Number: c.Int64("pipeline-number"),
|
|
||||||
Parent: c.Int64("pipeline-parent"),
|
|
||||||
Created: c.Int64("pipeline-created"),
|
|
||||||
Started: c.Int64("pipeline-started"),
|
|
||||||
Finished: c.Int64("pipeline-finished"),
|
|
||||||
Status: c.String("pipeline-status"),
|
|
||||||
Event: c.String("pipeline-event"),
|
|
||||||
Link: c.String("pipeline-link"),
|
|
||||||
Target: c.String("pipeline-target"),
|
|
||||||
Commit: frontend.Commit{
|
|
||||||
Sha: c.String("commit-sha"),
|
|
||||||
Ref: c.String("commit-ref"),
|
|
||||||
Refspec: c.String("commit-refspec"),
|
|
||||||
Branch: c.String("commit-branch"),
|
|
||||||
Message: c.String("commit-message"),
|
|
||||||
Author: frontend.Author{
|
|
||||||
Name: c.String("commit-author-name"),
|
|
||||||
Email: c.String("commit-author-email"),
|
|
||||||
Avatar: c.String("commit-author-avatar"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Prev: frontend.Pipeline{
|
|
||||||
Number: c.Int64("prev-pipeline-number"),
|
|
||||||
Created: c.Int64("prev-pipeline-created"),
|
|
||||||
Started: c.Int64("prev-pipeline-started"),
|
|
||||||
Finished: c.Int64("prev-pipeline-finished"),
|
|
||||||
Status: c.String("prev-pipeline-status"),
|
|
||||||
Event: c.String("prev-pipeline-event"),
|
|
||||||
Link: c.String("prev-pipeline-link"),
|
|
||||||
Commit: frontend.Commit{
|
|
||||||
Sha: c.String("prev-commit-sha"),
|
|
||||||
Ref: c.String("prev-commit-ref"),
|
|
||||||
Refspec: c.String("prev-commit-refspec"),
|
|
||||||
Branch: c.String("prev-commit-branch"),
|
|
||||||
Message: c.String("prev-commit-message"),
|
|
||||||
Author: frontend.Author{
|
|
||||||
Name: c.String("prev-commit-author-name"),
|
|
||||||
Email: c.String("prev-commit-author-email"),
|
|
||||||
Avatar: c.String("prev-commit-author-avatar"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Workflow: frontend.Workflow{
|
|
||||||
Name: c.String("workflow-name"),
|
|
||||||
Number: c.Int("workflow-number"),
|
|
||||||
Matrix: axis,
|
|
||||||
},
|
|
||||||
Step: frontend.Step{
|
|
||||||
Name: c.String("step-name"),
|
|
||||||
Number: c.Int("step-number"),
|
|
||||||
},
|
|
||||||
Sys: frontend.System{
|
|
||||||
Name: c.String("system-name"),
|
|
||||||
Link: c.String("system-link"),
|
|
||||||
Platform: platform,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertPathForWindows(path string) string {
|
func convertPathForWindows(path string) string {
|
||||||
base := filepath.VolumeName(path)
|
base := filepath.VolumeName(path)
|
||||||
if len(base) == 2 {
|
if len(base) == 2 {
|
||||||
|
@ -115,8 +115,13 @@ var flags = []cli.Flag{
|
|||||||
Value: "https://github.com/woodpecker-ci/woodpecker",
|
Value: "https://github.com/woodpecker-ci/woodpecker",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
EnvVars: []string{"CI_REPO_NAME"},
|
EnvVars: []string{"CI_REPO"},
|
||||||
Name: "repo-name",
|
Name: "repo",
|
||||||
|
Usage: "full repo name",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
EnvVars: []string{"CI_REPO_REMOTE_ID"},
|
||||||
|
Name: "repo-remote-id",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
EnvVars: []string{"CI_REPO_URL", "CI_REPO_LINK"},
|
EnvVars: []string{"CI_REPO_URL", "CI_REPO_LINK"},
|
||||||
@ -130,6 +135,10 @@ var flags = []cli.Flag{
|
|||||||
EnvVars: []string{"CI_REPO_PRIVATE"},
|
EnvVars: []string{"CI_REPO_PRIVATE"},
|
||||||
Name: "repo-private",
|
Name: "repo-private",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
EnvVars: []string{"CI_REPO_TRUSTED"},
|
||||||
|
Name: "repo-trusted",
|
||||||
|
},
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
EnvVars: []string{"CI_PIPELINE_NUMBER"},
|
EnvVars: []string{"CI_PIPELINE_NUMBER"},
|
||||||
Name: "pipeline-number",
|
Name: "pipeline-number",
|
||||||
@ -275,6 +284,14 @@ var flags = []cli.Flag{
|
|||||||
EnvVars: []string{"CI_ENV"},
|
EnvVars: []string{"CI_ENV"},
|
||||||
Name: "env",
|
Name: "env",
|
||||||
},
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
EnvVars: []string{"CI_FORGE_TYPE"},
|
||||||
|
Name: "forge-type",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
EnvVars: []string{"CI_FORGE_URL"},
|
||||||
|
Name: "forge-url",
|
||||||
|
},
|
||||||
|
|
||||||
// backend docker
|
// backend docker
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
|
117
cli/exec/metadata.go
Normal file
117
cli/exec/metadata.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2023 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package exec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/matrix"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
// return the metadata from the cli context.
|
||||||
|
func metadataFromContext(c *cli.Context, axis matrix.Axis) metadata.Metadata {
|
||||||
|
platform := c.String("system-platform")
|
||||||
|
if platform == "" {
|
||||||
|
platform = runtime.GOOS + "/" + runtime.GOARCH
|
||||||
|
}
|
||||||
|
|
||||||
|
fullRepoName := c.String("repo-name")
|
||||||
|
repoOwner := ""
|
||||||
|
repoName := ""
|
||||||
|
if idx := strings.LastIndex(fullRepoName, "/"); idx != -1 {
|
||||||
|
repoOwner = fullRepoName[:idx]
|
||||||
|
repoName = fullRepoName[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.Metadata{
|
||||||
|
Repo: metadata.Repo{
|
||||||
|
Name: repoName,
|
||||||
|
Owner: repoOwner,
|
||||||
|
RemoteID: c.String("repo-remote-id"),
|
||||||
|
Link: c.String("repo-link"),
|
||||||
|
CloneURL: c.String("repo-clone-url"),
|
||||||
|
Private: c.Bool("repo-private"),
|
||||||
|
Trusted: c.Bool("repo-trusted"),
|
||||||
|
},
|
||||||
|
Curr: metadata.Pipeline{
|
||||||
|
Number: c.Int64("pipeline-number"),
|
||||||
|
Parent: c.Int64("pipeline-parent"),
|
||||||
|
Created: c.Int64("pipeline-created"),
|
||||||
|
Started: c.Int64("pipeline-started"),
|
||||||
|
Finished: c.Int64("pipeline-finished"),
|
||||||
|
Status: c.String("pipeline-status"),
|
||||||
|
Event: c.String("pipeline-event"),
|
||||||
|
Link: c.String("pipeline-link"),
|
||||||
|
Target: c.String("pipeline-target"),
|
||||||
|
Commit: metadata.Commit{
|
||||||
|
Sha: c.String("commit-sha"),
|
||||||
|
Ref: c.String("commit-ref"),
|
||||||
|
Refspec: c.String("commit-refspec"),
|
||||||
|
Branch: c.String("commit-branch"),
|
||||||
|
Message: c.String("commit-message"),
|
||||||
|
Author: metadata.Author{
|
||||||
|
Name: c.String("commit-author-name"),
|
||||||
|
Email: c.String("commit-author-email"),
|
||||||
|
Avatar: c.String("commit-author-avatar"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Prev: metadata.Pipeline{
|
||||||
|
Number: c.Int64("prev-pipeline-number"),
|
||||||
|
Created: c.Int64("prev-pipeline-created"),
|
||||||
|
Started: c.Int64("prev-pipeline-started"),
|
||||||
|
Finished: c.Int64("prev-pipeline-finished"),
|
||||||
|
Status: c.String("prev-pipeline-status"),
|
||||||
|
Event: c.String("prev-pipeline-event"),
|
||||||
|
Link: c.String("prev-pipeline-link"),
|
||||||
|
Commit: metadata.Commit{
|
||||||
|
Sha: c.String("prev-commit-sha"),
|
||||||
|
Ref: c.String("prev-commit-ref"),
|
||||||
|
Refspec: c.String("prev-commit-refspec"),
|
||||||
|
Branch: c.String("prev-commit-branch"),
|
||||||
|
Message: c.String("prev-commit-message"),
|
||||||
|
Author: metadata.Author{
|
||||||
|
Name: c.String("prev-commit-author-name"),
|
||||||
|
Email: c.String("prev-commit-author-email"),
|
||||||
|
Avatar: c.String("prev-commit-author-avatar"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Workflow: metadata.Workflow{
|
||||||
|
Name: c.String("workflow-name"),
|
||||||
|
Number: c.Int("workflow-number"),
|
||||||
|
Matrix: axis,
|
||||||
|
},
|
||||||
|
Step: metadata.Step{
|
||||||
|
Name: c.String("step-name"),
|
||||||
|
Number: c.Int("step-number"),
|
||||||
|
},
|
||||||
|
Sys: metadata.System{
|
||||||
|
Name: c.String("system-name"),
|
||||||
|
Link: c.String("system-link"),
|
||||||
|
Platform: platform,
|
||||||
|
Version: version.Version,
|
||||||
|
},
|
||||||
|
Forge: metadata.Forge{
|
||||||
|
Type: c.String("forge-type"),
|
||||||
|
URL: c.String("forge-url"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -53,8 +53,9 @@ This is the reference list of all environment variables available to your pipeli
|
|||||||
| `CI_REPO` | repository full name `<owner>/<name>` |
|
| `CI_REPO` | repository full name `<owner>/<name>` |
|
||||||
| `CI_REPO_OWNER` | repository owner |
|
| `CI_REPO_OWNER` | repository owner |
|
||||||
| `CI_REPO_NAME` | repository name |
|
| `CI_REPO_NAME` | repository name |
|
||||||
|
| `CI_REPO_REMOTE_ID` | repository remote ID, is the UID it has in the forge |
|
||||||
| `CI_REPO_SCM` | repository SCM (git) |
|
| `CI_REPO_SCM` | repository SCM (git) |
|
||||||
| `CI_REPO_URL` | repository web URL |
|
| `CI_REPO_URL` | repository web URL |
|
||||||
| `CI_REPO_CLONE_URL` | repository clone URL |
|
| `CI_REPO_CLONE_URL` | repository clone URL |
|
||||||
| `CI_REPO_DEFAULT_BRANCH` | repository default branch (master) |
|
| `CI_REPO_DEFAULT_BRANCH` | repository default branch (master) |
|
||||||
| `CI_REPO_PRIVATE` | repository is private |
|
| `CI_REPO_PRIVATE` | repository is private |
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022 Woodpecker Authors
|
// Copyright 2023 Woodpecker Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -15,274 +15,131 @@
|
|||||||
package frontend
|
package frontend
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"fmt"
|
||||||
"strconv"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/drone/envsubst"
|
||||||
|
|
||||||
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
"github.com/woodpecker-ci/woodpecker/version"
|
"github.com/woodpecker-ci/woodpecker/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event types corresponding to scm hooks.
|
func EnvVarSubst(yaml string, environ map[string]string) (string, error) {
|
||||||
const (
|
return envsubst.Eval(yaml, func(name string) string {
|
||||||
EventPush = "push"
|
env := environ[name]
|
||||||
EventPull = "pull_request"
|
if strings.Contains(env, "\n") {
|
||||||
EventTag = "tag"
|
env = fmt.Sprintf("%q", env)
|
||||||
EventDeploy = "deployment"
|
}
|
||||||
EventCron = "cron"
|
return env
|
||||||
EventManual = "manual"
|
})
|
||||||
)
|
|
||||||
|
|
||||||
// Different ways to handle failure states
|
|
||||||
const (
|
|
||||||
FailureIgnore = "ignore"
|
|
||||||
FailureFail = "fail"
|
|
||||||
// FailureCancel = "cancel" // Not implemented yet
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// Metadata defines runtime m.
|
|
||||||
Metadata struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Repo Repo `json:"repo,omitempty"`
|
|
||||||
Curr Pipeline `json:"curr,omitempty"`
|
|
||||||
Prev Pipeline `json:"prev,omitempty"`
|
|
||||||
Workflow Workflow `json:"workflow,omitempty"`
|
|
||||||
Step Step `json:"step,omitempty"`
|
|
||||||
Sys System `json:"sys,omitempty"`
|
|
||||||
Forge Forge `json:"forge,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repo defines runtime metadata for a repository.
|
|
||||||
Repo struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Link string `json:"link,omitempty"`
|
|
||||||
CloneURL string `json:"clone_url,omitempty"`
|
|
||||||
Private bool `json:"private,omitempty"`
|
|
||||||
Secrets []Secret `json:"secrets,omitempty"`
|
|
||||||
Branch string `json:"default_branch,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pipeline defines runtime metadata for a pipeline.
|
|
||||||
Pipeline struct {
|
|
||||||
Number int64 `json:"number,omitempty"`
|
|
||||||
Created int64 `json:"created,omitempty"`
|
|
||||||
Started int64 `json:"started,omitempty"`
|
|
||||||
Finished int64 `json:"finished,omitempty"`
|
|
||||||
Timeout int64 `json:"timeout,omitempty"`
|
|
||||||
Status string `json:"status,omitempty"`
|
|
||||||
Event string `json:"event,omitempty"`
|
|
||||||
Link string `json:"link,omitempty"`
|
|
||||||
Target string `json:"target,omitempty"`
|
|
||||||
Trusted bool `json:"trusted,omitempty"`
|
|
||||||
Commit Commit `json:"commit,omitempty"`
|
|
||||||
Parent int64 `json:"parent,omitempty"`
|
|
||||||
Cron string `json:"cron,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit defines runtime metadata for a commit.
|
|
||||||
Commit struct {
|
|
||||||
Sha string `json:"sha,omitempty"`
|
|
||||||
Ref string `json:"ref,omitempty"`
|
|
||||||
Refspec string `json:"refspec,omitempty"`
|
|
||||||
Branch string `json:"branch,omitempty"`
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
Author Author `json:"author,omitempty"`
|
|
||||||
ChangedFiles []string `json:"changed_files,omitempty"`
|
|
||||||
PullRequestLabels []string `json:"labels,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Author defines runtime metadata for a commit author.
|
|
||||||
Author struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Email string `json:"email,omitempty"`
|
|
||||||
Avatar string `json:"avatar,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workflow defines runtime metadata for a workflow.
|
|
||||||
Workflow struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Number int `json:"number,omitempty"`
|
|
||||||
Matrix map[string]string `json:"matrix,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step defines runtime metadata for a step.
|
|
||||||
Step struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Number int `json:"number,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secret defines a runtime secret
|
|
||||||
Secret struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Value string `json:"value,omitempty"`
|
|
||||||
Mount string `json:"mount,omitempty"`
|
|
||||||
Mask bool `json:"mask,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// System defines runtime metadata for a ci/cd system.
|
|
||||||
System struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Host string `json:"host,omitempty"`
|
|
||||||
Link string `json:"link,omitempty"`
|
|
||||||
Platform string `json:"arch,omitempty"`
|
|
||||||
Version string `json:"version,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forge defines runtime metadata about the forge that host the repo
|
|
||||||
Forge struct {
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
URL string `json:"url,omitempty"`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Environ returns the metadata as a map of environment variables.
|
|
||||||
func (m *Metadata) Environ() map[string]string {
|
|
||||||
var (
|
|
||||||
repoOwner string
|
|
||||||
repoName string
|
|
||||||
sourceBranch string
|
|
||||||
targetBranch string
|
|
||||||
)
|
|
||||||
|
|
||||||
repoParts := strings.Split(m.Repo.Name, "/")
|
|
||||||
if len(repoParts) == 2 {
|
|
||||||
repoOwner = repoParts[0]
|
|
||||||
repoName = repoParts[1]
|
|
||||||
} else {
|
|
||||||
repoName = m.Repo.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
branchParts := strings.Split(m.Curr.Commit.Refspec, ":")
|
|
||||||
if len(branchParts) == 2 {
|
|
||||||
sourceBranch = branchParts[0]
|
|
||||||
targetBranch = branchParts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
params := map[string]string{
|
|
||||||
"CI": m.Sys.Name,
|
|
||||||
"CI_REPO": m.Repo.Name,
|
|
||||||
"CI_REPO_OWNER": repoOwner,
|
|
||||||
"CI_REPO_NAME": repoName,
|
|
||||||
"CI_REPO_SCM": "git",
|
|
||||||
"CI_REPO_URL": m.Repo.Link,
|
|
||||||
"CI_REPO_CLONE_URL": m.Repo.CloneURL,
|
|
||||||
"CI_REPO_DEFAULT_BRANCH": m.Repo.Branch,
|
|
||||||
"CI_REPO_PRIVATE": strconv.FormatBool(m.Repo.Private),
|
|
||||||
"CI_REPO_TRUSTED": "false", // TODO should this be added?
|
|
||||||
|
|
||||||
"CI_COMMIT_SHA": m.Curr.Commit.Sha,
|
|
||||||
"CI_COMMIT_REF": m.Curr.Commit.Ref,
|
|
||||||
"CI_COMMIT_REFSPEC": m.Curr.Commit.Refspec,
|
|
||||||
"CI_COMMIT_BRANCH": m.Curr.Commit.Branch,
|
|
||||||
"CI_COMMIT_SOURCE_BRANCH": sourceBranch,
|
|
||||||
"CI_COMMIT_TARGET_BRANCH": targetBranch,
|
|
||||||
"CI_COMMIT_URL": m.Curr.Link,
|
|
||||||
"CI_COMMIT_MESSAGE": m.Curr.Commit.Message,
|
|
||||||
"CI_COMMIT_AUTHOR": m.Curr.Commit.Author.Name,
|
|
||||||
"CI_COMMIT_AUTHOR_EMAIL": m.Curr.Commit.Author.Email,
|
|
||||||
"CI_COMMIT_AUTHOR_AVATAR": m.Curr.Commit.Author.Avatar,
|
|
||||||
"CI_COMMIT_TAG": "", // will be set if event is tag
|
|
||||||
"CI_COMMIT_PULL_REQUEST": "", // will be set if event is pr
|
|
||||||
"CI_COMMIT_PULL_REQUEST_LABELS": "", // will be set if event is pr
|
|
||||||
|
|
||||||
"CI_PIPELINE_NUMBER": strconv.FormatInt(m.Curr.Number, 10),
|
|
||||||
"CI_PIPELINE_PARENT": strconv.FormatInt(m.Curr.Parent, 10),
|
|
||||||
"CI_PIPELINE_EVENT": m.Curr.Event,
|
|
||||||
"CI_PIPELINE_URL": m.Curr.Link,
|
|
||||||
"CI_PIPELINE_DEPLOY_TARGET": m.Curr.Target,
|
|
||||||
"CI_PIPELINE_STATUS": m.Curr.Status,
|
|
||||||
"CI_PIPELINE_CREATED": strconv.FormatInt(m.Curr.Created, 10),
|
|
||||||
"CI_PIPELINE_STARTED": strconv.FormatInt(m.Curr.Started, 10),
|
|
||||||
"CI_PIPELINE_FINISHED": strconv.FormatInt(m.Curr.Finished, 10),
|
|
||||||
|
|
||||||
"CI_WORKFLOW_NAME": m.Workflow.Name,
|
|
||||||
"CI_WORKFLOW_NUMBER": strconv.Itoa(m.Workflow.Number),
|
|
||||||
|
|
||||||
"CI_STEP_NAME": m.Step.Name,
|
|
||||||
"CI_STEP_NUMBER": strconv.Itoa(m.Step.Number),
|
|
||||||
"CI_STEP_STATUS": "", // will be set by agent
|
|
||||||
"CI_STEP_STARTED": "", // will be set by agent
|
|
||||||
"CI_STEP_FINISHED": "", // will be set by agent
|
|
||||||
|
|
||||||
"CI_PREV_COMMIT_SHA": m.Prev.Commit.Sha,
|
|
||||||
"CI_PREV_COMMIT_REF": m.Prev.Commit.Ref,
|
|
||||||
"CI_PREV_COMMIT_REFSPEC": m.Prev.Commit.Refspec,
|
|
||||||
"CI_PREV_COMMIT_BRANCH": m.Prev.Commit.Branch,
|
|
||||||
"CI_PREV_COMMIT_URL": m.Prev.Link,
|
|
||||||
"CI_PREV_COMMIT_MESSAGE": m.Prev.Commit.Message,
|
|
||||||
"CI_PREV_COMMIT_AUTHOR": m.Prev.Commit.Author.Name,
|
|
||||||
"CI_PREV_COMMIT_AUTHOR_EMAIL": m.Prev.Commit.Author.Email,
|
|
||||||
"CI_PREV_COMMIT_AUTHOR_AVATAR": m.Prev.Commit.Author.Avatar,
|
|
||||||
|
|
||||||
"CI_PREV_PIPELINE_NUMBER": strconv.FormatInt(m.Prev.Number, 10),
|
|
||||||
"CI_PREV_PIPELINE_PARENT": strconv.FormatInt(m.Prev.Parent, 10),
|
|
||||||
"CI_PREV_PIPELINE_EVENT": m.Prev.Event,
|
|
||||||
"CI_PREV_PIPELINE_URL": m.Prev.Link,
|
|
||||||
"CI_PREV_PIPELINE_DEPLOY_TARGET": m.Prev.Target,
|
|
||||||
"CI_PREV_PIPELINE_STATUS": m.Prev.Status,
|
|
||||||
"CI_PREV_PIPELINE_CREATED": strconv.FormatInt(m.Prev.Created, 10),
|
|
||||||
"CI_PREV_PIPELINE_STARTED": strconv.FormatInt(m.Prev.Started, 10),
|
|
||||||
"CI_PREV_PIPELINE_FINISHED": strconv.FormatInt(m.Prev.Finished, 10),
|
|
||||||
|
|
||||||
"CI_SYSTEM_NAME": m.Sys.Name,
|
|
||||||
"CI_SYSTEM_URL": m.Sys.Link,
|
|
||||||
"CI_SYSTEM_HOST": m.Sys.Host,
|
|
||||||
"CI_SYSTEM_PLATFORM": m.Sys.Platform, // will be set by pipeline platform option or by agent
|
|
||||||
"CI_SYSTEM_VERSION": version.Version,
|
|
||||||
|
|
||||||
"CI_FORGE_TYPE": m.Forge.Type,
|
|
||||||
"CI_FORGE_URL": m.Forge.URL,
|
|
||||||
|
|
||||||
// DEPRECATED
|
|
||||||
"CI_SYSTEM_ARCH": m.Sys.Platform, // TODO: remove after v1.0.x version
|
|
||||||
// use CI_PIPELINE_*
|
|
||||||
"CI_BUILD_NUMBER": strconv.FormatInt(m.Curr.Number, 10),
|
|
||||||
"CI_BUILD_PARENT": strconv.FormatInt(m.Curr.Parent, 10),
|
|
||||||
"CI_BUILD_EVENT": m.Curr.Event,
|
|
||||||
"CI_BUILD_LINK": m.Curr.Link,
|
|
||||||
"CI_BUILD_DEPLOY_TARGET": m.Curr.Target,
|
|
||||||
"CI_BUILD_STATUS": m.Curr.Status,
|
|
||||||
"CI_BUILD_CREATED": strconv.FormatInt(m.Curr.Created, 10),
|
|
||||||
"CI_BUILD_STARTED": strconv.FormatInt(m.Curr.Started, 10),
|
|
||||||
"CI_BUILD_FINISHED": strconv.FormatInt(m.Curr.Finished, 10),
|
|
||||||
// use CI_PREV_PIPELINE_*
|
|
||||||
"CI_PREV_BUILD_NUMBER": strconv.FormatInt(m.Prev.Number, 10),
|
|
||||||
"CI_PREV_BUILD_PARENT": strconv.FormatInt(m.Prev.Parent, 10),
|
|
||||||
"CI_PREV_BUILD_EVENT": m.Prev.Event,
|
|
||||||
"CI_PREV_BUILD_LINK": m.Prev.Link,
|
|
||||||
"CI_PREV_BUILD_DEPLOY_TARGET": m.Prev.Target,
|
|
||||||
"CI_PREV_BUILD_STATUS": m.Prev.Status,
|
|
||||||
"CI_PREV_BUILD_CREATED": strconv.FormatInt(m.Prev.Created, 10),
|
|
||||||
"CI_PREV_BUILD_STARTED": strconv.FormatInt(m.Prev.Started, 10),
|
|
||||||
"CI_PREV_BUILD_FINISHED": strconv.FormatInt(m.Prev.Finished, 10),
|
|
||||||
// use CI_STEP_*
|
|
||||||
"CI_JOB_NUMBER": strconv.Itoa(m.Step.Number),
|
|
||||||
"CI_JOB_STATUS": "", // will be set by agent
|
|
||||||
"CI_JOB_STARTED": "", // will be set by agent
|
|
||||||
"CI_JOB_FINISHED": "", // will be set by agent
|
|
||||||
// CI_REPO_CLONE_URL
|
|
||||||
"CI_REPO_REMOTE": m.Repo.CloneURL,
|
|
||||||
// use *_URL
|
|
||||||
"CI_REPO_LINK": m.Repo.Link,
|
|
||||||
"CI_COMMIT_LINK": m.Curr.Link,
|
|
||||||
"CI_PIPELINE_LINK": m.Curr.Link,
|
|
||||||
"CI_PREV_COMMIT_LINK": m.Prev.Link,
|
|
||||||
"CI_PREV_PIPELINE_LINK": m.Prev.Link,
|
|
||||||
"CI_SYSTEM_LINK": m.Sys.Link,
|
|
||||||
}
|
|
||||||
if m.Curr.Event == EventTag {
|
|
||||||
params["CI_COMMIT_TAG"] = strings.TrimPrefix(m.Curr.Commit.Ref, "refs/tags/")
|
|
||||||
}
|
|
||||||
if m.Curr.Event == EventPull {
|
|
||||||
params["CI_COMMIT_PULL_REQUEST"] = pullRegexp.FindString(m.Curr.Commit.Ref)
|
|
||||||
params["CI_COMMIT_PULL_REQUEST_LABELS"] = strings.Join(m.Curr.Commit.PullRequestLabels, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
return params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var pullRegexp = regexp.MustCompile(`\d+`)
|
// MetadataFromStruct return the metadata from a pipeline will run with.
|
||||||
|
func MetadataFromStruct(forge metadata.ServerForge, repo *model.Repo, pipeline, last *model.Pipeline, workflow *model.Step, link string) metadata.Metadata {
|
||||||
|
host := link
|
||||||
|
uri, err := url.Parse(link)
|
||||||
|
if err == nil {
|
||||||
|
host = uri.Host
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Metadata) SetPlatform(platform string) {
|
fForge := metadata.Forge{}
|
||||||
m.Sys.Platform = platform
|
if forge != nil {
|
||||||
|
fForge = metadata.Forge{
|
||||||
|
Type: forge.Name(),
|
||||||
|
URL: forge.URL(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fRepo := metadata.Repo{}
|
||||||
|
if repo != nil {
|
||||||
|
fRepo = metadata.Repo{
|
||||||
|
Name: repo.Name,
|
||||||
|
Owner: repo.Owner,
|
||||||
|
RemoteID: fmt.Sprint(repo.ForgeRemoteID),
|
||||||
|
Link: repo.Link,
|
||||||
|
CloneURL: repo.Clone,
|
||||||
|
Private: repo.IsSCMPrivate,
|
||||||
|
Branch: repo.Branch,
|
||||||
|
Trusted: repo.IsTrusted,
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx := strings.LastIndex(repo.FullName, "/"); idx != -1 {
|
||||||
|
if fRepo.Name == "" && repo.FullName != "" {
|
||||||
|
fRepo.Name = repo.FullName[idx+1:]
|
||||||
|
}
|
||||||
|
if fRepo.Owner == "" && repo.FullName != "" {
|
||||||
|
fRepo.Owner = repo.FullName[:idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fWorkflow := metadata.Workflow{}
|
||||||
|
if workflow != nil {
|
||||||
|
fWorkflow = metadata.Workflow{
|
||||||
|
Name: workflow.Name,
|
||||||
|
Number: workflow.PID,
|
||||||
|
Matrix: workflow.Environ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.Metadata{
|
||||||
|
Repo: fRepo,
|
||||||
|
Curr: metadataPipelineFromModelPipeline(pipeline, true),
|
||||||
|
Prev: metadataPipelineFromModelPipeline(last, false),
|
||||||
|
Workflow: fWorkflow,
|
||||||
|
Step: metadata.Step{},
|
||||||
|
Sys: metadata.System{
|
||||||
|
Name: "woodpecker",
|
||||||
|
Link: link,
|
||||||
|
Host: host,
|
||||||
|
Platform: "", // will be set by pipeline platform option or by agent
|
||||||
|
Version: version.Version,
|
||||||
|
},
|
||||||
|
Forge: fForge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func metadataPipelineFromModelPipeline(pipeline *model.Pipeline, includeParent bool) metadata.Pipeline {
|
||||||
|
if pipeline == nil {
|
||||||
|
return metadata.Pipeline{}
|
||||||
|
}
|
||||||
|
|
||||||
|
cron := ""
|
||||||
|
if pipeline.Event == model.EventCron {
|
||||||
|
cron = pipeline.Sender
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := int64(0)
|
||||||
|
if includeParent {
|
||||||
|
parent = pipeline.Parent
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.Pipeline{
|
||||||
|
Number: pipeline.Number,
|
||||||
|
Parent: parent,
|
||||||
|
Created: pipeline.Created,
|
||||||
|
Started: pipeline.Started,
|
||||||
|
Finished: pipeline.Finished,
|
||||||
|
Status: string(pipeline.Status),
|
||||||
|
Event: string(pipeline.Event),
|
||||||
|
Link: pipeline.Link,
|
||||||
|
Target: pipeline.Deploy,
|
||||||
|
Commit: metadata.Commit{
|
||||||
|
Sha: pipeline.Commit,
|
||||||
|
Ref: pipeline.Ref,
|
||||||
|
Refspec: pipeline.Refspec,
|
||||||
|
Branch: pipeline.Branch,
|
||||||
|
Message: pipeline.Message,
|
||||||
|
Author: metadata.Author{
|
||||||
|
Name: pipeline.Author,
|
||||||
|
Email: pipeline.Email,
|
||||||
|
Avatar: pipeline.Avatar,
|
||||||
|
},
|
||||||
|
ChangedFiles: pipeline.ChangedFiles,
|
||||||
|
PullRequestLabels: pipeline.PullRequestLabels,
|
||||||
|
},
|
||||||
|
Cron: cron,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
32
pipeline/frontend/metadata/const.go
Normal file
32
pipeline/frontend/metadata/const.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2022 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
// Event types corresponding to scm hooks.
|
||||||
|
const (
|
||||||
|
EventPush = "push"
|
||||||
|
EventPull = "pull_request"
|
||||||
|
EventTag = "tag"
|
||||||
|
EventDeploy = "deployment"
|
||||||
|
EventCron = "cron"
|
||||||
|
EventManual = "manual"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Different ways to handle failure states
|
||||||
|
const (
|
||||||
|
FailureIgnore = "ignore"
|
||||||
|
FailureFail = "fail"
|
||||||
|
// FailureCancel = "cancel" // Not implemented yet
|
||||||
|
)
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package pipeline
|
package metadata
|
||||||
|
|
||||||
// SetDroneEnviron set dedicated to DroneCI environment vars as compatibility
|
// SetDroneEnviron set dedicated to DroneCI environment vars as compatibility
|
||||||
// layer. Main purpose is to be compatible with drone plugins.
|
// layer. Main purpose is to be compatible with drone plugins.
|
132
pipeline/frontend/metadata/drone_compatibility_test.go
Normal file
132
pipeline/frontend/metadata/drone_compatibility_test.go
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
package metadata_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetDroneEnviron(t *testing.T) {
|
||||||
|
woodpeckerVars := `CI=woodpecker
|
||||||
|
CI_BUILD_CREATED=1685749339
|
||||||
|
CI_BUILD_EVENT=pull_request
|
||||||
|
CI_BUILD_FINISHED=1685749350
|
||||||
|
CI_BUILD_LINK=https://codeberg.org/Epsilon_02/todo-checker/pulls/9
|
||||||
|
CI_BUILD_NUMBER=41
|
||||||
|
CI_BUILD_STARTED=1685749339
|
||||||
|
CI_BUILD_STATUS=success
|
||||||
|
CI_COMMIT_AUTHOR=6543
|
||||||
|
CI_COMMIT_AUTHOR_AVATAR=https://codeberg.org/avatars/09a234c768cb9bca78f6b2f82d6af173
|
||||||
|
CI_COMMIT_BRANCH=main
|
||||||
|
CI_COMMIT_LINK=https://codeberg.org/Epsilon_02/todo-checker/pulls/9
|
||||||
|
CI_COMMIT_MESSAGE=fix testscript
|
||||||
|
CI_COMMIT_PULL_REQUEST=9
|
||||||
|
CI_COMMIT_REF=refs/pull/9/head
|
||||||
|
CI_COMMIT_REFSPEC=fix_fail-on-err:main
|
||||||
|
CI_COMMIT_SHA=a778b069d9f5992786d2db9be493b43868cfce76
|
||||||
|
CI_COMMIT_SOURCE_BRANCH=fix_fail-on-err
|
||||||
|
CI_COMMIT_TARGET_BRANCH=main
|
||||||
|
CI_JOB_FINISHED=1685749350
|
||||||
|
CI_JOB_STARTED=1685749339
|
||||||
|
CI_JOB_STATUS=success
|
||||||
|
CI_MACHINE=7939910e431b
|
||||||
|
CI_PIPELINE_CREATED=1685749339
|
||||||
|
CI_PIPELINE_EVENT=pull_request
|
||||||
|
CI_PIPELINE_FINISHED=1685749350
|
||||||
|
CI_PIPELINE_LINK=https://codeberg.org/Epsilon_02/todo-checker/pulls/9
|
||||||
|
CI_PIPELINE_NUMBER=41
|
||||||
|
CI_PIPELINE_STARTED=1685749339
|
||||||
|
CI_PIPELINE_STATUS=success
|
||||||
|
CI_PREV_BUILD_CREATED=1685748680
|
||||||
|
CI_PREV_BUILD_EVENT=pull_request
|
||||||
|
CI_PREV_BUILD_FINISHED=1685748704
|
||||||
|
CI_PREV_BUILD_LINK=https://codeberg.org/Epsilon_02/todo-checker/pulls/13
|
||||||
|
CI_PREV_BUILD_NUMBER=40
|
||||||
|
CI_PREV_BUILD_STARTED=1685748680
|
||||||
|
CI_PREV_BUILD_STATUS=success
|
||||||
|
CI_PREV_COMMIT_AUTHOR=6543
|
||||||
|
CI_PREV_COMMIT_AUTHOR_AVATAR=https://codeberg.org/avatars/09a234c768cb9bca78f6b2f82d6af173
|
||||||
|
CI_PREV_COMMIT_BRANCH=main
|
||||||
|
CI_PREV_COMMIT_LINK=https://codeberg.org/Epsilon_02/todo-checker/pulls/13
|
||||||
|
CI_PREV_COMMIT_MESSAGE=Print filename and linenuber on fail
|
||||||
|
CI_PREV_COMMIT_REF=refs/pull/13/head
|
||||||
|
CI_PREV_COMMIT_REFSPEC=print_file_and_line:main
|
||||||
|
CI_PREV_COMMIT_SHA=e246aff5a9466df2e522efc9007823a7496d9d41
|
||||||
|
CI_PREV_PIPELINE_CREATED=1685748680
|
||||||
|
CI_PREV_PIPELINE_EVENT=pull_request
|
||||||
|
CI_PREV_PIPELINE_FINISHED=1685748704
|
||||||
|
CI_PREV_PIPELINE_LINK=https://codeberg.org/Epsilon_02/todo-checker/pulls/13
|
||||||
|
CI_PREV_PIPELINE_NUMBER=40
|
||||||
|
CI_PREV_PIPELINE_STARTED=1685748680
|
||||||
|
CI_PREV_PIPELINE_STATUS=success
|
||||||
|
CI_REPO=Epsilon_02/todo-checker
|
||||||
|
CI_REPO_CLONE_URL=https://codeberg.org/Epsilon_02/todo-checker.git
|
||||||
|
CI_REPO_DEFAULT_BRANCH=main
|
||||||
|
CI_REPO_LINK=https://codeberg.org/Epsilon_02/todo-checker
|
||||||
|
CI_REPO_NAME=todo-checker
|
||||||
|
CI_REPO_OWNER=Epsilon_02
|
||||||
|
CI_REPO_REMOTE=https://codeberg.org/Epsilon_02/todo-checker.git
|
||||||
|
CI_REPO_SCM=git
|
||||||
|
CI_STEP_FINISHED=1685749350
|
||||||
|
CI_STEP_NAME=wp_01h1z7v5d1tskaqjexw0ng6w7d_0_step_3
|
||||||
|
CI_STEP_STARTED=1685749339
|
||||||
|
CI_STEP_STATUS=success
|
||||||
|
CI_SYSTEM_ARCH=linux/amd64
|
||||||
|
CI_SYSTEM_HOST=ci.codeberg.org
|
||||||
|
CI_SYSTEM_LINK=https://ci.codeberg.org
|
||||||
|
CI_SYSTEM_NAME=woodpecker
|
||||||
|
CI_SYSTEM_VERSION=next-dd644da3
|
||||||
|
CI_WORKFLOW_NAME=woodpecker
|
||||||
|
CI_WORKFLOW_NUMBER=1
|
||||||
|
CI_WORKSPACE=/woodpecker/src/codeberg.org/Epsilon_02/todo-checker`
|
||||||
|
|
||||||
|
droneVars := `DRONE_BRANCH=main
|
||||||
|
DRONE_BUILD_CREATED=1685749339
|
||||||
|
DRONE_BUILD_EVENT=pull_request
|
||||||
|
DRONE_BUILD_FINISHED=1685749350
|
||||||
|
DRONE_BUILD_NUMBER=41
|
||||||
|
DRONE_BUILD_STARTED=1685749339
|
||||||
|
DRONE_BUILD_STATUS=success
|
||||||
|
DRONE_COMMIT=a778b069d9f5992786d2db9be493b43868cfce76
|
||||||
|
DRONE_COMMIT_AUTHOR=6543
|
||||||
|
DRONE_COMMIT_AUTHOR_AVATAR=https://codeberg.org/avatars/09a234c768cb9bca78f6b2f82d6af173
|
||||||
|
DRONE_COMMIT_AUTHOR_NAME=6543
|
||||||
|
DRONE_COMMIT_BEFORE=e246aff5a9466df2e522efc9007823a7496d9d41
|
||||||
|
DRONE_COMMIT_BRANCH=main
|
||||||
|
DRONE_COMMIT_MESSAGE=fix testscript
|
||||||
|
DRONE_COMMIT_REF=refs/pull/9/head
|
||||||
|
DRONE_COMMIT_SHA=a778b069d9f5992786d2db9be493b43868cfce76
|
||||||
|
DRONE_GIT_HTTP_URL=https://codeberg.org/Epsilon_02/todo-checker.git
|
||||||
|
DRONE_PULL_REQUEST=9
|
||||||
|
DRONE_REMOTE_URL=https://codeberg.org/Epsilon_02/todo-checker.git
|
||||||
|
DRONE_REPO=Epsilon_02/todo-checker
|
||||||
|
DRONE_REPO_BRANCH=main
|
||||||
|
DRONE_REPO_NAME=todo-checker
|
||||||
|
DRONE_REPO_OWNER=Epsilon_02
|
||||||
|
DRONE_REPO_SCM=git
|
||||||
|
DRONE_SOURCE_BRANCH=fix_fail-on-err
|
||||||
|
DRONE_SYSTEM_HOST=ci.codeberg.org
|
||||||
|
DRONE_TARGET_BRANCH=main`
|
||||||
|
|
||||||
|
env := convertListToEnvMap(t, woodpeckerVars)
|
||||||
|
metadata.SetDroneEnviron(env)
|
||||||
|
// filter only new added env vars
|
||||||
|
for k := range convertListToEnvMap(t, woodpeckerVars) {
|
||||||
|
delete(env, k)
|
||||||
|
}
|
||||||
|
assert.EqualValues(t, convertListToEnvMap(t, droneVars), env)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertListToEnvMap(t *testing.T, list string) map[string]string {
|
||||||
|
result := make(map[string]string)
|
||||||
|
for _, s := range strings.Split(list, "\n") {
|
||||||
|
ss := strings.SplitN(strings.TrimSpace(s), "=", 2)
|
||||||
|
if len(ss) != 2 {
|
||||||
|
t.Fatal("helper function got invalid test data")
|
||||||
|
}
|
||||||
|
result[ss[0]] = ss[1]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
161
pipeline/frontend/metadata/environment.go
Normal file
161
pipeline/frontend/metadata/environment.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// Copyright 2023 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pullRegexp = regexp.MustCompile(`\d+`)
|
||||||
|
|
||||||
|
// Environ returns the metadata as a map of environment variables.
|
||||||
|
func (m *Metadata) Environ() map[string]string {
|
||||||
|
var (
|
||||||
|
sourceBranch string
|
||||||
|
targetBranch string
|
||||||
|
)
|
||||||
|
|
||||||
|
branchParts := strings.Split(m.Curr.Commit.Refspec, ":")
|
||||||
|
if len(branchParts) == 2 {
|
||||||
|
sourceBranch = branchParts[0]
|
||||||
|
targetBranch = branchParts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string]string{
|
||||||
|
"CI": m.Sys.Name,
|
||||||
|
"CI_REPO": path.Join(m.Repo.Owner, m.Repo.Name),
|
||||||
|
"CI_REPO_NAME": m.Repo.Name,
|
||||||
|
"CI_REPO_OWNER": m.Repo.Owner,
|
||||||
|
"CI_REPO_REMOTE_ID": m.Repo.RemoteID,
|
||||||
|
"CI_REPO_SCM": "git",
|
||||||
|
"CI_REPO_URL": m.Repo.Link,
|
||||||
|
"CI_REPO_CLONE_URL": m.Repo.CloneURL,
|
||||||
|
"CI_REPO_DEFAULT_BRANCH": m.Repo.Branch,
|
||||||
|
"CI_REPO_PRIVATE": strconv.FormatBool(m.Repo.Private),
|
||||||
|
"CI_REPO_TRUSTED": strconv.FormatBool(m.Repo.Trusted),
|
||||||
|
|
||||||
|
"CI_COMMIT_SHA": m.Curr.Commit.Sha,
|
||||||
|
"CI_COMMIT_REF": m.Curr.Commit.Ref,
|
||||||
|
"CI_COMMIT_REFSPEC": m.Curr.Commit.Refspec,
|
||||||
|
"CI_COMMIT_BRANCH": m.Curr.Commit.Branch,
|
||||||
|
"CI_COMMIT_SOURCE_BRANCH": sourceBranch,
|
||||||
|
"CI_COMMIT_TARGET_BRANCH": targetBranch,
|
||||||
|
"CI_COMMIT_URL": m.Curr.Link,
|
||||||
|
"CI_COMMIT_MESSAGE": m.Curr.Commit.Message,
|
||||||
|
"CI_COMMIT_AUTHOR": m.Curr.Commit.Author.Name,
|
||||||
|
"CI_COMMIT_AUTHOR_EMAIL": m.Curr.Commit.Author.Email,
|
||||||
|
"CI_COMMIT_AUTHOR_AVATAR": m.Curr.Commit.Author.Avatar,
|
||||||
|
"CI_COMMIT_TAG": "", // will be set if event is tag
|
||||||
|
"CI_COMMIT_PULL_REQUEST": "", // will be set if event is pr
|
||||||
|
"CI_COMMIT_PULL_REQUEST_LABELS": "", // will be set if event is pr
|
||||||
|
|
||||||
|
"CI_PIPELINE_NUMBER": strconv.FormatInt(m.Curr.Number, 10),
|
||||||
|
"CI_PIPELINE_PARENT": strconv.FormatInt(m.Curr.Parent, 10),
|
||||||
|
"CI_PIPELINE_EVENT": m.Curr.Event,
|
||||||
|
"CI_PIPELINE_URL": m.Curr.Link,
|
||||||
|
"CI_PIPELINE_DEPLOY_TARGET": m.Curr.Target,
|
||||||
|
"CI_PIPELINE_STATUS": m.Curr.Status,
|
||||||
|
"CI_PIPELINE_CREATED": strconv.FormatInt(m.Curr.Created, 10),
|
||||||
|
"CI_PIPELINE_STARTED": strconv.FormatInt(m.Curr.Started, 10),
|
||||||
|
"CI_PIPELINE_FINISHED": strconv.FormatInt(m.Curr.Finished, 10),
|
||||||
|
|
||||||
|
"CI_WORKFLOW_NAME": m.Workflow.Name,
|
||||||
|
"CI_WORKFLOW_NUMBER": strconv.Itoa(m.Workflow.Number),
|
||||||
|
|
||||||
|
"CI_STEP_NAME": m.Step.Name,
|
||||||
|
"CI_STEP_NUMBER": strconv.Itoa(m.Step.Number),
|
||||||
|
"CI_STEP_STATUS": "", // will be set by agent
|
||||||
|
"CI_STEP_STARTED": "", // will be set by agent
|
||||||
|
"CI_STEP_FINISHED": "", // will be set by agent
|
||||||
|
|
||||||
|
"CI_PREV_COMMIT_SHA": m.Prev.Commit.Sha,
|
||||||
|
"CI_PREV_COMMIT_REF": m.Prev.Commit.Ref,
|
||||||
|
"CI_PREV_COMMIT_REFSPEC": m.Prev.Commit.Refspec,
|
||||||
|
"CI_PREV_COMMIT_BRANCH": m.Prev.Commit.Branch,
|
||||||
|
"CI_PREV_COMMIT_URL": m.Prev.Link,
|
||||||
|
"CI_PREV_COMMIT_MESSAGE": m.Prev.Commit.Message,
|
||||||
|
"CI_PREV_COMMIT_AUTHOR": m.Prev.Commit.Author.Name,
|
||||||
|
"CI_PREV_COMMIT_AUTHOR_EMAIL": m.Prev.Commit.Author.Email,
|
||||||
|
"CI_PREV_COMMIT_AUTHOR_AVATAR": m.Prev.Commit.Author.Avatar,
|
||||||
|
|
||||||
|
"CI_PREV_PIPELINE_NUMBER": strconv.FormatInt(m.Prev.Number, 10),
|
||||||
|
"CI_PREV_PIPELINE_PARENT": strconv.FormatInt(m.Prev.Parent, 10),
|
||||||
|
"CI_PREV_PIPELINE_EVENT": m.Prev.Event,
|
||||||
|
"CI_PREV_PIPELINE_URL": m.Prev.Link,
|
||||||
|
"CI_PREV_PIPELINE_DEPLOY_TARGET": m.Prev.Target,
|
||||||
|
"CI_PREV_PIPELINE_STATUS": m.Prev.Status,
|
||||||
|
"CI_PREV_PIPELINE_CREATED": strconv.FormatInt(m.Prev.Created, 10),
|
||||||
|
"CI_PREV_PIPELINE_STARTED": strconv.FormatInt(m.Prev.Started, 10),
|
||||||
|
"CI_PREV_PIPELINE_FINISHED": strconv.FormatInt(m.Prev.Finished, 10),
|
||||||
|
|
||||||
|
"CI_SYSTEM_NAME": m.Sys.Name,
|
||||||
|
"CI_SYSTEM_URL": m.Sys.Link,
|
||||||
|
"CI_SYSTEM_HOST": m.Sys.Host,
|
||||||
|
"CI_SYSTEM_PLATFORM": m.Sys.Platform, // will be set by pipeline platform option or by agent
|
||||||
|
"CI_SYSTEM_VERSION": m.Sys.Version,
|
||||||
|
|
||||||
|
"CI_FORGE_TYPE": m.Forge.Type,
|
||||||
|
"CI_FORGE_URL": m.Forge.URL,
|
||||||
|
|
||||||
|
// DEPRECATED
|
||||||
|
"CI_SYSTEM_ARCH": m.Sys.Platform, // TODO: remove after v1.0.x version
|
||||||
|
// use CI_PIPELINE_*
|
||||||
|
"CI_BUILD_NUMBER": strconv.FormatInt(m.Curr.Number, 10),
|
||||||
|
"CI_BUILD_PARENT": strconv.FormatInt(m.Curr.Parent, 10),
|
||||||
|
"CI_BUILD_EVENT": m.Curr.Event,
|
||||||
|
"CI_BUILD_LINK": m.Curr.Link,
|
||||||
|
"CI_BUILD_DEPLOY_TARGET": m.Curr.Target,
|
||||||
|
"CI_BUILD_STATUS": m.Curr.Status,
|
||||||
|
"CI_BUILD_CREATED": strconv.FormatInt(m.Curr.Created, 10),
|
||||||
|
"CI_BUILD_STARTED": strconv.FormatInt(m.Curr.Started, 10),
|
||||||
|
"CI_BUILD_FINISHED": strconv.FormatInt(m.Curr.Finished, 10),
|
||||||
|
// use CI_PREV_PIPELINE_*
|
||||||
|
"CI_PREV_BUILD_NUMBER": strconv.FormatInt(m.Prev.Number, 10),
|
||||||
|
"CI_PREV_BUILD_PARENT": strconv.FormatInt(m.Prev.Parent, 10),
|
||||||
|
"CI_PREV_BUILD_EVENT": m.Prev.Event,
|
||||||
|
"CI_PREV_BUILD_LINK": m.Prev.Link,
|
||||||
|
"CI_PREV_BUILD_DEPLOY_TARGET": m.Prev.Target,
|
||||||
|
"CI_PREV_BUILD_STATUS": m.Prev.Status,
|
||||||
|
"CI_PREV_BUILD_CREATED": strconv.FormatInt(m.Prev.Created, 10),
|
||||||
|
"CI_PREV_BUILD_STARTED": strconv.FormatInt(m.Prev.Started, 10),
|
||||||
|
"CI_PREV_BUILD_FINISHED": strconv.FormatInt(m.Prev.Finished, 10),
|
||||||
|
// use CI_STEP_*
|
||||||
|
"CI_JOB_NUMBER": strconv.Itoa(m.Step.Number),
|
||||||
|
"CI_JOB_STATUS": "", // will be set by agent
|
||||||
|
"CI_JOB_STARTED": "", // will be set by agent
|
||||||
|
"CI_JOB_FINISHED": "", // will be set by agent
|
||||||
|
// CI_REPO_CLONE_URL
|
||||||
|
"CI_REPO_REMOTE": m.Repo.CloneURL,
|
||||||
|
// use *_URL
|
||||||
|
"CI_REPO_LINK": m.Repo.Link,
|
||||||
|
"CI_COMMIT_LINK": m.Curr.Link,
|
||||||
|
"CI_PIPELINE_LINK": m.Curr.Link,
|
||||||
|
"CI_PREV_COMMIT_LINK": m.Prev.Link,
|
||||||
|
"CI_PREV_PIPELINE_LINK": m.Prev.Link,
|
||||||
|
"CI_SYSTEM_LINK": m.Sys.Link,
|
||||||
|
}
|
||||||
|
if m.Curr.Event == EventTag {
|
||||||
|
params["CI_COMMIT_TAG"] = strings.TrimPrefix(m.Curr.Commit.Ref, "refs/tags/")
|
||||||
|
}
|
||||||
|
if m.Curr.Event == EventPull {
|
||||||
|
params["CI_COMMIT_PULL_REQUEST"] = pullRegexp.FindString(m.Curr.Commit.Ref)
|
||||||
|
params["CI_COMMIT_PULL_REQUEST_LABELS"] = strings.Join(m.Curr.Commit.PullRequestLabels, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
122
pipeline/frontend/metadata/types.go
Normal file
122
pipeline/frontend/metadata/types.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright 2023 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Metadata defines runtime m.
|
||||||
|
Metadata struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Repo Repo `json:"repo,omitempty"`
|
||||||
|
Curr Pipeline `json:"curr,omitempty"`
|
||||||
|
Prev Pipeline `json:"prev,omitempty"`
|
||||||
|
Workflow Workflow `json:"workflow,omitempty"`
|
||||||
|
Step Step `json:"step,omitempty"`
|
||||||
|
Sys System `json:"sys,omitempty"`
|
||||||
|
Forge Forge `json:"forge,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repo defines runtime metadata for a repository.
|
||||||
|
Repo struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Owner string `json:"owner,omitempty"`
|
||||||
|
RemoteID string `json:"remote_id,omitempty"`
|
||||||
|
Link string `json:"link,omitempty"`
|
||||||
|
CloneURL string `json:"clone_url,omitempty"`
|
||||||
|
Private bool `json:"private,omitempty"`
|
||||||
|
Secrets []Secret `json:"secrets,omitempty"`
|
||||||
|
Branch string `json:"default_branch,omitempty"`
|
||||||
|
Trusted bool `json:"trusted,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pipeline defines runtime metadata for a pipeline.
|
||||||
|
Pipeline struct {
|
||||||
|
Number int64 `json:"number,omitempty"`
|
||||||
|
Created int64 `json:"created,omitempty"`
|
||||||
|
Started int64 `json:"started,omitempty"`
|
||||||
|
Finished int64 `json:"finished,omitempty"`
|
||||||
|
Timeout int64 `json:"timeout,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Event string `json:"event,omitempty"`
|
||||||
|
Link string `json:"link,omitempty"`
|
||||||
|
Target string `json:"target,omitempty"`
|
||||||
|
Trusted bool `json:"trusted,omitempty"`
|
||||||
|
Commit Commit `json:"commit,omitempty"`
|
||||||
|
Parent int64 `json:"parent,omitempty"`
|
||||||
|
Cron string `json:"cron,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit defines runtime metadata for a commit.
|
||||||
|
Commit struct {
|
||||||
|
Sha string `json:"sha,omitempty"`
|
||||||
|
Ref string `json:"ref,omitempty"`
|
||||||
|
Refspec string `json:"refspec,omitempty"`
|
||||||
|
Branch string `json:"branch,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
Author Author `json:"author,omitempty"`
|
||||||
|
ChangedFiles []string `json:"changed_files,omitempty"`
|
||||||
|
PullRequestLabels []string `json:"labels,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author defines runtime metadata for a commit author.
|
||||||
|
Author struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
Avatar string `json:"avatar,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workflow defines runtime metadata for a workflow.
|
||||||
|
Workflow struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Number int `json:"number,omitempty"`
|
||||||
|
Matrix map[string]string `json:"matrix,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step defines runtime metadata for a step.
|
||||||
|
Step struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Number int `json:"number,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secret defines a runtime secret
|
||||||
|
Secret struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
Mount string `json:"mount,omitempty"`
|
||||||
|
Mask bool `json:"mask,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// System defines runtime metadata for a ci/cd system.
|
||||||
|
System struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Host string `json:"host,omitempty"`
|
||||||
|
Link string `json:"link,omitempty"`
|
||||||
|
Platform string `json:"arch,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forge defines runtime metadata about the forge that host the repo
|
||||||
|
Forge struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerForge represent the needed func of a server forge to get its metadata
|
||||||
|
ServerForge interface {
|
||||||
|
// Name returns the string name of this driver
|
||||||
|
Name() string
|
||||||
|
// URL returns the root url of a configured forge
|
||||||
|
URL() string
|
||||||
|
}
|
||||||
|
)
|
132
pipeline/frontend/metadata_test.go
Normal file
132
pipeline/frontend/metadata_test.go
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2023 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package frontend_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/forge/mocks"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnvVarSubst(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
yaml string
|
||||||
|
environ map[string]string
|
||||||
|
want string
|
||||||
|
}{{
|
||||||
|
name: "simple substitution",
|
||||||
|
yaml: `pipeline:
|
||||||
|
step1:
|
||||||
|
image: ${HELLO_IMAGE}`,
|
||||||
|
environ: map[string]string{"HELLO_IMAGE": "hello-world"},
|
||||||
|
want: `pipeline:
|
||||||
|
step1:
|
||||||
|
image: hello-world`,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
result, err := frontend.EnvVarSubst(testCase.yaml, testCase.environ)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, testCase.want, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMetadataFromStruct(t *testing.T) {
|
||||||
|
forge := mocks.NewForge(t)
|
||||||
|
forge.On("Name").Return("gitea")
|
||||||
|
forge.On("URL").Return("https://gitea.com")
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
forge metadata.ServerForge
|
||||||
|
repo *model.Repo
|
||||||
|
pipeline, last *model.Pipeline
|
||||||
|
workflow *model.Step
|
||||||
|
link string
|
||||||
|
expectedMetadata metadata.Metadata
|
||||||
|
expectedEnviron map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Test with empty info",
|
||||||
|
expectedMetadata: metadata.Metadata{Sys: metadata.System{Name: "woodpecker"}},
|
||||||
|
expectedEnviron: map[string]string{
|
||||||
|
"CI": "woodpecker", "CI_BUILD_CREATED": "0", "CI_BUILD_DEPLOY_TARGET": "", "CI_BUILD_EVENT": "", "CI_BUILD_FINISHED": "0", "CI_BUILD_LINK": "", "CI_BUILD_NUMBER": "0", "CI_BUILD_PARENT": "0",
|
||||||
|
"CI_BUILD_STARTED": "0", "CI_BUILD_STATUS": "", "CI_COMMIT_AUTHOR": "", "CI_COMMIT_AUTHOR_AVATAR": "", "CI_COMMIT_AUTHOR_EMAIL": "", "CI_COMMIT_BRANCH": "", "CI_COMMIT_LINK": "",
|
||||||
|
"CI_COMMIT_MESSAGE": "", "CI_COMMIT_PULL_REQUEST": "", "CI_COMMIT_PULL_REQUEST_LABELS": "", "CI_COMMIT_REF": "", "CI_COMMIT_REFSPEC": "", "CI_COMMIT_SHA": "", "CI_COMMIT_SOURCE_BRANCH": "",
|
||||||
|
"CI_COMMIT_TAG": "", "CI_COMMIT_TARGET_BRANCH": "", "CI_COMMIT_URL": "", "CI_FORGE_TYPE": "", "CI_FORGE_URL": "", "CI_JOB_FINISHED": "", "CI_JOB_NUMBER": "0", "CI_JOB_STARTED": "",
|
||||||
|
"CI_JOB_STATUS": "", "CI_PIPELINE_CREATED": "0", "CI_PIPELINE_DEPLOY_TARGET": "", "CI_PIPELINE_EVENT": "", "CI_PIPELINE_FINISHED": "0", "CI_PIPELINE_LINK": "", "CI_PIPELINE_NUMBER": "0",
|
||||||
|
"CI_PIPELINE_PARENT": "0", "CI_PIPELINE_STARTED": "0", "CI_PIPELINE_STATUS": "", "CI_PIPELINE_URL": "", "CI_PREV_BUILD_CREATED": "0", "CI_PREV_BUILD_DEPLOY_TARGET": "",
|
||||||
|
"CI_PREV_BUILD_EVENT": "", "CI_PREV_BUILD_FINISHED": "0", "CI_PREV_BUILD_LINK": "", "CI_PREV_BUILD_NUMBER": "0", "CI_PREV_BUILD_PARENT": "0", "CI_PREV_BUILD_STARTED": "0",
|
||||||
|
"CI_PREV_BUILD_STATUS": "", "CI_PREV_COMMIT_AUTHOR": "", "CI_PREV_COMMIT_AUTHOR_AVATAR": "", "CI_PREV_COMMIT_AUTHOR_EMAIL": "", "CI_PREV_COMMIT_BRANCH": "", "CI_PREV_COMMIT_LINK": "",
|
||||||
|
"CI_PREV_COMMIT_MESSAGE": "", "CI_PREV_COMMIT_REF": "", "CI_PREV_COMMIT_REFSPEC": "", "CI_PREV_COMMIT_SHA": "", "CI_PREV_COMMIT_URL": "", "CI_PREV_PIPELINE_CREATED": "0",
|
||||||
|
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_LINK": "", "CI_PREV_PIPELINE_NUMBER": "0", "CI_PREV_PIPELINE_PARENT": "0",
|
||||||
|
"CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_STATUS": "", "CI_PREV_PIPELINE_URL": "", "CI_REPO": "", "CI_REPO_CLONE_URL": "", "CI_REPO_DEFAULT_BRANCH": "", "CI_REPO_LINK": "", "CI_REPO_REMOTE_ID": "",
|
||||||
|
"CI_REPO_NAME": "", "CI_REPO_OWNER": "", "CI_REPO_PRIVATE": "false", "CI_REPO_REMOTE": "", "CI_REPO_SCM": "git", "CI_REPO_TRUSTED": "false", "CI_REPO_URL": "", "CI_STEP_FINISHED": "",
|
||||||
|
"CI_STEP_NAME": "", "CI_STEP_NUMBER": "0", "CI_STEP_STARTED": "", "CI_STEP_STATUS": "", "CI_SYSTEM_ARCH": "", "CI_SYSTEM_HOST": "", "CI_SYSTEM_LINK": "", "CI_SYSTEM_NAME": "woodpecker",
|
||||||
|
"CI_SYSTEM_PLATFORM": "", "CI_SYSTEM_URL": "", "CI_SYSTEM_VERSION": "", "CI_WORKFLOW_NAME": "", "CI_WORKFLOW_NUMBER": "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test with forge",
|
||||||
|
forge: forge,
|
||||||
|
repo: &model.Repo{FullName: "testUser/testRepo", Link: "https://gitea.com/testUser/testRepo", Clone: "https://gitea.com/testUser/testRepo.git", Branch: "main", IsSCMPrivate: true},
|
||||||
|
pipeline: &model.Pipeline{Number: 3},
|
||||||
|
last: &model.Pipeline{Number: 2},
|
||||||
|
workflow: &model.Step{Name: "hello"},
|
||||||
|
link: "https://example.com",
|
||||||
|
expectedMetadata: metadata.Metadata{
|
||||||
|
Forge: metadata.Forge{Type: "gitea", URL: "https://gitea.com"},
|
||||||
|
Sys: metadata.System{Name: "woodpecker", Host: "example.com", Link: "https://example.com"},
|
||||||
|
Repo: metadata.Repo{Owner: "testUser", Name: "testRepo", Link: "https://gitea.com/testUser/testRepo", CloneURL: "https://gitea.com/testUser/testRepo.git", Branch: "main", Private: true},
|
||||||
|
Curr: metadata.Pipeline{Number: 3},
|
||||||
|
Prev: metadata.Pipeline{Number: 2},
|
||||||
|
Workflow: metadata.Workflow{Name: "hello"},
|
||||||
|
},
|
||||||
|
expectedEnviron: map[string]string{
|
||||||
|
"CI": "woodpecker", "CI_BUILD_CREATED": "0", "CI_BUILD_DEPLOY_TARGET": "", "CI_BUILD_EVENT": "", "CI_BUILD_FINISHED": "0", "CI_BUILD_LINK": "", "CI_BUILD_NUMBER": "3", "CI_BUILD_PARENT": "0",
|
||||||
|
"CI_BUILD_STARTED": "0", "CI_BUILD_STATUS": "", "CI_COMMIT_AUTHOR": "", "CI_COMMIT_AUTHOR_AVATAR": "", "CI_COMMIT_AUTHOR_EMAIL": "", "CI_COMMIT_BRANCH": "", "CI_COMMIT_LINK": "",
|
||||||
|
"CI_COMMIT_MESSAGE": "", "CI_COMMIT_PULL_REQUEST": "", "CI_COMMIT_PULL_REQUEST_LABELS": "", "CI_COMMIT_REF": "", "CI_COMMIT_REFSPEC": "", "CI_COMMIT_SHA": "", "CI_COMMIT_SOURCE_BRANCH": "",
|
||||||
|
"CI_COMMIT_TAG": "", "CI_COMMIT_TARGET_BRANCH": "", "CI_COMMIT_URL": "", "CI_FORGE_TYPE": "gitea", "CI_FORGE_URL": "https://gitea.com", "CI_JOB_FINISHED": "", "CI_JOB_NUMBER": "0",
|
||||||
|
"CI_JOB_STARTED": "", "CI_JOB_STATUS": "", "CI_PIPELINE_CREATED": "0", "CI_PIPELINE_DEPLOY_TARGET": "", "CI_PIPELINE_EVENT": "", "CI_PIPELINE_FINISHED": "0", "CI_PIPELINE_LINK": "",
|
||||||
|
"CI_PIPELINE_NUMBER": "3", "CI_PIPELINE_PARENT": "0", "CI_PIPELINE_STARTED": "0", "CI_PIPELINE_STATUS": "", "CI_PIPELINE_URL": "", "CI_PREV_BUILD_CREATED": "0", "CI_PREV_BUILD_DEPLOY_TARGET": "",
|
||||||
|
"CI_PREV_BUILD_EVENT": "", "CI_PREV_BUILD_FINISHED": "0", "CI_PREV_BUILD_LINK": "", "CI_PREV_BUILD_NUMBER": "2", "CI_PREV_BUILD_PARENT": "0", "CI_PREV_BUILD_STARTED": "0",
|
||||||
|
"CI_PREV_BUILD_STATUS": "", "CI_PREV_COMMIT_AUTHOR": "", "CI_PREV_COMMIT_AUTHOR_AVATAR": "", "CI_PREV_COMMIT_AUTHOR_EMAIL": "", "CI_PREV_COMMIT_BRANCH": "", "CI_PREV_COMMIT_LINK": "",
|
||||||
|
"CI_PREV_COMMIT_MESSAGE": "", "CI_PREV_COMMIT_REF": "", "CI_PREV_COMMIT_REFSPEC": "", "CI_PREV_COMMIT_SHA": "", "CI_PREV_COMMIT_URL": "", "CI_PREV_PIPELINE_CREATED": "0",
|
||||||
|
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_LINK": "", "CI_PREV_PIPELINE_NUMBER": "2", "CI_PREV_PIPELINE_PARENT": "0",
|
||||||
|
"CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_STATUS": "", "CI_PREV_PIPELINE_URL": "", "CI_REPO": "testUser/testRepo", "CI_REPO_CLONE_URL": "https://gitea.com/testUser/testRepo.git",
|
||||||
|
"CI_REPO_DEFAULT_BRANCH": "main", "CI_REPO_LINK": "https://gitea.com/testUser/testRepo", "CI_REPO_NAME": "testRepo", "CI_REPO_OWNER": "testUser", "CI_REPO_PRIVATE": "true", "CI_REPO_REMOTE_ID": "",
|
||||||
|
"CI_REPO_REMOTE": "https://gitea.com/testUser/testRepo.git", "CI_REPO_SCM": "git", "CI_REPO_TRUSTED": "false", "CI_REPO_URL": "https://gitea.com/testUser/testRepo", "CI_STEP_FINISHED": "",
|
||||||
|
"CI_STEP_NAME": "", "CI_STEP_NUMBER": "0", "CI_STEP_STARTED": "", "CI_STEP_STATUS": "", "CI_SYSTEM_ARCH": "", "CI_SYSTEM_HOST": "example.com", "CI_SYSTEM_LINK": "https://example.com",
|
||||||
|
"CI_SYSTEM_NAME": "woodpecker", "CI_SYSTEM_PLATFORM": "", "CI_SYSTEM_URL": "https://example.com", "CI_SYSTEM_VERSION": "", "CI_WORKFLOW_NAME": "hello", "CI_WORKFLOW_NUMBER": "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
result := frontend.MetadataFromStruct(testCase.forge, testCase.repo, testCase.pipeline, testCase.last, testCase.workflow, testCase.link)
|
||||||
|
assert.EqualValues(t, testCase.expectedMetadata, result)
|
||||||
|
assert.EqualValues(t, testCase.expectedEnviron, result.Environ())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,11 @@ package compiler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
||||||
"github.com/woodpecker-ci/woodpecker/shared/constant"
|
"github.com/woodpecker-ci/woodpecker/shared/constant"
|
||||||
)
|
)
|
||||||
@ -75,7 +76,7 @@ type Compiler struct {
|
|||||||
cloneEnv map[string]string
|
cloneEnv map[string]string
|
||||||
base string
|
base string
|
||||||
path string
|
path string
|
||||||
metadata frontend.Metadata
|
metadata metadata.Metadata
|
||||||
registries []Registry
|
registries []Registry
|
||||||
secrets secretMap
|
secrets secretMap
|
||||||
cacher Cacher
|
cacher Cacher
|
||||||
@ -156,7 +157,7 @@ func (c *Compiler) Compile(conf *yaml.Config) (*backend.Config, error) {
|
|||||||
// add default clone step
|
// add default clone step
|
||||||
if !c.local && len(conf.Clone.Containers) == 0 && !conf.SkipClone {
|
if !c.local && len(conf.Clone.Containers) == 0 && !conf.SkipClone {
|
||||||
cloneSettings := map[string]interface{}{"depth": "0"}
|
cloneSettings := map[string]interface{}{"depth": "0"}
|
||||||
if c.metadata.Curr.Event == frontend.EventTag {
|
if c.metadata.Curr.Event == metadata.EventTag {
|
||||||
cloneSettings["tags"] = "true"
|
cloneSettings["tags"] = "true"
|
||||||
}
|
}
|
||||||
container := &yaml.Container{
|
container := &yaml.Container{
|
||||||
@ -263,7 +264,7 @@ func (c *Compiler) setupCache(conf *yaml.Config, ir *backend.Config) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
container := c.cacher.Restore(c.metadata.Repo.Name, c.metadata.Curr.Commit.Branch, conf.Cache)
|
container := c.cacher.Restore(path.Join(c.metadata.Repo.Owner, c.metadata.Repo.Name), c.metadata.Curr.Commit.Branch, conf.Cache)
|
||||||
name := fmt.Sprintf("%s_restore_cache", c.prefix)
|
name := fmt.Sprintf("%s_restore_cache", c.prefix)
|
||||||
step := c.createProcess(name, container, "cache")
|
step := c.createProcess(name, container, "cache")
|
||||||
|
|
||||||
@ -276,10 +277,10 @@ func (c *Compiler) setupCache(conf *yaml.Config, ir *backend.Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) setupCacheRebuild(conf *yaml.Config, ir *backend.Config) {
|
func (c *Compiler) setupCacheRebuild(conf *yaml.Config, ir *backend.Config) {
|
||||||
if c.local || len(conf.Cache) == 0 || c.metadata.Curr.Event != frontend.EventPush || c.cacher == nil {
|
if c.local || len(conf.Cache) == 0 || c.metadata.Curr.Event != metadata.EventPush || c.cacher == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
container := c.cacher.Rebuild(c.metadata.Repo.Name, c.metadata.Curr.Commit.Branch, conf.Cache)
|
container := c.cacher.Rebuild(path.Join(c.metadata.Repo.Owner, c.metadata.Repo.Name), c.metadata.Curr.Commit.Branch, conf.Cache)
|
||||||
|
|
||||||
name := fmt.Sprintf("%s_rebuild_cache", c.prefix)
|
name := fmt.Sprintf("%s_rebuild_cache", c.prefix)
|
||||||
step := c.createProcess(name, container, "cache")
|
step := c.createProcess(name, container, "cache")
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/compiler/settings"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/compiler/settings"
|
||||||
)
|
)
|
||||||
@ -152,7 +152,7 @@ func (c *Compiler) createProcess(name string, container *yaml.Container, section
|
|||||||
|
|
||||||
failure := container.Failure
|
failure := container.Failure
|
||||||
if container.Failure == "" {
|
if container.Failure == "" {
|
||||||
failure = frontend.FailureFail
|
failure = metadata.FailureFail
|
||||||
}
|
}
|
||||||
|
|
||||||
return &backend.Step{
|
return &backend.Step{
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Option configures a compiler option.
|
// Option configures a compiler option.
|
||||||
@ -67,7 +67,7 @@ func WithSecret(secrets ...Secret) Option {
|
|||||||
// and system metadata. The metadata is used to remove steps from
|
// and system metadata. The metadata is used to remove steps from
|
||||||
// the compiled pipeline configuration that should be skipped. The
|
// the compiled pipeline configuration that should be skipped. The
|
||||||
// metadata is also added to each container as environment variables.
|
// metadata is also added to each container as environment variables.
|
||||||
func WithMetadata(metadata frontend.Metadata) Option {
|
func WithMetadata(metadata metadata.Metadata) Option {
|
||||||
return func(compiler *Compiler) {
|
return func(compiler *Compiler) {
|
||||||
compiler.metadata = metadata
|
compiler.metadata = metadata
|
||||||
|
|
||||||
|
@ -3,10 +3,9 @@ package compiler
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWithWorkspace(t *testing.T) {
|
func TestWithWorkspace(t *testing.T) {
|
||||||
@ -98,9 +97,10 @@ func TestWithPrefix(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWithMetadata(t *testing.T) {
|
func TestWithMetadata(t *testing.T) {
|
||||||
metadata := frontend.Metadata{
|
metadata := metadata.Metadata{
|
||||||
Repo: frontend.Repo{
|
Repo: metadata.Repo{
|
||||||
Name: "octocat/hello-world",
|
Owner: "octacat",
|
||||||
|
Name: "hello-world",
|
||||||
Private: true,
|
Private: true,
|
||||||
Link: "https://github.com/octocat/hello-world",
|
Link: "https://github.com/octocat/hello-world",
|
||||||
CloneURL: "https://github.com/octocat/hello-world.git",
|
CloneURL: "https://github.com/octocat/hello-world.git",
|
||||||
@ -113,7 +113,7 @@ func TestWithMetadata(t *testing.T) {
|
|||||||
t.Errorf("WithMetadata must set compiler the metadata")
|
t.Errorf("WithMetadata must set compiler the metadata")
|
||||||
}
|
}
|
||||||
|
|
||||||
if compiler.env["CI_REPO_NAME"] != strings.Split(metadata.Repo.Name, "/")[1] {
|
if compiler.env["CI_REPO_NAME"] != metadata.Repo.Name {
|
||||||
t.Errorf("WithMetadata must set CI_REPO_NAME")
|
t.Errorf("WithMetadata must set CI_REPO_NAME")
|
||||||
}
|
}
|
||||||
if compiler.env["CI_REPO_URL"] != metadata.Repo.Link {
|
if compiler.env["CI_REPO_URL"] != metadata.Repo.Link {
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package yaml
|
package yaml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"codeberg.org/6543/xyaml"
|
"codeberg.org/6543/xyaml"
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/constraint"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/constraint"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/types"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/types"
|
||||||
)
|
)
|
||||||
@ -23,7 +26,7 @@ type (
|
|||||||
RunsOn []string `yaml:"runs_on,omitempty"`
|
RunsOn []string `yaml:"runs_on,omitempty"`
|
||||||
SkipClone bool `yaml:"skip_clone"`
|
SkipClone bool `yaml:"skip_clone"`
|
||||||
// Deprecated use When.Branch
|
// Deprecated use When.Branch
|
||||||
Branches constraint.List
|
BranchesDontUseIt *constraint.List `yaml:"branches,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workspace defines a pipeline workspace.
|
// Workspace defines a pipeline workspace.
|
||||||
@ -41,6 +44,18 @@ func ParseBytes(b []byte) (*Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// support deprecated branch filter
|
||||||
|
if out.BranchesDontUseIt != nil {
|
||||||
|
if out.When.Constraints == nil {
|
||||||
|
out.When.Constraints = []constraint.Constraint{{Branch: *out.BranchesDontUseIt}}
|
||||||
|
} else if len(out.When.Constraints) == 1 && out.When.Constraints[0].Branch.IsEmpty() {
|
||||||
|
out.When.Constraints[0].Branch = *out.BranchesDontUseIt
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("could not apply deprecated branches filter into global when filter")
|
||||||
|
}
|
||||||
|
out.BranchesDontUseIt = nil
|
||||||
|
}
|
||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/franela/goblin"
|
"github.com/franela/goblin"
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/types"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -79,8 +79,8 @@ func TestParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.It("Should match event tester", func() {
|
g.It("Should match event tester", func() {
|
||||||
match, err := matchConfig.When.Match(frontend.Metadata{
|
match, err := matchConfig.When.Match(metadata.Metadata{
|
||||||
Curr: frontend.Pipeline{
|
Curr: metadata.Pipeline{
|
||||||
Event: "tester",
|
Event: "tester",
|
||||||
},
|
},
|
||||||
}, false)
|
}, false)
|
||||||
@ -89,8 +89,8 @@ func TestParse(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
g.It("Should match event tester2", func() {
|
g.It("Should match event tester2", func() {
|
||||||
match, err := matchConfig.When.Match(frontend.Metadata{
|
match, err := matchConfig.When.Match(metadata.Metadata{
|
||||||
Curr: frontend.Pipeline{
|
Curr: metadata.Pipeline{
|
||||||
Event: "tester2",
|
Event: "tester2",
|
||||||
},
|
},
|
||||||
}, false)
|
}, false)
|
||||||
@ -99,9 +99,9 @@ func TestParse(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
g.It("Should match branch tester", func() {
|
g.It("Should match branch tester", func() {
|
||||||
match, err := matchConfig.When.Match(frontend.Metadata{
|
match, err := matchConfig.When.Match(metadata.Metadata{
|
||||||
Curr: frontend.Pipeline{
|
Curr: metadata.Pipeline{
|
||||||
Commit: frontend.Commit{
|
Commit: metadata.Commit{
|
||||||
Branch: "tester",
|
Branch: "tester",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -111,8 +111,8 @@ func TestParse(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
g.It("Should not match event push", func() {
|
g.It("Should not match event push", func() {
|
||||||
match, err := matchConfig.When.Match(frontend.Metadata{
|
match, err := matchConfig.When.Match(metadata.Metadata{
|
||||||
Curr: frontend.Pipeline{
|
Curr: metadata.Pipeline{
|
||||||
Event: "push",
|
Event: "push",
|
||||||
},
|
},
|
||||||
}, false)
|
}, false)
|
||||||
|
@ -3,13 +3,14 @@ package constraint
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/antonmedv/expr"
|
"github.com/antonmedv/expr"
|
||||||
"github.com/bmatcuk/doublestar/v4"
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/types"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,7 +62,7 @@ func (when *When) IsEmpty() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if at least one of the internal constraints is true.
|
// Returns true if at least one of the internal constraints is true.
|
||||||
func (when *When) Match(metadata frontend.Metadata, global bool) (bool, error) {
|
func (when *When) Match(metadata metadata.Metadata, global bool) (bool, error) {
|
||||||
for _, c := range when.Constraints {
|
for _, c := range when.Constraints {
|
||||||
match, err := c.Match(metadata, global)
|
match, err := c.Match(metadata, global)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -138,37 +139,37 @@ func (when *When) UnmarshalYAML(value *yaml.Node) error {
|
|||||||
|
|
||||||
// Match returns true if all constraints match the given input. If a single
|
// Match returns true if all constraints match the given input. If a single
|
||||||
// constraint fails a false value is returned.
|
// constraint fails a false value is returned.
|
||||||
func (c *Constraint) Match(metadata frontend.Metadata, global bool) (bool, error) {
|
func (c *Constraint) Match(m metadata.Metadata, global bool) (bool, error) {
|
||||||
match := true
|
match := true
|
||||||
if !global {
|
if !global {
|
||||||
c.SetDefaultEventFilter()
|
c.SetDefaultEventFilter()
|
||||||
|
|
||||||
// apply step only filters
|
// apply step only filters
|
||||||
match = c.Matrix.Match(metadata.Workflow.Matrix)
|
match = c.Matrix.Match(m.Workflow.Matrix)
|
||||||
}
|
}
|
||||||
|
|
||||||
match = match && c.Platform.Match(metadata.Sys.Platform) &&
|
match = match && c.Platform.Match(m.Sys.Platform) &&
|
||||||
c.Environment.Match(metadata.Curr.Target) &&
|
c.Environment.Match(m.Curr.Target) &&
|
||||||
c.Event.Match(metadata.Curr.Event) &&
|
c.Event.Match(m.Curr.Event) &&
|
||||||
c.Repo.Match(metadata.Repo.Name) &&
|
c.Repo.Match(path.Join(m.Repo.Owner, m.Repo.Name)) &&
|
||||||
c.Ref.Match(metadata.Curr.Commit.Ref) &&
|
c.Ref.Match(m.Curr.Commit.Ref) &&
|
||||||
c.Instance.Match(metadata.Sys.Host)
|
c.Instance.Match(m.Sys.Host)
|
||||||
|
|
||||||
// changed files filter apply only for pull-request and push events
|
// changed files filter apply only for pull-request and push events
|
||||||
if metadata.Curr.Event == frontend.EventPull || metadata.Curr.Event == frontend.EventPush {
|
if m.Curr.Event == metadata.EventPull || m.Curr.Event == metadata.EventPush {
|
||||||
match = match && c.Path.Match(metadata.Curr.Commit.ChangedFiles, metadata.Curr.Commit.Message)
|
match = match && c.Path.Match(m.Curr.Commit.ChangedFiles, m.Curr.Commit.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if metadata.Curr.Event != frontend.EventTag {
|
if m.Curr.Event != metadata.EventTag {
|
||||||
match = match && c.Branch.Match(metadata.Curr.Commit.Branch)
|
match = match && c.Branch.Match(m.Curr.Commit.Branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
if metadata.Curr.Event == frontend.EventCron {
|
if m.Curr.Event == metadata.EventCron {
|
||||||
match = match && c.Cron.Match(metadata.Curr.Cron)
|
match = match && c.Cron.Match(m.Curr.Cron)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Evaluate != "" {
|
if c.Evaluate != "" {
|
||||||
env := metadata.Environ()
|
env := m.Environ()
|
||||||
out, err := expr.Compile(c.Evaluate, expr.Env(env), expr.AsBool())
|
out, err := expr.Compile(c.Evaluate, expr.Env(env), expr.AsBool())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -187,11 +188,11 @@ func (c *Constraint) Match(metadata frontend.Metadata, global bool) (bool, error
|
|||||||
func (c *Constraint) SetDefaultEventFilter() {
|
func (c *Constraint) SetDefaultEventFilter() {
|
||||||
if c.Event.IsEmpty() {
|
if c.Event.IsEmpty() {
|
||||||
c.Event.Include = []string{
|
c.Event.Include = []string{
|
||||||
frontend.EventPush,
|
metadata.EventPush,
|
||||||
frontend.EventPull,
|
metadata.EventPull,
|
||||||
frontend.EventTag,
|
metadata.EventTag,
|
||||||
frontend.EventDeploy,
|
metadata.EventDeploy,
|
||||||
frontend.EventManual,
|
metadata.EventManual,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConstraint(t *testing.T) {
|
func TestConstraint(t *testing.T) {
|
||||||
@ -403,15 +404,15 @@ func TestConstraints(t *testing.T) {
|
|||||||
testdata := []struct {
|
testdata := []struct {
|
||||||
desc string
|
desc string
|
||||||
conf string
|
conf string
|
||||||
with frontend.Metadata
|
with metadata.Metadata
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "no constraints, must match on default events",
|
desc: "no constraints, must match on default events",
|
||||||
conf: "",
|
conf: "",
|
||||||
with: frontend.Metadata{
|
with: metadata.Metadata{
|
||||||
Curr: frontend.Pipeline{
|
Curr: metadata.Pipeline{
|
||||||
Event: frontend.EventPush,
|
Event: metadata.EventPush,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: true,
|
want: true,
|
||||||
@ -419,106 +420,117 @@ func TestConstraints(t *testing.T) {
|
|||||||
{
|
{
|
||||||
desc: "global branch filter",
|
desc: "global branch filter",
|
||||||
conf: "{ branch: develop }",
|
conf: "{ branch: develop }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush, Commit: frontend.Commit{Branch: "master"}}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush, Commit: metadata.Commit{Branch: "master"}}},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "global branch filter",
|
desc: "global branch filter",
|
||||||
conf: "{ branch: master }",
|
conf: "{ branch: master }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush, Commit: frontend.Commit{Branch: "master"}}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush, Commit: metadata.Commit{Branch: "master"}}},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "repo constraint",
|
desc: "repo constraint",
|
||||||
conf: "{ repo: owner/* }",
|
conf: "{ repo: owner/* }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush}, Repo: frontend.Repo{Name: "owner/repo"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush}, Repo: metadata.Repo{Owner: "owner", Name: "repo"}},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "repo constraint",
|
desc: "repo constraint",
|
||||||
conf: "{ repo: octocat/* }",
|
conf: "{ repo: octocat/* }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush}, Repo: frontend.Repo{Name: "owner/repo"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush}, Repo: metadata.Repo{Owner: "owner", Name: "repo"}},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ref constraint",
|
desc: "ref constraint",
|
||||||
conf: "{ ref: refs/tags/* }",
|
conf: "{ ref: refs/tags/* }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Commit: frontend.Commit{Ref: "refs/tags/v1.0.0"}, Event: frontend.EventPush}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Commit: metadata.Commit{Ref: "refs/tags/v1.0.0"}, Event: metadata.EventPush}},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ref constraint",
|
desc: "ref constraint",
|
||||||
conf: "{ ref: refs/tags/* }",
|
conf: "{ ref: refs/tags/* }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Commit: frontend.Commit{Ref: "refs/heads/master"}, Event: frontend.EventPush}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Commit: metadata.Commit{Ref: "refs/heads/master"}, Event: metadata.EventPush}},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "platform constraint",
|
desc: "platform constraint",
|
||||||
conf: "{ platform: linux/amd64 }",
|
conf: "{ platform: linux/amd64 }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush}, Sys: frontend.System{Platform: "linux/amd64"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush}, Sys: metadata.System{Platform: "linux/amd64"}},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "platform constraint",
|
desc: "platform constraint",
|
||||||
conf: "{ repo: linux/amd64 }",
|
conf: "{ repo: linux/amd64 }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush}, Sys: frontend.System{Platform: "windows/amd64"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush}, Sys: metadata.System{Platform: "windows/amd64"}},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "instance constraint",
|
desc: "instance constraint",
|
||||||
conf: "{ instance: agent.tld }",
|
conf: "{ instance: agent.tld }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush}, Sys: frontend.System{Host: "agent.tld"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush}, Sys: metadata.System{Host: "agent.tld"}},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "instance constraint",
|
desc: "instance constraint",
|
||||||
conf: "{ instance: agent.tld }",
|
conf: "{ instance: agent.tld }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush}, Sys: frontend.System{Host: "beta.agent.tld"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush}, Sys: metadata.System{Host: "beta.agent.tld"}},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "filter cron by default constraint",
|
desc: "filter cron by default constraint",
|
||||||
conf: "{}",
|
conf: "{}",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventCron}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventCron}},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "filter cron by matching name",
|
desc: "filter cron by matching name",
|
||||||
conf: "{ event: cron, cron: job1 }",
|
conf: "{ event: cron, cron: job1 }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventCron, Cron: "job1"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventCron, Cron: "job1"}},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "filter cron by name",
|
desc: "filter cron by name",
|
||||||
conf: "{ event: cron, cron: job2 }",
|
conf: "{ event: cron, cron: job2 }",
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventCron, Cron: "job1"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventCron, Cron: "job1"}},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "no constraints, event gets filtered by default event filter",
|
desc: "no constraints, event gets filtered by default event filter",
|
||||||
conf: "",
|
conf: "",
|
||||||
with: frontend.Metadata{
|
with: metadata.Metadata{
|
||||||
Curr: frontend.Pipeline{Event: "non-default"},
|
Curr: metadata.Pipeline{Event: "non-default"},
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "filter with build-in env passes",
|
||||||
|
conf: "{ branch: ${CI_REPO_DEFAULT_BRANCH} }",
|
||||||
|
with: metadata.Metadata{
|
||||||
|
Curr: metadata.Pipeline{Event: metadata.EventPush, Commit: metadata.Commit{Branch: "stable"}},
|
||||||
|
Repo: metadata.Repo{Branch: "stable"},
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "filter by eval based on event",
|
desc: "filter by eval based on event",
|
||||||
conf: `{ evaluate: 'CI_PIPELINE_EVENT == "push"' }`,
|
conf: `{ evaluate: 'CI_PIPELINE_EVENT == "push"' }`,
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush}},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "filter by eval based on event and repo",
|
desc: "filter by eval based on event and repo",
|
||||||
conf: `{ evaluate: 'CI_PIPELINE_EVENT == "push" && CI_REPO == "owner/repo"' }`,
|
conf: `{ evaluate: 'CI_PIPELINE_EVENT == "push" && CI_REPO == "owner/repo"' }`,
|
||||||
with: frontend.Metadata{Curr: frontend.Pipeline{Event: frontend.EventPush}, Repo: frontend.Repo{Name: "owner/repo"}},
|
with: metadata.Metadata{Curr: metadata.Pipeline{Event: metadata.EventPush}, Repo: metadata.Repo{Owner: "owner", Name: "repo"}},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testdata {
|
for _, test := range testdata {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
c := parseConstraints(t, test.conf)
|
conf, err := frontend.EnvVarSubst(test.conf, test.with.Environ())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
c := parseConstraints(t, conf)
|
||||||
got, err := c.Match(test.with, false)
|
got, err := c.Match(test.with, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Match returned error: %v", err)
|
t.Errorf("Match returned error: %v", err)
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/multipart"
|
"github.com/woodpecker-ci/woodpecker/pipeline/multipart"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ func (r *Runtime) execAll(steps []*backend.Step) <-chan error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add compatibility for drone-ci plugins
|
// add compatibility for drone-ci plugins
|
||||||
SetDroneEnviron(step.Environment)
|
metadata.SetDroneEnviron(step.Environment)
|
||||||
|
|
||||||
logger.Debug().
|
logger.Debug().
|
||||||
Str("Step", step.Name).
|
Str("Step", step.Name).
|
||||||
@ -199,7 +199,7 @@ func (r *Runtime) execAll(steps []*backend.Step) <-chan error {
|
|||||||
|
|
||||||
// Return the error after tracing it.
|
// Return the error after tracing it.
|
||||||
err = r.traceStep(processState, err, step)
|
err = r.traceStep(processState, err, step)
|
||||||
if err != nil && step.Failure == frontend.FailureIgnore {
|
if err != nil && step.Failure == metadata.FailureIgnore {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -17,16 +17,15 @@ package pipeline
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/drone/envsubst"
|
|
||||||
"github.com/oklog/ulid/v2"
|
"github.com/oklog/ulid/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/compiler"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/compiler"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/linter"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/linter"
|
||||||
@ -47,6 +46,7 @@ type StepBuilder struct {
|
|||||||
Link string
|
Link string
|
||||||
Yamls []*forge_types.FileMeta
|
Yamls []*forge_types.FileMeta
|
||||||
Envs map[string]string
|
Envs map[string]string
|
||||||
|
Forge metadata.ServerForge
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
@ -85,8 +85,8 @@ func (b *StepBuilder) Build() ([]*Item, error) {
|
|||||||
Name: SanitizePath(y.Name),
|
Name: SanitizePath(y.Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := metadataFromStruct(b.Repo, b.Curr, b.Last, workflow, b.Link)
|
workflowMetadata := frontend.MetadataFromStruct(b.Forge, b.Repo, b.Curr, b.Last, workflow, b.Link)
|
||||||
environ := b.environmentVariables(metadata, axis)
|
environ := b.environmentVariables(workflowMetadata, axis)
|
||||||
|
|
||||||
// add global environment variables for substituting
|
// add global environment variables for substituting
|
||||||
for k, v := range b.Envs {
|
for k, v := range b.Envs {
|
||||||
@ -98,7 +98,7 @@ func (b *StepBuilder) Build() ([]*Item, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// substitute vars
|
// substitute vars
|
||||||
substituted, err := b.envsubst(string(y.Data), environ)
|
substituted, err := frontend.EnvVarSubst(string(y.Data), environ)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ func (b *StepBuilder) Build() ([]*Item, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checking if filtered.
|
// checking if filtered.
|
||||||
if match, err := parsed.When.Match(metadata, true); !match && err == nil {
|
if match, err := parsed.When.Match(workflowMetadata, true); !match && err == nil {
|
||||||
log.Debug().Str("pipeline", workflow.Name).Msg(
|
log.Debug().Str("pipeline", workflow.Name).Msg(
|
||||||
"Marked as skipped, dose not match metadata",
|
"Marked as skipped, dose not match metadata",
|
||||||
)
|
)
|
||||||
@ -129,15 +129,7 @@ func (b *StepBuilder) Build() ([]*Item, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deprecated branches filter => remove after some time
|
ir, err := b.toInternalRepresentation(parsed, environ, workflowMetadata, workflow.ID)
|
||||||
if !parsed.Branches.Match(b.Curr.Branch) && (b.Curr.Event != model.EventDeploy && b.Curr.Event != model.EventTag) {
|
|
||||||
log.Debug().Str("pipeline", workflow.Name).Msg(
|
|
||||||
"Marked as skipped, dose not match branch",
|
|
||||||
)
|
|
||||||
workflow.State = model.StatusSkipped
|
|
||||||
}
|
|
||||||
|
|
||||||
ir, err := b.toInternalRepresentation(parsed, environ, metadata, workflow.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -216,17 +208,7 @@ func containsItemWithName(name string, items []*Item) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *StepBuilder) envsubst(y string, environ map[string]string) (string, error) {
|
func (b *StepBuilder) environmentVariables(metadata metadata.Metadata, axis matrix.Axis) map[string]string {
|
||||||
return envsubst.Eval(y, func(name string) string {
|
|
||||||
env := environ[name]
|
|
||||||
if strings.Contains(env, "\n") {
|
|
||||||
env = fmt.Sprintf("%q", env)
|
|
||||||
}
|
|
||||||
return env
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *StepBuilder) environmentVariables(metadata frontend.Metadata, axis matrix.Axis) map[string]string {
|
|
||||||
environ := metadata.Environ()
|
environ := metadata.Environ()
|
||||||
for k, v := range axis {
|
for k, v := range axis {
|
||||||
environ[k] = v
|
environ[k] = v
|
||||||
@ -234,7 +216,7 @@ func (b *StepBuilder) environmentVariables(metadata frontend.Metadata, axis matr
|
|||||||
return environ
|
return environ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *StepBuilder) toInternalRepresentation(parsed *yaml.Config, environ map[string]string, metadata frontend.Metadata, stepID int64) (*backend.Config, error) {
|
func (b *StepBuilder) toInternalRepresentation(parsed *yaml.Config, environ map[string]string, metadata metadata.Metadata, stepID int64) (*backend.Config, error) {
|
||||||
var secrets []compiler.Secret
|
var secrets []compiler.Secret
|
||||||
for _, sec := range b.Secs {
|
for _, sec := range b.Secs {
|
||||||
if !sec.Match(b.Curr.Event) {
|
if !sec.Match(b.Curr.Event) {
|
||||||
@ -261,6 +243,7 @@ func (b *StepBuilder) toInternalRepresentation(parsed *yaml.Config, environ map[
|
|||||||
return compiler.New(
|
return compiler.New(
|
||||||
compiler.WithEnviron(environ),
|
compiler.WithEnviron(environ),
|
||||||
compiler.WithEnviron(b.Envs),
|
compiler.WithEnviron(b.Envs),
|
||||||
|
// TODO: server deps should be moved into StepBuilder fields and set on StepBuilder creation
|
||||||
compiler.WithEscalated(server.Config.Pipeline.Privileged...),
|
compiler.WithEscalated(server.Config.Pipeline.Privileged...),
|
||||||
compiler.WithResourceLimit(server.Config.Pipeline.Limits.MemSwapLimit, server.Config.Pipeline.Limits.MemLimit, server.Config.Pipeline.Limits.ShmSize, server.Config.Pipeline.Limits.CPUQuota, server.Config.Pipeline.Limits.CPUShares, server.Config.Pipeline.Limits.CPUSet),
|
compiler.WithResourceLimit(server.Config.Pipeline.Limits.MemSwapLimit, server.Config.Pipeline.Limits.MemLimit, server.Config.Pipeline.Limits.ShmSize, server.Config.Pipeline.Limits.CPUQuota, server.Config.Pipeline.Limits.CPUShares, server.Config.Pipeline.Limits.CPUSet),
|
||||||
compiler.WithVolumes(server.Config.Pipeline.Volumes...),
|
compiler.WithVolumes(server.Config.Pipeline.Volumes...),
|
||||||
@ -328,87 +311,6 @@ func SetPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*Item)
|
|||||||
return pipeline
|
return pipeline
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the metadata from the cli context.
|
|
||||||
func metadataFromStruct(repo *model.Repo, pipeline, last *model.Pipeline, workflow *model.Step, link string) frontend.Metadata {
|
|
||||||
host := link
|
|
||||||
uri, err := url.Parse(link)
|
|
||||||
if err == nil {
|
|
||||||
host = uri.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
forge := frontend.Forge{}
|
|
||||||
if server.Config.Services.Forge != nil {
|
|
||||||
forge = frontend.Forge{
|
|
||||||
Type: server.Config.Services.Forge.Name(),
|
|
||||||
URL: server.Config.Services.Forge.URL(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return frontend.Metadata{
|
|
||||||
Repo: frontend.Repo{
|
|
||||||
Name: repo.FullName,
|
|
||||||
Link: repo.Link,
|
|
||||||
CloneURL: repo.Clone,
|
|
||||||
Private: repo.IsSCMPrivate,
|
|
||||||
Branch: repo.Branch,
|
|
||||||
},
|
|
||||||
Curr: metadataPipelineFromModelPipeline(pipeline, true),
|
|
||||||
Prev: metadataPipelineFromModelPipeline(last, false),
|
|
||||||
Workflow: frontend.Workflow{
|
|
||||||
Name: workflow.Name,
|
|
||||||
Number: workflow.PID,
|
|
||||||
Matrix: workflow.Environ,
|
|
||||||
},
|
|
||||||
Step: frontend.Step{},
|
|
||||||
Sys: frontend.System{
|
|
||||||
Name: "woodpecker",
|
|
||||||
Link: link,
|
|
||||||
Host: host,
|
|
||||||
Platform: "", // will be set by pipeline platform option or by agent
|
|
||||||
},
|
|
||||||
Forge: forge,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func metadataPipelineFromModelPipeline(pipeline *model.Pipeline, includeParent bool) frontend.Pipeline {
|
|
||||||
cron := ""
|
|
||||||
if pipeline.Event == model.EventCron {
|
|
||||||
cron = pipeline.Sender
|
|
||||||
}
|
|
||||||
|
|
||||||
parent := int64(0)
|
|
||||||
if includeParent {
|
|
||||||
parent = pipeline.Parent
|
|
||||||
}
|
|
||||||
|
|
||||||
return frontend.Pipeline{
|
|
||||||
Number: pipeline.Number,
|
|
||||||
Parent: parent,
|
|
||||||
Created: pipeline.Created,
|
|
||||||
Started: pipeline.Started,
|
|
||||||
Finished: pipeline.Finished,
|
|
||||||
Status: string(pipeline.Status),
|
|
||||||
Event: string(pipeline.Event),
|
|
||||||
Link: pipeline.Link,
|
|
||||||
Target: pipeline.Deploy,
|
|
||||||
Commit: frontend.Commit{
|
|
||||||
Sha: pipeline.Commit,
|
|
||||||
Ref: pipeline.Ref,
|
|
||||||
Refspec: pipeline.Refspec,
|
|
||||||
Branch: pipeline.Branch,
|
|
||||||
Message: pipeline.Message,
|
|
||||||
Author: frontend.Author{
|
|
||||||
Name: pipeline.Author,
|
|
||||||
Email: pipeline.Email,
|
|
||||||
Avatar: pipeline.Avatar,
|
|
||||||
},
|
|
||||||
ChangedFiles: pipeline.ChangedFiles,
|
|
||||||
PullRequestLabels: pipeline.PullRequestLabels,
|
|
||||||
},
|
|
||||||
Cron: cron,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SanitizePath(path string) string {
|
func SanitizePath(path string) string {
|
||||||
path = filepath.Base(path)
|
path = filepath.Base(path)
|
||||||
path = strings.TrimSuffix(path, ".yml")
|
path = strings.TrimSuffix(path, ".yml")
|
||||||
|
@ -17,10 +17,11 @@ package pipeline
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/woodpecker-ci/woodpecker/server"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/forge"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/forge/mocks"
|
"github.com/woodpecker-ci/woodpecker/server/forge/mocks"
|
||||||
forge_types "github.com/woodpecker-ci/woodpecker/server/forge/types"
|
forge_types "github.com/woodpecker-ci/woodpecker/server/forge/types"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
@ -28,9 +29,9 @@ import (
|
|||||||
|
|
||||||
func TestGlobalEnvsubst(t *testing.T) {
|
func TestGlobalEnvsubst(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Envs: map[string]string{
|
Envs: map[string]string{
|
||||||
"KEY_K": "VALUE_V",
|
"KEY_K": "VALUE_V",
|
||||||
"IMAGE": "scratch",
|
"IMAGE": "scratch",
|
||||||
@ -63,9 +64,9 @@ pipeline:
|
|||||||
|
|
||||||
func TestMissingGlobalEnvsubst(t *testing.T) {
|
func TestMissingGlobalEnvsubst(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Envs: map[string]string{
|
Envs: map[string]string{
|
||||||
"KEY_K": "VALUE_V",
|
"KEY_K": "VALUE_V",
|
||||||
"NO_IMAGE": "scratch",
|
"NO_IMAGE": "scratch",
|
||||||
@ -98,10 +99,10 @@ pipeline:
|
|||||||
|
|
||||||
func TestMultilineEnvsubst(t *testing.T) {
|
func TestMultilineEnvsubst(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
Repo: &model.Repo{},
|
Forge: getMockForge(t),
|
||||||
|
Repo: &model.Repo{},
|
||||||
Curr: &model.Pipeline{
|
Curr: &model.Pipeline{
|
||||||
Message: `aaa
|
Message: `aaa
|
||||||
bbb`,
|
bbb`,
|
||||||
@ -136,9 +137,9 @@ pipeline:
|
|||||||
|
|
||||||
func TestMultiPipeline(t *testing.T) {
|
func TestMultiPipeline(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: &model.Pipeline{},
|
Curr: &model.Pipeline{},
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -171,9 +172,9 @@ pipeline:
|
|||||||
|
|
||||||
func TestDependsOn(t *testing.T) {
|
func TestDependsOn(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: &model.Pipeline{},
|
Curr: &model.Pipeline{},
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -218,9 +219,9 @@ depends_on:
|
|||||||
|
|
||||||
func TestRunsOn(t *testing.T) {
|
func TestRunsOn(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: &model.Pipeline{},
|
Curr: &model.Pipeline{},
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -255,9 +256,9 @@ runs_on:
|
|||||||
|
|
||||||
func TestPipelineName(t *testing.T) {
|
func TestPipelineName(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{Config: ".woodpecker"},
|
Repo: &model.Repo{Config: ".woodpecker"},
|
||||||
Curr: &model.Pipeline{},
|
Curr: &model.Pipeline{},
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -291,9 +292,9 @@ pipeline:
|
|||||||
|
|
||||||
func TestBranchFilter(t *testing.T) {
|
func TestBranchFilter(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: &model.Pipeline{Branch: "dev"},
|
Curr: &model.Pipeline{Branch: "dev"},
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -320,27 +321,19 @@ pipeline:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(pipelineItems) != 2 {
|
if !assert.Len(t, pipelineItems, 1) {
|
||||||
t.Fatal("Should have generated 2 pipeline")
|
t.Fatal("Should have generated 1 pipeline")
|
||||||
}
|
}
|
||||||
if pipelineItems[0].Workflow.State != model.StatusSkipped {
|
if pipelineItems[0].Workflow.State != model.StatusPending {
|
||||||
t.Fatal("Should not run on dev branch")
|
|
||||||
}
|
|
||||||
for _, child := range pipelineItems[0].Workflow.Children {
|
|
||||||
if child.State != model.StatusSkipped {
|
|
||||||
t.Fatal("Children should skipped status too")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pipelineItems[1].Workflow.State != model.StatusPending {
|
|
||||||
t.Fatal("Should run on dev branch")
|
t.Fatal("Should run on dev branch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRootWhenFilter(t *testing.T) {
|
func TestRootWhenFilter(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: &model.Pipeline{Event: "tester"},
|
Curr: &model.Pipeline{Event: "tester"},
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -385,11 +378,11 @@ pipeline:
|
|||||||
|
|
||||||
func TestZeroSteps(t *testing.T) {
|
func TestZeroSteps(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
pipeline := &model.Pipeline{Branch: "dev"}
|
pipeline := &model.Pipeline{Branch: "dev"}
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: pipeline,
|
Curr: pipeline,
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -420,11 +413,11 @@ pipeline:
|
|||||||
|
|
||||||
func TestZeroStepsAsMultiPipelineDeps(t *testing.T) {
|
func TestZeroStepsAsMultiPipelineDeps(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
pipeline := &model.Pipeline{Branch: "dev"}
|
pipeline := &model.Pipeline{Branch: "dev"}
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: pipeline,
|
Curr: pipeline,
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -469,11 +462,11 @@ depends_on: [ zerostep ]
|
|||||||
|
|
||||||
func TestZeroStepsAsMultiPipelineTransitiveDeps(t *testing.T) {
|
func TestZeroStepsAsMultiPipelineTransitiveDeps(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
pipeline := &model.Pipeline{Branch: "dev"}
|
pipeline := &model.Pipeline{Branch: "dev"}
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: pipeline,
|
Curr: pipeline,
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -524,13 +517,13 @@ depends_on: [ shouldbefiltered ]
|
|||||||
|
|
||||||
func TestTree(t *testing.T) {
|
func TestTree(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
pipeline := &model.Pipeline{
|
pipeline := &model.Pipeline{
|
||||||
Event: model.EventPush,
|
Event: model.EventPush,
|
||||||
}
|
}
|
||||||
|
|
||||||
b := StepBuilder{
|
b := StepBuilder{
|
||||||
|
Forge: getMockForge(t),
|
||||||
Repo: &model.Repo{},
|
Repo: &model.Repo{},
|
||||||
Curr: pipeline,
|
Curr: pipeline,
|
||||||
Last: &model.Pipeline{},
|
Last: &model.Pipeline{},
|
||||||
@ -565,7 +558,6 @@ pipeline:
|
|||||||
|
|
||||||
func TestSanitizePath(t *testing.T) {
|
func TestSanitizePath(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
setupMockForge(t)
|
|
||||||
|
|
||||||
testTable := []struct {
|
testTable := []struct {
|
||||||
path string
|
path string
|
||||||
@ -604,14 +596,9 @@ func TestSanitizePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var setupMockForgeLock = sync.Once{}
|
func getMockForge(t *testing.T) forge.Forge {
|
||||||
|
forge := mocks.NewForge(t)
|
||||||
func setupMockForge(t *testing.T) {
|
forge.On("Name").Return("mock")
|
||||||
setupMockForgeLock.Do(func() {
|
forge.On("URL").Return("https://codeberg.org")
|
||||||
forge := mocks.NewForge(t)
|
return forge
|
||||||
forge.On("Name").Return("mock")
|
|
||||||
forge.On("URL").Return("https://codeberg.org")
|
|
||||||
|
|
||||||
server.Config.Services.Forge = forge
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ func Create(ctx context.Context, _store store.Store, repo *model.Repo, pipeline
|
|||||||
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 configFetchErr == nil {
|
if configFetchErr == nil {
|
||||||
filtered, parseErr = checkIfFiltered(pipeline, forgeYamlConfigs)
|
filtered, parseErr = checkIfFiltered(repo, pipeline, forgeYamlConfigs)
|
||||||
if parseErr == nil {
|
if parseErr == nil {
|
||||||
if filtered {
|
if filtered {
|
||||||
err := ErrFiltered{Msg: "branch does not match restrictions defined in yaml"}
|
err := ErrFiltered{Msg: "branch does not match restrictions defined in yaml"}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/woodpecker-ci/woodpecker/pipeline"
|
"github.com/woodpecker-ci/woodpecker/pipeline"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server"
|
||||||
forge_types "github.com/woodpecker-ci/woodpecker/server/forge/types"
|
forge_types "github.com/woodpecker-ci/woodpecker/server/forge/types"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
)
|
)
|
||||||
@ -36,6 +37,7 @@ func zeroSteps(currentPipeline *model.Pipeline, forgeYamlConfigs []*forge_types.
|
|||||||
Regs: []*model.Registry{},
|
Regs: []*model.Registry{},
|
||||||
Link: "",
|
Link: "",
|
||||||
Yamls: forgeYamlConfigs,
|
Yamls: forgeYamlConfigs,
|
||||||
|
Forge: server.Config.Services.Forge,
|
||||||
}
|
}
|
||||||
|
|
||||||
pipelineItems, err := b.Build()
|
pipelineItems, err := b.Build()
|
||||||
@ -51,22 +53,20 @@ func zeroSteps(currentPipeline *model.Pipeline, forgeYamlConfigs []*forge_types.
|
|||||||
|
|
||||||
// TODO: parse yaml once and not for each filter function
|
// TODO: parse yaml once and not for each filter function
|
||||||
// Check if at least one pipeline step will be execute otherwise we will just ignore this webhook
|
// Check if at least one pipeline step will be execute otherwise we will just ignore this webhook
|
||||||
func checkIfFiltered(pipeline *model.Pipeline, forgeYamlConfigs []*forge_types.FileMeta) (bool, error) {
|
func checkIfFiltered(repo *model.Repo, p *model.Pipeline, forgeYamlConfigs []*forge_types.FileMeta) (bool, error) {
|
||||||
log.Trace().Msgf("hook.branchFiltered(): pipeline branch: '%s' pipeline event: '%s' config count: %d", pipeline.Branch, pipeline.Event, len(forgeYamlConfigs))
|
log.Trace().Msgf("hook.branchFiltered(): pipeline branch: '%s' pipeline event: '%s' config count: %d", p.Branch, p.Event, len(forgeYamlConfigs))
|
||||||
|
|
||||||
matchMetadata := frontend.Metadata{
|
matchMetadata := frontend.MetadataFromStruct(server.Config.Services.Forge, repo, p, nil, nil, "")
|
||||||
Curr: frontend.Pipeline{
|
|
||||||
Event: string(pipeline.Event),
|
|
||||||
Commit: frontend.Commit{
|
|
||||||
Branch: pipeline.Branch,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, forgeYamlConfig := range forgeYamlConfigs {
|
for _, forgeYamlConfig := range forgeYamlConfigs {
|
||||||
parsedPipelineConfig, err := yaml.ParseBytes(forgeYamlConfig.Data)
|
substitutedConfigData, err := frontend.EnvVarSubst(string(forgeYamlConfig.Data), matchMetadata.Environ())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace().Msgf("parse config '%s': %s", forgeYamlConfig.Name, err)
|
log.Trace().Err(err).Msgf("failed to substitute config '%s'", forgeYamlConfig.Name)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
parsedPipelineConfig, err := yaml.ParseString(substitutedConfigData)
|
||||||
|
if err != nil {
|
||||||
|
log.Trace().Err(err).Msgf("failed to parse config '%s'", forgeYamlConfig.Name)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
log.Trace().Msgf("config '%s': %#v", forgeYamlConfig.Name, parsedPipelineConfig)
|
log.Trace().Msgf("config '%s': %#v", forgeYamlConfig.Name, parsedPipelineConfig)
|
||||||
@ -78,11 +78,6 @@ func checkIfFiltered(pipeline *model.Pipeline, forgeYamlConfigs []*forge_types.F
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore if the pipeline was filtered by the branch (legacy)
|
|
||||||
if !parsedPipelineConfig.Branches.Match(pipeline.Branch) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// at least one config yielded in a valid run.
|
// at least one config yielded in a valid run.
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ func createPipelineItems(c context.Context, store store.Store,
|
|||||||
Envs: envs,
|
Envs: envs,
|
||||||
Link: server.Config.Server.Host,
|
Link: server.Config.Server.Host,
|
||||||
Yamls: yamls,
|
Yamls: yamls,
|
||||||
|
Forge: server.Config.Services.Forge,
|
||||||
}
|
}
|
||||||
pipelineItems, err := b.Build()
|
pipelineItems, err := b.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user