v0.1 #8
@ -3,8 +3,8 @@ 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
|
||||||
provider:
|
providers:
|
||||||
- gitea: git.front.kjuulh.io/kraken
|
- gitea: git.front.kjuulh.io/kraken
|
||||||
organisation: "kraken"
|
organisation: "kraken"
|
||||||
action:
|
actions:
|
||||||
command: go run main.go
|
- "go run main.go"
|
||||||
|
@ -17,9 +17,14 @@ func CreateKrakenProcessCmd() *cobra.Command {
|
|||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err := json.NewEncoder(&buf).
|
err := json.NewEncoder(&buf).
|
||||||
Encode(struct {
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
16
internal/actions/action.go
Normal file
16
internal/actions/action.go
Normal file
@ -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
|
||||||
|
}
|
88
internal/actions/action_creator.go
Normal file
88
internal/actions/action_creator.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,9 @@ func CommandRoute(logger *zap.Logger, app *gin.Engine, deps *serverdeps.ServerDe
|
|||||||
commandRoute := app.Group("commands")
|
commandRoute := app.Group("commands")
|
||||||
commandRoute.POST("processRepos", func(c *gin.Context) {
|
commandRoute.POST("processRepos", func(c *gin.Context) {
|
||||||
type processReposRequest struct {
|
type processReposRequest struct {
|
||||||
RepositoryUrls []string `json:"repositoryUrls"`
|
Repository string `json:"repository"`
|
||||||
|
Branch string `json:"branch"`
|
||||||
|
Path string `json:"path"`
|
||||||
}
|
}
|
||||||
var request processReposRequest
|
var request processReposRequest
|
||||||
err := c.BindJSON(&request)
|
err := c.BindJSON(&request)
|
||||||
@ -28,11 +30,11 @@ func CommandRoute(logger *zap.Logger, app *gin.Engine, deps *serverdeps.ServerDe
|
|||||||
|
|
||||||
jobId := uuid.New().String()
|
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)
|
ctx := context.WithValue(context.Background(), jobs.JobId{}, jobId)
|
||||||
processRepos := commands.NewProcessRepos(logger, deps)
|
processRepos := commands.NewProcessRepos(logger, deps)
|
||||||
err = processRepos.Process(ctx, repositoryUrls)
|
err = processRepos.Process(ctx, repository, branch, path)
|
||||||
}(request.RepositoryUrls, jobId)
|
}(request.Repository, request.Branch, request.Path, jobId)
|
||||||
|
|
||||||
c.Status(http.StatusAccepted)
|
c.Status(http.StatusAccepted)
|
||||||
})
|
})
|
||||||
|
@ -3,14 +3,10 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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/providers"
|
||||||
"git.front.kjuulh.io/kjuulh/kraken/internal/services/storage"
|
"git.front.kjuulh.io/kjuulh/kraken/internal/services/storage"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@ -21,13 +17,13 @@ type (
|
|||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
storage *storage.Service
|
storage *storage.Service
|
||||||
git *providers.Git
|
git *providers.Git
|
||||||
action *actions.Action
|
actionCreator *actions.ActionCreator
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessReposDeps interface {
|
ProcessReposDeps interface {
|
||||||
GetStorageService() *storage.Service
|
GetStorageService() *storage.Service
|
||||||
GetGitProvider() *providers.Git
|
GetGitProvider() *providers.Git
|
||||||
GetAction() *actions.Action
|
GetActionCreator() *actions.ActionCreator
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,13 +32,24 @@ func NewProcessRepos(logger *zap.Logger, deps ProcessReposDeps) *ProcessRepos {
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
storage: deps.GetStorageService(),
|
storage: deps.GetStorageService(),
|
||||||
git: deps.GetGitProvider(),
|
git: deps.GetGitProvider(),
|
||||||
action: deps.GetAction(),
|
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)
|
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 := sync.WaitGroup{}
|
||||||
wg.Add(len(repositoryUrls))
|
wg.Add(len(repositoryUrls))
|
||||||
|
|
||||||
@ -51,7 +58,11 @@ func (pr *ProcessRepos) Process(ctx context.Context, repositoryUrls []string) er
|
|||||||
defer func() {
|
defer func() {
|
||||||
wg.Done()
|
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)
|
}(ctx, repoUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,53 +77,25 @@ func (pr *ProcessRepos) Process(ctx context.Context, repositoryUrls []string) er
|
|||||||
return nil
|
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)
|
cleanup, area, err := pr.prepareAction(ctx)
|
||||||
defer func() {
|
defer func() {
|
||||||
if cleanup != nil {
|
if cleanup != nil {
|
||||||
cleanup(ctx, errChan)
|
cleanup(ctx)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- err
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := pr.clone(ctx, area, repoUrl)
|
repo, err := pr.clone(ctx, area, repoUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- err
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pr.action.Run(
|
err = action.Execute(ctx, area)
|
||||||
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create readme: %w", err)
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
_, err = file.WriteString("# Readme")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not write readme: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pr.commit(ctx, area, repo)
|
err = pr.commit(ctx, area, repo)
|
||||||
@ -120,20 +103,13 @@ func (pr *ProcessRepos) processRepo(ctx context.Context, repoUrl string, errChan
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}, false)
|
|
||||||
if err != nil {
|
|
||||||
pr.logger.Error("could not run action", zap.Error(err))
|
|
||||||
errChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.logger.Debug("processing done", zap.String("path", area.Path), zap.String("repoUrl", repoUrl))
|
pr.logger.Debug("processing done", zap.String("path", area.Path), zap.String("repoUrl", repoUrl))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pr *ProcessRepos) prepareAction(
|
func (pr *ProcessRepos) prepareAction(
|
||||||
ctx context.Context,
|
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")
|
pr.logger.Debug("Creating area")
|
||||||
area, err := pr.storage.CreateArea(ctx)
|
area, err := pr.storage.CreateArea(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -141,12 +117,11 @@ func (pr *ProcessRepos) prepareAction(
|
|||||||
return nil, nil, err
|
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))
|
pr.logger.Debug("Removing area", zap.String("path", area.Path))
|
||||||
err = pr.storage.RemoveArea(ctx, area)
|
err = pr.storage.RemoveArea(ctx, area)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- err
|
panic(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
internal/schema/kraken.go
Normal file
25
internal/schema/kraken.go
Normal file
@ -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
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package serverdeps
|
package serverdeps
|
||||||
|
|
||||||
import (
|
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/actions"
|
||||||
"git.front.kjuulh.io/kjuulh/kraken/internal/services/providers"
|
"git.front.kjuulh.io/kjuulh/kraken/internal/services/providers"
|
||||||
"git.front.kjuulh.io/kjuulh/kraken/internal/services/signer"
|
"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")))
|
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 {
|
func (deps *ServerDeps) GetOpenPGP() *signer.OpenPGP {
|
||||||
return deps.openPGP
|
return deps.openPGP
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl stri
|
|||||||
Auth: auth,
|
Auth: auth,
|
||||||
RemoteName: "origin",
|
RemoteName: "origin",
|
||||||
ReferenceName: "refs/heads/main",
|
ReferenceName: "refs/heads/main",
|
||||||
SingleBranch: true,
|
SingleBranch: false,
|
||||||
NoCheckout: false,
|
NoCheckout: false,
|
||||||
Depth: 1,
|
Depth: 1,
|
||||||
RecurseSubmodules: 1,
|
RecurseSubmodules: 1,
|
||||||
@ -90,6 +90,21 @@ 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 {
|
||||||
|
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 {
|
func (g *Git) getProgressWriter() *zapio.Writer {
|
||||||
return &zapio.Writer{
|
return &zapio.Writer{
|
||||||
Log: g.logger.With(zap.String("process", "go-git")),
|
Log: g.logger.With(zap.String("process", "go-git")),
|
||||||
|
Loading…
Reference in New Issue
Block a user