feature/add-actions (#9)

Co-authored-by: kjuulh <contact@kjuulh.io>
Reviewed-on: kjuulh/kraken#9
This commit is contained in:
Kasper Juul Hermansen 2022-09-14 23:40:58 +02:00
parent ce55f6523c
commit 0cb923a3a5
12 changed files with 275 additions and 42 deletions

View File

@ -3,8 +3,143 @@ name: write-a-readme
select: select:
repositories: repositories:
- git@git.front.kjuulh.io:kjuulh/kraken-test.git - git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
- git@git.front.kjuulh.io:kjuulh/kraken-test.git
providers: providers:
- gitea: git.front.kjuulh.io/kraken - gitea: git.front.kjuulh.io/kraken
organisation: "kraken" organisation: "kraken"
actions: actions:
- "go run main.go" - type: go
entry: "main.go"

View File

@ -3,7 +3,10 @@ package main
import "github.com/bitfield/script" import "github.com/bitfield/script"
func main() { func main() {
script. _, err := script.
Echo("# Readme"). Echo("# Readme").
WriteFile("README.md") WriteFile("README.md")
if err != nil {
panic(err)
}
} }

View File

@ -11,7 +11,7 @@ import (
func CreateKrakenProcessCmd() *cobra.Command { func CreateKrakenProcessCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "process", Use: "process",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, _ []string) {
client := http.Client{} client := http.Client{}
var buf bytes.Buffer var buf bytes.Buffer
@ -22,8 +22,8 @@ func CreateKrakenProcessCmd() *cobra.Command {
Path string `json:"path"` Path string `json:"path"`
}{ }{
Repository: "git@git.front.kjuulh.io:kjuulh/kraken.git", Repository: "git@git.front.kjuulh.io:kjuulh/kraken.git",
Branch: "v0.1", Branch: "feature/add-actions",
Path: "_examples/actions/write_a_readme/kraken.yml", Path: "_examples/actions/write_a_readme/",
}) })
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -15,6 +15,8 @@ func main() {
} }
_ = logger.Sync() _ = logger.Sync()
zap.ReplaceGlobals(logger)
Execute(logger) Execute(logger)
} }

View File

@ -2,15 +2,38 @@ package actions
import ( import (
"context" "context"
"errors"
"git.front.kjuulh.io/kjuulh/kraken/internal/actions/builders"
"git.front.kjuulh.io/kjuulh/kraken/internal/schema" "git.front.kjuulh.io/kjuulh/kraken/internal/schema"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/storage" "git.front.kjuulh.io/kjuulh/kraken/internal/services/storage"
"go.uber.org/zap"
) )
type Action struct { type Action struct {
Schema *schema.KrakenSchema Schema *schema.KrakenSchema
SchemaPath string
} }
func (a *Action) Execute(ctx context.Context, area *storage.Area) error { func (a *Action) Execute(ctx context.Context, area *storage.Area) error {
for _, action := range a.Schema.Actions {
switch action.Type {
case "go":
exe, err := builders.NewGo(zap.L()).Build(ctx, a.SchemaPath, action.Entry)
if err != nil {
return err
}
err = exe(ctx, area.Path)
if err != nil {
return err
}
zap.L().Debug("Execution done")
default:
return errors.New("could not determine action type")
}
}
return nil return nil
} }

View File

@ -47,18 +47,13 @@ func (ac *ActionCreator) Prepare(ctx context.Context, ops *ActionCreatorOps) (*A
return nil, err return nil, err
} }
cloneCtx, _ := context.WithTimeout(ctx, time.Second*5) cloneCtx, _ := context.WithTimeout(ctx, time.Second*10)
repo, err := ac.git.Clone(cloneCtx, area, ops.RepositoryUrl) _, err = ac.git.CloneBranch(cloneCtx, area, ops.RepositoryUrl, ops.Branch)
if err != nil { if err != nil {
ac.logger.Error("could not clone repo", zap.Error(err)) ac.logger.Error("could not clone repo", zap.Error(err))
return nil, err return nil, err
} }
err = ac.git.Checkout(ctx, repo, ops.Branch)
if err != nil {
return nil, err
}
executorUrl := path.Join(area.Path, ops.Path) executorUrl := path.Join(area.Path, ops.Path)
if _, err = os.Stat(executorUrl); os.IsNotExist(err) { if _, err = os.Stat(executorUrl); os.IsNotExist(err) {
return nil, fmt.Errorf("path is invalid: %s", ops.Path) return nil, fmt.Errorf("path is invalid: %s", ops.Path)
@ -74,8 +69,10 @@ func (ac *ActionCreator) Prepare(ctx context.Context, ops *ActionCreatorOps) (*A
return nil, err return nil, err
} }
ac.logger.Debug("Action creator done")
return &Action{ return &Action{
Schema: krakenSchema, Schema: krakenSchema,
SchemaPath: executorUrl,
}, nil }, nil
} }

