Skip to content

Commit

Permalink
Add ability to trigger manual builds (#1156)
Browse files Browse the repository at this point in the history
closes #83 
closes #240 

Co-authored-by: Anbraten <anton@ju60.de>
Co-authored-by: qwerty287 <80460567+qwerty287@users.noreply.github.com>
Co-authored-by: 6543 <6543@obermui.de>
  • Loading branch information
4 people authored Sep 27, 2022
1 parent 86bc751 commit b4d89a1
Show file tree
Hide file tree
Showing 33 changed files with 1,117 additions and 827 deletions.
1 change: 1 addition & 0 deletions cli/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ var Command = &cli.Command{
buildQueueCmd,
buildKillCmd,
buildPsCmd,
buildCreateCmd,
},
}
78 changes: 78 additions & 0 deletions cli/build/build_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package build

import (
"os"
"strings"
"text/template"

"github.com/woodpecker-ci/woodpecker/woodpecker-go/woodpecker"

"github.com/urfave/cli/v2"

"github.com/woodpecker-ci/woodpecker/cli/common"
"github.com/woodpecker-ci/woodpecker/cli/internal"
)

var buildCreateCmd = &cli.Command{
Name: "create",
Usage: "create new build",
ArgsUsage: "<repo/name>",
Action: buildCreate,
Flags: append(common.GlobalFlags,
common.FormatFlag(tmplBuildList),
&cli.StringFlag{
Name: "branch",
Usage: "branch to create build from",
Required: true,
},
&cli.StringSliceFlag{
Name: "var",
Usage: "key=value",
},
),
}

func buildCreate(c *cli.Context) error {
repo := c.Args().First()

owner, name, err := internal.ParseRepo(repo)
if err != nil {
return err
}

client, err := internal.NewClient(c)
if err != nil {
return err
}

branch := c.String("branch")
variables := make(map[string]string)

for _, vaz := range c.StringSlice("var") {
sp := strings.SplitN(vaz, "=", 2)
if len(sp) == 2 {
variables[sp[0]] = sp[1]
}
}

options := &woodpecker.BuildOptions{
Branch: branch,
Variables: variables,
}

build, err := client.BuildCreate(owner, name, options)
if err != nil {
return err
}

tmpl, err := template.New("_").Parse(c.String("format") + "\n")
if err != nil {
return err
}

if err := tmpl.Execute(os.Stdout, build); err != nil {
return err
}

return nil
}
4 changes: 2 additions & 2 deletions docs/docs/20-usage/20-pipeline-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,10 +273,10 @@ when:
:::info
**By default steps are filtered by following event types:**

`push`, `pull_request`, `tag`, `deployment`.
`push`, `pull_request`, `tag`, `deployment`, `manual`.
:::

Available events: `push`, `pull_request`, `tag`, `deployment`, `cron`
Available events: `push`, `pull_request`, `tag`, `deployment`, `cron`, `manual`

Execute a step if the build event is a `tag`:

Expand Down
24 changes: 24 additions & 0 deletions docs/docs/40-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,30 @@ State: {{ .State }}

**--token, -t**="": server auth token

### create

create new build

**--branch**="": branch to create build from

**--format**="": format output (default: Build #{{ .Number }} 
Status: {{ .Status }}
Event: {{ .Event }}
Commit: {{ .Commit }}
Branch: {{ .Branch }}
Ref: {{ .Ref }}
Author: {{ .Author }} {{ if .Email }}<{{.Email}}>{{ end }}
Message: {{ .Message }}
)

**--log-level**="": set logging level (default: info)

**--server, -s**="": server address

**--token, -t**="": server auth token

**--var**="": key=value

## log

manage logs
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/woodpecker-ci/woodpecker
go 1.18

