diff --git a/_examples/actions/write_a_readme/kraken.yml b/_examples/actions/write_a_readme/kraken.yml index 0b7f7ae..77db45b 100644 --- a/_examples/actions/write_a_readme/kraken.yml +++ b/_examples/actions/write_a_readme/kraken.yml @@ -3,8 +3,143 @@ name: write-a-readme select: 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 providers: - gitea: git.front.kjuulh.io/kraken organisation: "kraken" actions: - - "go run main.go" + - type: go + entry: "main.go" diff --git a/_examples/actions/write_a_readme/main.go b/_examples/actions/write_a_readme/main.go index a5bd52f..5c24133 100644 --- a/_examples/actions/write_a_readme/main.go +++ b/_examples/actions/write_a_readme/main.go @@ -3,7 +3,10 @@ package main import "github.com/bitfield/script" func main() { - script. + _, err := script. Echo("# Readme"). WriteFile("README.md") + if err != nil { + panic(err) + } } diff --git a/cmd/kraken/commands/process.go b/cmd/kraken/commands/process.go index 6ed29ee..274b299 100644 --- a/cmd/kraken/commands/process.go +++ b/cmd/kraken/commands/process.go @@ -11,7 +11,7 @@ import ( func CreateKrakenProcessCmd() *cobra.Command { cmd := &cobra.Command{ Use: "process", - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { client := http.Client{} var buf bytes.Buffer @@ -22,8 +22,8 @@ func CreateKrakenProcessCmd() *cobra.Command { Path string `json:"path"` }{ Repository: "git@git.front.kjuulh.io:kjuulh/kraken.git", - Branch: "v0.1", - Path: "_examples/actions/write_a_readme/kraken.yml", + Branch: "feature/add-actions", + Path: "_examples/actions/write_a_readme/", }) if err != nil { panic(err) diff --git a/cmd/server/server.go b/cmd/server/server.go index 49b713c..5c835ec 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -15,6 +15,8 @@ func main() { } _ = logger.Sync() + zap.ReplaceGlobals(logger) + Execute(logger) } diff --git a/internal/actions/action.go b/internal/actions/action.go index 3d7e6aa..3c6416e 100644 --- a/internal/actions/action.go +++ b/internal/actions/action.go @@ -2,15 +2,38 @@ package actions import ( "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/services/storage" + "go.uber.org/zap" ) type Action struct { - Schema *schema.KrakenSchema + Schema *schema.KrakenSchema + SchemaPath string } 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 } diff --git a/internal/actions/action_creator.go b/internal/actions/action_creator.go index ebdebd3..b731519 100644 --- a/internal/actions/action_creator.go +++ b/internal/actions/action_creator.go @@ -47,18 +47,13 @@ func (ac *ActionCreator) Prepare(ctx context.Context, ops *ActionCreatorOps) (*A return nil, err } - cloneCtx, _ := context.WithTimeout(ctx, time.Second*5) - repo, err := ac.git.Clone(cloneCtx, area, ops.RepositoryUrl) + cloneCtx, _ := context.WithTimeout(ctx, time.Second*10) + _, err = ac.git.CloneBranch(cloneCtx, area, ops.RepositoryUrl, ops.Branch) if err != nil { ac.logger.Error("could not clone repo", zap.Error(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) if _, err = os.Stat(executorUrl); os.IsNotExist(err) { 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 } + ac.logger.Debug("Action creator done") return &Action{ - Schema: krakenSchema, + Schema: krakenSchema, + SchemaPath: executorUrl, }, nil } diff --git a/internal/actions/builders/go.go b/internal/actions/builders/go.go new file mode 100644 index 0000000..6d828ee --- /dev/null +++ b/internal/actions/builders/go.go @@ -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 +} diff --git a/internal/api/process_command.go b/internal/api/process_command.go index 549a678..2e852b5 100644 --- a/internal/api/process_command.go +++ b/internal/api/process_command.go @@ -34,6 +34,9 @@ func CommandRoute(logger *zap.Logger, app *gin.Engine, deps *serverdeps.ServerDe ctx := context.WithValue(context.Background(), jobs.JobId{}, jobId) processRepos := commands.NewProcessRepos(logger, deps) 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) c.Status(http.StatusAccepted) diff --git a/internal/commands/process_repos.go b/internal/commands/process_repos.go index 6b0bdf0..d29a30b 100644 --- a/internal/commands/process_repos.go +++ b/internal/commands/process_repos.go @@ -48,7 +48,7 @@ func (pr *ProcessRepos) Process(ctx context.Context, repository string, branch s return err } - repositoryUrls := make([]string, 0) + repositoryUrls := action.Schema.Select.Repositories wg := sync.WaitGroup{} 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) if err != nil { - pr.logger.Error("could not process repo", zap.Error(err)) errChan <- err } }(ctx, repoUrl) @@ -113,7 +112,6 @@ func (pr *ProcessRepos) prepareAction( pr.logger.Debug("Creating area") area, err := pr.storage.CreateArea(ctx) if err != nil { - pr.logger.Error("failed to allocate area", zap.Error(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) repo, err := pr.git.Clone(cloneCtx, area, repoUrl) if err != nil { - pr.logger.Error("could not clone repo", zap.Error(err)) return nil, err } err = pr.git.CreateBranch(ctx, repo) if err != nil { - pr.logger.Error("could not create branch", zap.Error(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) } - err = pr.git.Push(ctx, repo) - if err != nil { - return fmt.Errorf("could not push to repo: %w", err) + dryrun := true + if !dryrun { + err = pr.git.Push(ctx, repo) + if err != nil { + return fmt.Errorf("could not push to repo: %w", err) + } } return nil } diff --git a/internal/schema/kraken.go b/internal/schema/kraken.go index 13881bf..fb7d75e 100644 --- a/internal/schema/kraken.go +++ b/internal/schema/kraken.go @@ -12,7 +12,10 @@ type KrakenSchema struct { Organisation string `yaml:"organisation"` } `yaml:"providers"` } `yaml:"select"` - Actions []string `yaml:"actions"` + Actions []struct { + Type string `yaml:"type"` + Entry string `yaml:"entry"` + } `yaml:"actions"` } func Unmarshal(raw string) (*KrakenSchema, error) { diff --git a/internal/services/providers/git.go b/internal/services/providers/git.go index 4a19e57..4a91121 100644 --- a/internal/services/providers/git.go +++ b/internal/services/providers/git.go @@ -53,7 +53,7 @@ func NewGit(logger *zap.Logger, gitConfig *GitConfig, openPGP *signer.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( "cloning repository", zap.String("repoUrl", repoUrl), @@ -69,8 +69,8 @@ func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl stri URL: repoUrl, Auth: auth, RemoteName: "origin", - ReferenceName: "refs/heads/main", - SingleBranch: false, + ReferenceName: plumbing.NewBranchReferenceName(branch), + SingleBranch: true, NoCheckout: false, Depth: 1, RecurseSubmodules: 1, @@ -90,19 +90,41 @@ func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl stri return &GitRepo{repo: repo}, nil } -func (g *Git) Checkout(ctx context.Context, gitRepo *GitRepo, branch string) error { - wt, err := gitRepo.repo.Worktree() +func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl string) (*GitRepo, error) { + g.logger.Debug( + "cloning repository", + zap.String("repoUrl", repoUrl), + zap.String("path", storageArea.Path), + ) + + auth, err := g.GetAuth() if err != nil { - return err + return nil, err } - return wt.Checkout(&git.CheckoutOptions{ - Hash: [20]byte{}, - Branch: plumbing.NewBranchReferenceName(branch), - Create: false, - Force: false, - Keep: false, - }) + cloneOptions := git.CloneOptions{ + URL: repoUrl, + Auth: auth, + RemoteName: "origin", + ReferenceName: "refs/heads/main", + 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 { diff --git a/roadmap.md b/roadmap.md index ba55870..dbb3f3e 100644 --- a/roadmap.md +++ b/roadmap.md @@ -11,15 +11,15 @@ ### Not in scope -- [ ] Pooled runners -- [ ] CLI with options -- [ ] Server app -- [ ] Git hosting providers +- Pooled runners +- CLI with options +- Server app +- Git hosting providers ## Version 0.1 -- [ ] Setup a way to choose actions and predicates -- [ ] Allow instantiation of actions, kraken template repo etc. +- [x] Setup a way to choose actions and predicates +- [x] Allow instantiation of actions, kraken template repo etc. - [ ] Create pr for gitea provider - [ ] Think about some sort of isolation - [ ] Create CLI to trigger action