From ce55f6523cfbaa940d45b6380f41770e87958108 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Tue, 13 Sep 2022 22:54:49 +0200 Subject: [PATCH] with action creator --- _examples/actions/write_a_readme/kraken.yml | 6 +- cmd/kraken/commands/process.go | 9 +- internal/actions/action.go | 16 +++ internal/actions/action_creator.go | 88 ++++++++++++++++ internal/api/process_command.go | 10 +- internal/commands/process_repos.go | 109 ++++++++------------ internal/schema/kraken.go | 25 +++++ internal/serverdeps/server_deps.go | 5 + internal/services/providers/git.go | 17 ++- 9 files changed, 208 insertions(+), 77 deletions(-) create mode 100644 internal/actions/action.go create mode 100644 internal/actions/action_creator.go create mode 100644 internal/schema/kraken.go diff --git a/_examples/actions/write_a_readme/kraken.yml b/_examples/actions/write_a_readme/kraken.yml index 739f293..0b7f7ae 100644 --- a/_examples/actions/write_a_readme/kraken.yml +++ b/_examples/actions/write_a_readme/kraken.yml @@ -3,8 +3,8 @@ name: write-a-readme select: repositories: - git@git.front.kjuulh.io:kjuulh/kraken-test.git - provider: + providers: - gitea: git.front.kjuulh.io/kraken organisation: "kraken" -action: - command: go run main.go +actions: + - "go run main.go" diff --git a/cmd/kraken/commands/process.go b/cmd/kraken/commands/process.go index 1097089..6ed29ee 100644 --- a/cmd/kraken/commands/process.go +++ b/cmd/kraken/commands/process.go @@ -17,9 +17,14 @@ func CreateKrakenProcessCmd() *cobra.Command { var buf bytes.Buffer err := json.NewEncoder(&buf). Encode(struct { - RepositoryUrls []string `json:"repositoryUrls"` + Repository string `json:"repository"` + Branch string `json:"branch"` + Path string `json:"path"` }{ - RepositoryUrls: []string{"git@git.front.kjuulh.io:kjuulh/kraken.git"}}) + Repository: "git@git.front.kjuulh.io:kjuulh/kraken.git", + Branch: "v0.1", + Path: "_examples/actions/write_a_readme/kraken.yml", + }) if err != nil { panic(err) } diff --git a/internal/actions/action.go b/internal/actions/action.go new file mode 100644 index 0000000..3d7e6aa --- /dev/null +++ b/internal/actions/action.go @@ -0,0 +1,16 @@ +package actions + +import ( + "context" + + "git.front.kjuulh.io/kjuulh/kraken/internal/schema" + "git.front.kjuulh.io/kjuulh/kraken/internal/services/storage" +) + +type Action struct { + Schema *schema.KrakenSchema +} + +func (a *Action) Execute(ctx context.Context, area *storage.Area) error { + return nil +} diff --git a/internal/actions/action_creator.go b/internal/actions/action_creator.go new file mode 100644 index 0000000..ebdebd3 --- /dev/null +++ b/internal/actions/action_creator.go @@ -0,0 +1,88 @@ +package actions + +import ( + "context" + "fmt" + "os" + "path" + "time" + + "git.front.kjuulh.io/kjuulh/kraken/internal/schema" + "git.front.kjuulh.io/kjuulh/kraken/internal/services/providers" + "git.front.kjuulh.io/kjuulh/kraken/internal/services/storage" + "go.uber.org/zap" +) + +type ( + ActionCreatorOps struct { + RepositoryUrl string + Branch string + Path string + } + + ActionCreator struct { + logger *zap.Logger + storage *storage.Service + git *providers.Git + } + + ActionCreatorDeps interface { + GetStorageService() *storage.Service + GetGitProvider() *providers.Git + } +) + +func NewActionCreator(logger *zap.Logger, deps ActionCreatorDeps) *ActionCreator { + return &ActionCreator{ + logger: logger, + storage: deps.GetStorageService(), + git: deps.GetGitProvider(), + } +} + +func (ac *ActionCreator) Prepare(ctx context.Context, ops *ActionCreatorOps) (*Action, error) { + area, err := ac.storage.CreateArea(ctx) + if err != nil { + ac.logger.Error("failed to allocate area", zap.Error(err)) + return nil, err + } + + cloneCtx, _ := context.WithTimeout(ctx, time.Second*5) + repo, err := ac.git.Clone(cloneCtx, area, ops.RepositoryUrl) + 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) + } + + contents, err := os.ReadFile(path.Join(executorUrl, "kraken.yml")) + if err != nil { + return nil, err + } + + krakenSchema, err := schema.Unmarshal(string(contents)) + if err != nil { + return nil, err + } + + return &Action{ + Schema: krakenSchema, + }, nil +} + +func (ac *ActionCreator) Cleanup(ctx context.Context, area *storage.Area) { + ac.logger.Debug("Removing area", zap.String("path", area.Path)) + err := ac.storage.RemoveArea(ctx, area) + if err != nil { + panic(err) + } +} diff --git a/internal/api/process_command.go b/internal/api/process_command.go index 589dd81..549a678 100644 --- a/internal/api/process_command.go +++ b/internal/api/process_command.go @@ -16,7 +16,9 @@ func CommandRoute(logger *zap.Logger, app *gin.Engine, deps *serverdeps.ServerDe commandRoute := app.Group("commands") commandRoute.POST("processRepos", func(c *gin.Context) { type processReposRequest struct { - RepositoryUrls []string `json:"repositoryUrls"` + Repository string `json:"repository"` + Branch string `json:"branch"` + Path string `json:"path"` } var request processReposRequest err := c.BindJSON(&request) @@ -28,11 +30,11 @@ func CommandRoute(logger *zap.Logger, app *gin.Engine, deps *serverdeps.ServerDe jobId := uuid.New().String() - go func(repositoryUrls []string, jobId string) { + go func(repository string, branch string, path string, jobId string) { ctx := context.WithValue(context.Background(), jobs.JobId{}, jobId) processRepos := commands.NewProcessRepos(logger, deps) - err = processRepos.Process(ctx, repositoryUrls) - }(request.RepositoryUrls, jobId) + err = processRepos.Process(ctx, repository, branch, path) + }(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 45a580a..6b0bdf0 100644 --- a/internal/commands/process_repos.go +++ b/internal/commands/process_repos.go @@ -3,14 +3,10 @@ package commands import ( "context" "fmt" - "io/fs" - "os" - "path" - "path/filepath" "sync" "time" - "git.front.kjuulh.io/kjuulh/kraken/internal/services/actions" + "git.front.kjuulh.io/kjuulh/kraken/internal/actions" "git.front.kjuulh.io/kjuulh/kraken/internal/services/providers" "git.front.kjuulh.io/kjuulh/kraken/internal/services/storage" "go.uber.org/zap" @@ -18,31 +14,42 @@ import ( type ( ProcessRepos struct { - logger *zap.Logger - storage *storage.Service - git *providers.Git - action *actions.Action + logger *zap.Logger + storage *storage.Service + git *providers.Git + actionCreator *actions.ActionCreator } ProcessReposDeps interface { GetStorageService() *storage.Service GetGitProvider() *providers.Git - GetAction() *actions.Action + GetActionCreator() *actions.ActionCreator } ) func NewProcessRepos(logger *zap.Logger, deps ProcessReposDeps) *ProcessRepos { return &ProcessRepos{ - logger: logger, - storage: deps.GetStorageService(), - git: deps.GetGitProvider(), - action: deps.GetAction(), + logger: logger, + storage: deps.GetStorageService(), + git: deps.GetGitProvider(), + actionCreator: deps.GetActionCreator(), } } -func (pr *ProcessRepos) Process(ctx context.Context, repositoryUrls []string) error { +func (pr *ProcessRepos) Process(ctx context.Context, repository string, branch string, actionPath string) error { errChan := make(chan error, 1) + action, err := pr.actionCreator.Prepare(ctx, &actions.ActionCreatorOps{ + RepositoryUrl: repository, + Branch: branch, + Path: actionPath, + }) + if err != nil { + return err + } + + repositoryUrls := make([]string, 0) + wg := sync.WaitGroup{} wg.Add(len(repositoryUrls)) @@ -51,7 +58,11 @@ func (pr *ProcessRepos) Process(ctx context.Context, repositoryUrls []string) er defer func() { wg.Done() }() - pr.processRepo(ctx, repoUrl, errChan) + err := pr.processRepo(ctx, repoUrl, action) + if err != nil { + pr.logger.Error("could not process repo", zap.Error(err)) + errChan <- err + } }(ctx, repoUrl) } @@ -66,74 +77,39 @@ func (pr *ProcessRepos) Process(ctx context.Context, repositoryUrls []string) er return nil } -func (pr *ProcessRepos) processRepo(ctx context.Context, repoUrl string, errChan chan error) { +func (pr *ProcessRepos) processRepo(ctx context.Context, repoUrl string, action *actions.Action) error { cleanup, area, err := pr.prepareAction(ctx) defer func() { if cleanup != nil { - cleanup(ctx, errChan) + cleanup(ctx) } }() if err != nil { - errChan <- err - return + return err } repo, err := pr.clone(ctx, area, repoUrl) if err != nil { - errChan <- err - return + return err } - err = pr.action.Run( - ctx, - area, - func(_ context.Context, area *storage.Area) (bool, error) { - pr.logger.Debug("checking predicate", zap.String("area", area.Path)) - - // TODO: Run predicate instead - contains := false - filepath.WalkDir(area.Path, func(_ string, d fs.DirEntry, _ error) error { - if d.Name() == "roadmap.md" { - contains = true - } - return nil - }) - return contains, nil - }, - func(_ context.Context, area *storage.Area) error { - pr.logger.Debug("running action", zap.String("area", area.Path)) - - // TODO: Run action instead - readme := path.Join(area.Path, "README.md") - file, err := os.Create(readme) - if err != nil { - return fmt.Errorf("could not create readme: %w", err) - } - - _, err = file.WriteString("# Readme") - if err != nil { - return fmt.Errorf("could not write readme: %w", err) - } - - err = pr.commit(ctx, area, repo) - if err != nil { - return err - } - - return nil - }, false) + err = action.Execute(ctx, area) if err != nil { - pr.logger.Error("could not run action", zap.Error(err)) - errChan <- err - return + return err + } + + err = pr.commit(ctx, area, repo) + if err != nil { + return err } pr.logger.Debug("processing done", zap.String("path", area.Path), zap.String("repoUrl", repoUrl)) + return nil } func (pr *ProcessRepos) prepareAction( ctx context.Context, -) (func(ctx context.Context, errChan chan error), *storage.Area, error) { +) (func(ctx context.Context), *storage.Area, error) { pr.logger.Debug("Creating area") area, err := pr.storage.CreateArea(ctx) if err != nil { @@ -141,12 +117,11 @@ func (pr *ProcessRepos) prepareAction( return nil, nil, err } - cleanupfunc := func(ctx context.Context, errChan chan error) { + cleanupfunc := func(ctx context.Context) { pr.logger.Debug("Removing area", zap.String("path", area.Path)) err = pr.storage.RemoveArea(ctx, area) if err != nil { - errChan <- err - return + panic(err) } } diff --git a/internal/schema/kraken.go b/internal/schema/kraken.go new file mode 100644 index 0000000..13881bf --- /dev/null +++ b/internal/schema/kraken.go @@ -0,0 +1,25 @@ +package schema + +import "gopkg.in/yaml.v3" + +type KrakenSchema struct { + ApiVersion string `yaml:"apiVersion"` + Name string `yaml:"name"` + Select struct { + Repositories []string `yaml:"repositories"` + Providers []struct { + Gitea string `yaml:"gitea"` + Organisation string `yaml:"organisation"` + } `yaml:"providers"` + } `yaml:"select"` + Actions []string `yaml:"actions"` +} + +func Unmarshal(raw string) (*KrakenSchema, error) { + k := &KrakenSchema{} + err := yaml.Unmarshal([]byte(raw), k) + if err != nil { + return nil, err + } + return k, nil +} diff --git a/internal/serverdeps/server_deps.go b/internal/serverdeps/server_deps.go index bfa6d39..d81c052 100644 --- a/internal/serverdeps/server_deps.go +++ b/internal/serverdeps/server_deps.go @@ -1,6 +1,7 @@ package serverdeps import ( + actionc "git.front.kjuulh.io/kjuulh/kraken/internal/actions" "git.front.kjuulh.io/kjuulh/kraken/internal/services/actions" "git.front.kjuulh.io/kjuulh/kraken/internal/services/providers" "git.front.kjuulh.io/kjuulh/kraken/internal/services/signer" @@ -59,6 +60,10 @@ func (deps *ServerDeps) GetAction() *actions.Action { return actions.NewAction(deps.logger.With(zap.Namespace("action"))) } +func (deps *ServerDeps) GetActionCreator() *actionc.ActionCreator { + return actionc.NewActionCreator(deps.logger.With(zap.Namespace("action")), deps) +} + func (deps *ServerDeps) GetOpenPGP() *signer.OpenPGP { return deps.openPGP } diff --git a/internal/services/providers/git.go b/internal/services/providers/git.go index f4dee7f..4a19e57 100644 --- a/internal/services/providers/git.go +++ b/internal/services/providers/git.go @@ -70,7 +70,7 @@ func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl stri Auth: auth, RemoteName: "origin", ReferenceName: "refs/heads/main", - SingleBranch: true, + SingleBranch: false, NoCheckout: false, Depth: 1, RecurseSubmodules: 1, @@ -90,6 +90,21 @@ 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() + if err != nil { + return err + } + + return wt.Checkout(&git.CheckoutOptions{ + Hash: [20]byte{}, + Branch: plumbing.NewBranchReferenceName(branch), + Create: false, + Force: false, + Keep: false, + }) +} + func (g *Git) getProgressWriter() *zapio.Writer { return &zapio.Writer{ Log: g.logger.With(zap.String("process", "go-git")),