View File

@ -0,0 +1,46 @@
package builders
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"go.uber.org/zap"
)
type Go struct {
logger *zap.Logger
}
func NewGo(logger *zap.Logger) *Go {
return &Go{logger: logger}
}
type GoExecutable func(ctx context.Context, victimPath string) error
func (g *Go) Build(ctx context.Context, modulePath, entryPath string) (GoExecutable, error) {
g.logger.Debug("Building go binary", zap.String("actiondir", modulePath), zap.String("entry", entryPath))
if _, err := os.Stat(fmt.Sprintf("%s/%s", modulePath, entryPath)); os.IsNotExist(err) {
return nil, errors.New("could not find entry")
}
err := exec.CommandContext(
ctx,
"/bin/bash",
"-c",
fmt.Sprintf("(cd %s; go build -o main %s)", modulePath, entryPath),
).Run()
if err != nil {
return nil, err
}
g.logger.Debug("Go binary built!")
return func(ctx context.Context, victimPath string) error {
g.logger.Debug("Executing script", zap.String("victim", victimPath))
return exec.CommandContext(ctx, "/bin/bash", "-c", fmt.Sprintf("(cd %s; %s/main)", victimPath, modulePath)).Run()
}, nil
}

View File

@ -34,6 +34,9 @@ func CommandRoute(logger *zap.Logger, app *gin.Engine, deps *serverdeps.ServerDe
ctx := context.WithValue(context.Background(), jobs.JobId{}, jobId) ctx := context.WithValue(context.Background(), jobs.JobId{}, jobId)
processRepos := commands.NewProcessRepos(logger, deps) processRepos := commands.NewProcessRepos(logger, deps)
err = processRepos.Process(ctx, repository, branch, path) err = processRepos.Process(ctx, repository, branch, path)
if err != nil {
logger.Error("could not process repo", zap.Error(err))
}
}(request.Repository, request.Branch, request.Path, jobId) }(request.Repository, request.Branch, request.Path, jobId)
c.Status(http.StatusAccepted) c.Status(http.StatusAccepted)

View File

@ -48,7 +48,7 @@ func (pr *ProcessRepos) Process(ctx context.Context, repository string, branch s
return err return err
} }
repositoryUrls := make([]string, 0) repositoryUrls := action.Schema.Select.Repositories
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(len(repositoryUrls)) wg.Add(len(repositoryUrls))
@ -60,7 +60,6 @@ func (pr *ProcessRepos) Process(ctx context.Context, repository string, branch s
}() }()
err := pr.processRepo(ctx, repoUrl, action) err := pr.processRepo(ctx, repoUrl, action)
if err != nil { if err != nil {
pr.logger.Error("could not process repo", zap.Error(err))
errChan <- err errChan <- err
} }
}(ctx, repoUrl) }(ctx, repoUrl)
@ -113,7 +112,6 @@ func (pr *ProcessRepos) prepareAction(
pr.logger.Debug("Creating area") pr.logger.Debug("Creating area")
area, err := pr.storage.CreateArea(ctx) area, err := pr.storage.CreateArea(ctx)
if err != nil { if err != nil {
pr.logger.Error("failed to allocate area", zap.Error(err))
return nil, nil, err return nil, nil, err
} }
@ -133,13 +131,11 @@ func (pr *ProcessRepos) clone(ctx context.Context, area *storage.Area, repoUrl s
cloneCtx, _ := context.WithTimeout(ctx, time.Second*5) cloneCtx, _ := context.WithTimeout(ctx, time.Second*5)
repo, err := pr.git.Clone(cloneCtx, area, repoUrl) repo, err := pr.git.Clone(cloneCtx, area, repoUrl)
if err != nil { if err != nil {
pr.logger.Error("could not clone repo", zap.Error(err))
return nil, err return nil, err
} }
err = pr.git.CreateBranch(ctx, repo) err = pr.git.CreateBranch(ctx, repo)
if err != nil { if err != nil {
pr.logger.Error("could not create branch", zap.Error(err))
return nil, err return nil, err
} }
@ -157,9 +153,12 @@ func (pr *ProcessRepos) commit(ctx context.Context, area *storage.Area, repo *pr
return fmt.Errorf("could not get diff: %w", err) return fmt.Errorf("could not get diff: %w", err)
} }
err = pr.git.Push(ctx, repo) dryrun := true
if err != nil { if !dryrun {
return fmt.Errorf("could not push to repo: %w", err) err = pr.git.Push(ctx, repo)
if err != nil {
return fmt.Errorf("could not push to repo: %w", err)
}
} }
return nil return nil
} }