require (
code.gitea.io/sdk/gitea v0.15.1-0.20220720025709-de34275bb64e
code.gitea.io/sdk/gitea v0.15.1-0.20220831004139-a0127ed0e7fe
codeberg.org/6543/go-yaml2json v0.2.1
github.com/bmatcuk/doublestar/v4 v4.2.0
github.com/caddyserver/certmagic v0.17.1-0.20220901172127-2e22c6fa8c47
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
code.gitea.io/sdk/gitea v0.15.1-0.20220720025709-de34275bb64e h1:xayGBU2DwsrA5ZyqKNpXB91w3BfnkNcLDWZ7Ynn/w+g=
code.gitea.io/sdk/gitea v0.15.1-0.20220720025709-de34275bb64e/go.mod h1:aRmrQC3CAHdJAU1LQt0C9zqzqI8tUB/5oQtNE746aYE=
code.gitea.io/sdk/gitea v0.15.1-0.20220831004139-a0127ed0e7fe h1:PeLyxnUZE85QuJtBZ4P8qCQcgWG5Ked67mlNgr0WkCQ=
code.gitea.io/sdk/gitea v0.15.1-0.20220831004139-a0127ed0e7fe/go.mod h1:aRmrQC3CAHdJAU1LQt0C9zqzqI8tUB/5oQtNE746aYE=
codeberg.org/6543/go-yaml2json v0.2.1 h1:S0dxlzRRpYnSLODxpbqaUfmJYZZg0Wcpf8bI9YzyOXo=
codeberg.org/6543/go-yaml2json v0.2.1/go.mod h1:mz61q14LWF4ZABrgMEDMmk3t9dPi6zgR1uBh2VKV2RQ=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
Expand Down
1 change: 1 addition & 0 deletions pipeline/frontend/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
EventTag = "tag"
EventDeploy = "deployment"
EventCron = "cron"
EventManual = "manual"
)

type (
Expand Down
1 change: 1 addition & 0 deletions pipeline/frontend/yaml/constraint/constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ func (c *Constraint) SetDefaultEventFilter() {
frontend.EventPull,
frontend.EventTag,
frontend.EventDeploy,
frontend.EventManual,
}
}
}
Expand Down
50 changes: 50 additions & 0 deletions server/api/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ package api

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"

"github.com/woodpecker-ci/woodpecker/server"

"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"

Expand All @@ -34,6 +37,53 @@ import (
"github.com/woodpecker-ci/woodpecker/server/store"
)

func CreateBuild(c *gin.Context) {
_store := store.FromContext(c)
repo := session.Repo(c)

var p model.BuildOptions

err := json.NewDecoder(c.Request.Body).Decode(&p)
if err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)
return
}

user := session.User(c)

lastCommit, _ := server.Config.Services.Remote.BranchHead(c, user, repo, p.Branch)

tmpBuild := createTmpBuild(model.EventManual, lastCommit, repo, user, &p)

build, err := pipeline.Create(c, _store, repo, tmpBuild)
if err != nil {
handlePipelineErr(c, err)
} else {
c.JSON(http.StatusOK, build)
}
}

func createTmpBuild(event model.WebhookEvent, commitSHA string, repo *model.Repo, user *model.User, opts *model.BuildOptions) *model.Build {
return &model.Build{
Event: event,
Commit: commitSHA,
Branch: opts.Branch,
Timestamp: time.Now().UTC().Unix(),

Avatar: user.Avatar,
Message: "MANUAL BUILD @ " + opts.Branch,

Ref: opts.Branch,
AdditionalVariables: opts.Variables,

Author: user.Login,
Email: user.Email,

// TODO: Generate proper link to commit
Link: repo.Link,
}
}

