Something went wrong. Try again.
Monorepo for Tangled forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135package spindle
import ( "context" "errors" "fmt" pathpkg "path" "strings"
"github.com/bluesky-social/indigo/atproto/syntax"
"tangled.org/core/gitclient" "tangled.org/core/gitutil" "tangled.org/core/spindle/db" "tangled.org/core/spindle/knotclient" "tangled.org/core/workflow")
// no fetch here, it uses a clone that already has the commit or asks the knotfunc (s *Spindle) readWorkflowDir(ctx context.Context, repoDid syntax.DID, ref string) (workflow.RawPipeline, string, error) { repo, err := s.db.GetRepoByDid(repoDid) if err != nil { return nil, "", fmt.Errorf("unknown repoDid %s: %w", repoDid, err) } // one client per read, so the knot is asked for its caps once git := s.knots.Git(knotclient.Knot{Host: repo.Knot}, repoDid)
admitted := false admit := func() error { if admitted { return nil } admitted = true return s.workflowDefs.admit(repoDid) }
commit := ref if !gitutil.Rev(ref).IsObjectID() { if err := admit(); err != nil { return nil, "", err } if commit, err = s.resolveWorkflowRef(ctx, git, *repo, ref); err != nil { return nil, "", err } }
raw, ok, err := s.readLocalWorkflows(ctx, s.newRepoPath(repoDid), commit) if ok || err != nil { return raw, commit, err } if err := admit(); err != nil { return nil, "", err } raw, err = s.readKnotWorkflows(ctx, git, commit) return raw, commit, err}
func (s *Spindle) resolveWorkflowRef(ctx context.Context, git *gitclient.Client, repo db.Repo, ref string) (string, error) { if ref == "HEAD" { _, hash, err := s.getRepoDefaultBranch(ctx, git, repo) return hash, err } hash, err := git.Resolve(ctx, ref) if err != nil { return "", fmt.Errorf("resolving %s of %s: %w", ref, repo.RepoDid, err) } return string(hash), nil}
// no lock, a fetch replacing the clone only makes git fail, which falls back to the knotfunc (s *Spindle) readLocalWorkflows(ctx context.Context, repoPath, commit string) (raw workflow.RawPipeline, ok bool, err error) { if _, err := gitutil.OutputOffline(ctx, "-C", repoPath, "cat-file", "-e", commit+"^{commit}"); err != nil { return nil, false, nil } raw, err = readWorkflowTree(ctx, gitutil.OutputOffline, repoPath, commit) if errors.Is(err, errTooManyWorkflowFiles) { return nil, true, err } return raw, err == nil, nil}
func (s *Spindle) readKnotWorkflows(ctx context.Context, git *gitclient.Client, commit string) (workflow.RawPipeline, error) { files, err := git.ReadDir(ctx, gitclient.Oid(commit), workflow.WorkflowDir) if errors.Is(err, gitclient.ErrPathNotFound) { return nil, nil } if err != nil { return nil, fmt.Errorf("listing %s: %w", workflow.WorkflowDir, err) } var raw workflow.RawPipeline for _, file := range files { if !isRegularFileMode(file.Mode) { continue } if len(raw) >= maxWorkflowFiles { return nil, fmt.Errorf("%w: more than %d files under %s", errTooManyWorkflowFiles, maxWorkflowFiles, workflow.WorkflowDir) } raw = append(raw, workflow.RawWorkflow{Name: file.Name, Contents: file.Data}) } return raw, nil}
// the knot pads modes to seven digits, git prints sixfunc isRegularFileMode(mode string) bool { mode = strings.TrimLeft(mode, "0") return mode == "100644" || mode == "100755"}
type gitOutput func(ctx context.Context, args ...string) ([]byte, error)
// a symlink in .tangled could point reads at files on this hostfunc readWorkflowTree(ctx context.Context, git gitOutput, repoPath, commit string) (workflow.RawPipeline, error) { listing, err := git(ctx, "-C", repoPath, "ls-tree", "-z", commit, workflow.WorkflowDir+"/") if err != nil { return nil, fmt.Errorf("listing %s: %w", workflow.WorkflowDir, err) } var raw workflow.RawPipeline for _, entry := range strings.Split(string(listing), "\x00") { meta, path, ok := strings.Cut(entry, "\t") fields := strings.Fields(meta) if !ok || len(fields) != 3 || !isRegularFileMode(fields[0]) { continue } if len(raw) >= maxWorkflowFiles { return nil, fmt.Errorf("%w: more than %d files under %s", errTooManyWorkflowFiles, maxWorkflowFiles, workflow.WorkflowDir) } contents, err := git(ctx, "-C", repoPath, "cat-file", "blob", fields[2]) if err != nil { return nil, fmt.Errorf("reading raw content of '%s': %w", path, err) } raw = append(raw, workflow.RawWorkflow{Name: pathpkg.Base(path), Contents: contents}) } return raw, nil}