View File

@ -12,7 +12,10 @@ type KrakenSchema struct {
Organisation string `yaml:"organisation"` Organisation string `yaml:"organisation"`
} `yaml:"providers"` } `yaml:"providers"`
} `yaml:"select"` } `yaml:"select"`
Actions []string `yaml:"actions"` Actions []struct {
Type string `yaml:"type"`
Entry string `yaml:"entry"`
} `yaml:"actions"`
} }
func Unmarshal(raw string) (*KrakenSchema, error) { func Unmarshal(raw string) (*KrakenSchema, error) {

View File

@ -53,7 +53,7 @@ func NewGit(logger *zap.Logger, gitConfig *GitConfig, openPGP *signer.OpenPGP) *
return &Git{logger: logger, gitConfig: gitConfig, openPGP: openPGP} return &Git{logger: logger, gitConfig: gitConfig, openPGP: openPGP}
} }
func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl string) (*GitRepo, error) { func (g *Git) CloneBranch(ctx context.Context, storageArea *storage.Area, repoUrl string, branch string) (*GitRepo, error) {
g.logger.Debug( g.logger.Debug(
"cloning repository", "cloning repository",
zap.String("repoUrl", repoUrl), zap.String("repoUrl", repoUrl),
@ -69,8 +69,8 @@ func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl stri
URL: repoUrl, URL: repoUrl,
Auth: auth, Auth: auth,
RemoteName: "origin", RemoteName: "origin",
ReferenceName: "refs/heads/main", ReferenceName: plumbing.NewBranchReferenceName(branch),
SingleBranch: false, SingleBranch: true,
NoCheckout: false, NoCheckout: false,
Depth: 1, Depth: 1,
RecurseSubmodules: 1, RecurseSubmodules: 1,
@ -90,19 +90,41 @@ func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl stri
return &GitRepo{repo: repo}, nil return &GitRepo{repo: repo}, nil
} }
func (g *Git) Checkout(ctx context.Context, gitRepo *GitRepo, branch string) error { func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl string) (*GitRepo, error) {
wt, err := gitRepo.repo.Worktree() g.logger.Debug(
"cloning repository",
zap.String("repoUrl", repoUrl),
zap.String("path", storageArea.Path),
)
auth, err := g.GetAuth()
if err != nil { if err != nil {
return err return nil, err
} }
return wt.Checkout(&git.CheckoutOptions{ cloneOptions := git.CloneOptions{
Hash: [20]byte{}, URL: repoUrl,
Branch: plumbing.NewBranchReferenceName(branch), Auth: auth,
Create: false, RemoteName: "origin",
Force: false, ReferenceName: "refs/heads/main",
Keep: false, SingleBranch: true,
}) NoCheckout: false,
Depth: 1,
RecurseSubmodules: 1,
Progress: g.getProgressWriter(),
Tags: 0,
InsecureSkipTLS: false,
CABundle: []byte{},
}
repo, err := git.PlainCloneContext(ctx, storageArea.Path, false, &cloneOptions)
if err != nil {
return nil, err
}
g.logger.Debug("done cloning repo")
return &GitRepo{repo: repo}, nil
} }
func (g *Git) getProgressWriter() *zapio.Writer { func (g *Git) getProgressWriter() *zapio.Writer {

View File

@ -11,15 +11,15 @@
### Not in scope ### Not in scope
- [ ] Pooled runners - Pooled runners
- [ ] CLI with options - CLI with options
- [ ] Server app - Server app
- [ ] Git hosting providers - Git hosting providers
## Version 0.1 ## Version 0.1
- [ ] Setup a way to choose actions and predicates - [x] Setup a way to choose actions and predicates
- [ ] Allow instantiation of actions, kraken template repo etc. - [x] Allow instantiation of actions, kraken template repo etc.
- [ ] Create pr for gitea provider - [ ] Create pr for gitea provider
- [ ] Think about some sort of isolation - [ ] Think about some sort of isolation
- [ ] Create CLI to trigger action - [ ] Create CLI to trigger action