func GetBuilds(c *gin.Context) {
repo := session.Repo(c)
page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
Expand Down
74 changes: 40 additions & 34 deletions server/model/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,41 @@ package model

// swagger:model build
type Build struct {
ID int64 `json:"id" xorm:"pk autoincr 'build_id'"`
RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'build_repo_id'"`
Number int64 `json:"number" xorm:"UNIQUE(s) 'build_number'"`
Author string `json:"author" xorm:"INDEX 'build_author'"`
ConfigID int64 `json:"-" xorm:"build_config_id"`
Parent int64 `json:"parent" xorm:"build_parent"`
Event WebhookEvent `json:"event" xorm:"build_event"`
Status StatusValue `json:"status" xorm:"INDEX 'build_status'"`
Error string `json:"error" xorm:"build_error"`
Enqueued int64 `json:"enqueued_at" xorm:"build_enqueued"`
Created int64 `json:"created_at" xorm:"build_created"`
Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"`
Started int64 `json:"started_at" xorm:"build_started"`
Finished int64 `json:"finished_at" xorm:"build_finished"`
Deploy string `json:"deploy_to" xorm:"build_deploy"`
Commit string `json:"commit" xorm:"build_commit"`
Branch string `json:"branch" xorm:"build_branch"`
Ref string `json:"ref" xorm:"build_ref"`
Refspec string `json:"refspec" xorm:"build_refspec"`
Remote string `json:"remote" xorm:"build_remote"`
Title string `json:"title" xorm:"build_title"`
Message string `json:"message" xorm:"build_message"`
Timestamp int64 `json:"timestamp" xorm:"build_timestamp"`
Sender string `json:"sender" xorm:"build_sender"` // uses reported user for webhooks and name of cron for cron pipelines
Avatar string `json:"author_avatar" xorm:"build_avatar"`
Email string `json:"author_email" xorm:"build_email"`
Link string `json:"link_url" xorm:"build_link"`
Signed bool `json:"signed" xorm:"build_signed"` // deprecate
Verified bool `json:"verified" xorm:"build_verified"` // deprecate
Reviewer string `json:"reviewed_by" xorm:"build_reviewer"`
Reviewed int64 `json:"reviewed_at" xorm:"build_reviewed"`
Procs []*Proc `json:"procs,omitempty" xorm:"-"`
Files []*File `json:"files,omitempty" xorm:"-"`
ChangedFiles []string `json:"changed_files,omitempty" xorm:"json 'changed_files'"`
ID int64 `json:"id" xorm:"pk autoincr 'build_id'"`
RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'build_repo_id'"`
Number int64 `json:"number" xorm:"UNIQUE(s) 'build_number'"`
Author string `json:"author" xorm:"INDEX 'build_author'"`
ConfigID int64 `json:"-" xorm:"build_config_id"`
Parent int64 `json:"parent" xorm:"build_parent"`
Event WebhookEvent `json:"event" xorm:"build_event"`
Status StatusValue `json:"status" xorm:"INDEX 'build_status'"`
Error string `json:"error" xorm:"build_error"`
Enqueued int64 `json:"enqueued_at" xorm:"build_enqueued"`
Created int64 `json:"created_at" xorm:"build_created"`
Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"`
Started int64 `json:"started_at" xorm:"build_started"`
Finished int64 `json:"finished_at" xorm:"build_finished"`
Deploy string `json:"deploy_to" xorm:"build_deploy"`
Commit string `json:"commit" xorm:"build_commit"`
Branch string `json:"branch" xorm:"build_branch"`
Ref string `json:"ref" xorm:"build_ref"`
Refspec string `json:"refspec" xorm:"build_refspec"`
Remote string `json:"remote" xorm:"build_remote"`
Title string `json:"title" xorm:"build_title"`
Message string `json:"message" xorm:"build_message"`
Timestamp int64 `json:"timestamp" xorm:"build_timestamp"`
Sender string `json:"sender" xorm:"build_sender"` // uses reported user for webhooks and name of cron for cron pipelines
Avatar string `json:"author_avatar" xorm:"build_avatar"`
Email string `json:"author_email" xorm:"build_email"`
Link string `json:"link_url" xorm:"build_link"`
Signed bool `json:"signed" xorm:"build_signed"` // deprecate
Verified bool `json:"verified" xorm:"build_verified"` // deprecate
Reviewer string `json:"reviewed_by" xorm:"build_reviewer"`
Reviewed int64 `json:"reviewed_at" xorm:"build_reviewed"`
Procs []*Proc `json:"procs,omitempty" xorm:"-"`
Files []*File `json:"files,omitempty" xorm:"-"`
ChangedFiles []string `json:"changed_files,omitempty" xorm:"json 'changed_files'"`
AdditionalVariables map[string]string `json:"variables,omitempty" xorm:"json 'additional_variables'"`
}

// TableName return database table name for xorm
Expand All @@ -61,3 +62,8 @@ func (Build) TableName() string {
type UpdateBuildStore interface {
UpdateBuild(*Build) error
}

type BuildOptions struct {
Branch string `json:"branch"`
Variables map[string]string `json:"variables"`
}
1 change: 1 addition & 0 deletions server/model/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
EventTag WebhookEvent = "tag"
EventDeploy WebhookEvent = "deployment"
EventCron WebhookEvent = "cron"
EventManual WebhookEvent = "manual"
)

func ValidateWebhookEvent(s WebhookEvent) bool {
Expand Down
4 changes: 4 additions & 0 deletions server/pipeline/items.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ func createBuildItems(ctx context.Context, store store.Store, build *model.Build
}
}

for k, v := range build.AdditionalVariables {
envs[k] = v
}

b := shared.ProcBuilder{
Repo: repo,
Curr: build,
Expand Down
1 change: 1 addition & 0 deletions server/router/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func apiRoutes(e *gin.Engine) {
repo.GET("/branches", api.GetRepoBranches)

repo.GET("/builds", api.GetBuilds)
repo.POST("/builds", session.MustPush, api.CreateBuild)
repo.GET("/builds/:number", api.GetBuild)
repo.GET("/builds/:number/config", api.GetBuildConfig)

Expand Down
4 changes: 4 additions & 0 deletions web/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ declare module '@vue/runtime-core' {
IIcRoundLightMode: typeof import('~icons/ic/round-light-mode')['default']
IIcSharpTimelapse: typeof import('~icons/ic/sharp-timelapse')['default']
IIcTwotoneAdd: typeof import('~icons/ic/twotone-add')['default']
ILaTimes: typeof import('~icons/la/times')['default']
IMdiBitbucket: typeof import('~icons/mdi/bitbucket')['default']
IMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
IMdiClockTimeEightOutline: typeof import('~icons/mdi/clock-time-eight-outline')['default']
IMdiFormatListBulleted: typeof import('~icons/mdi/format-list-bulleted')['default']
IMdiGestureTap: typeof import('~icons/mdi/gesture-tap')['default']
IMdiGithub: typeof import('~icons/mdi/github')['default']
IMdiLoading: typeof import('~icons/mdi/loading')['default']
IMdiSourceBranch: typeof import('~icons/mdi/source-branch')['default']
Expand All @@ -70,10 +72,12 @@ declare module '@vue/runtime-core' {
ITeenyiconsGitSolid: typeof import('~icons/teenyicons/git-solid')['default']
IVaadinQuestionCircleO: typeof import('~icons/vaadin/question-circle-o')['default']
ListItem: typeof import('./src/components/atomic/ListItem.vue')['default']
ManualPipelinePopup: typeof import('./src/components/layout/popups/ManualPipelinePopup.vue')['default']
Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default']
NumberField: typeof import('./src/components/form/NumberField.vue')['default']
OrgSecretsTab: typeof import('./src/components/org/settings/OrgSecretsTab.vue')['default']
Panel: typeof import('./src/components/layout/Panel.vue')['default']
Popup: typeof import('./src/components/layout/Popup.vue')['default']
RadioField: typeof import('./src/components/form/RadioField.vue')['default']
RegistriesTab: typeof import('./src/components/repo/settings/RegistriesTab.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
Expand Down
Loading

0 comments on commit b4d89a1

Please sign in to comment.