Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183package app
import ( "context" "encoding/json" "errors" "fmt" "time"
"github.com/alyraffauf/tg/atproto" "github.com/alyraffauf/tg/internal/gitutil" "github.com/alyraffauf/tg/internal/tangledlex" "github.com/bluesky-social/indigo/atproto/syntax" lexutil "github.com/bluesky-social/indigo/lex/util")
// CreatePullInput configures pull request creation.type CreatePullInput struct { RepoDir string // local git repository (for branch detection + patch) Title string Body string Base string // empty: use origin's default branch for a same-repo pull Head string // empty: current branch Target Target Source *Target // nil: same as Target}
var errForkPullBaseRequired = errors.New("fork pull requests require --base; pass the target's local remote branch, for example --base upstream/main")
// pullRecordInput is the write-side input to newPullRecord.type pullRecordInput struct { Title string Body string TargetRepoDid string SourceRepoDid string Base string Head string Patch *atproto.Blob}
// CreatePull generates a patch from the local repository, uploads it, and// writes a pull record.func (s *Service) CreatePull(ctx context.Context, in CreatePullInput) (*PRCreateResult, error) { atClient, did, err := s.authenticatedPDS(ctx) if err != nil { return nil, err }
head := in.Head if head == "" { head, err = s.git.CurrentBranch(ctx, in.RepoDir) if err != nil { return nil, fmt.Errorf("determine source branch: %w", err) } }
target, err := s.resolveRepo(ctx, in.Target) if err != nil { return nil, err } if !atURIPrefix(target.URI) { return nil, fmt.Errorf("target repository %q has no strong at:// URI", in.Target.Repo) } targetRepoDID := stringValue(target.Value.RepoDid) if targetRepoDID == "" { return nil, fmt.Errorf("target repository %q has no repository DID", in.Target.String()) } source := target if in.Source != nil { source, err = s.resolveRepo(ctx, *in.Source) if err != nil { return nil, fmt.Errorf("resolve source repository: %w", err) } } if stringValue(source.Value.RepoDid) == "" { sourceTarget := in.Target if in.Source != nil { sourceTarget = *in.Source } return nil, fmt.Errorf("source repository %q has no repository DID", sourceTarget.String()) } sourceRepoDID := stringValue(source.Value.RepoDid)
var base gitutil.PullBase if in.Base != "" { base, err = s.git.ResolvePullBase(ctx, in.RepoDir, in.Base) if err != nil { return nil, fmt.Errorf("resolve target branch: %w", err) } } else { if sourceRepoDID != targetRepoDID { return nil, errForkPullBaseRequired } base, err = s.git.DefaultPullBase(ctx, in.RepoDir) if err != nil { return nil, fmt.Errorf("determine target branch; set --base explicitly: %w", err) } }
patch, err := s.git.GeneratePatch(ctx, in.RepoDir, base.Revision, head) if err != nil { return nil, fmt.Errorf("generate pull request patch: %w", err) } blob, err := atClient.UploadBlob(ctx, patch, patchMimeType) if err != nil { return nil, err }
uri, err := createPullRecord(ctx, atClient, did, pullRecordInput{ Title: in.Title, Body: in.Body, TargetRepoDid: targetRepoDID, SourceRepoDid: sourceRepoDID, Base: base.Branch, Head: head, Patch: blob, }) if err != nil { return nil, err } return &PRCreateResult{URI: uri, Title: in.Title, Base: base.Branch, Head: head}, nil}
func atURIPrefix(uri string) bool { return len(uri) >= 5 && uri[:5] == "at://" }
func createPullRecord(ctx context.Context, atClient pdsClient, did string, input pullRecordInput) (string, error) { record, err := newPullRecord(input, time.Now().UTC()) if err != nil { return "", err } uri, _, err := atClient.PutRecord(ctx, atproto.PutRecordInput{ Repo: did, Collection: pullCollection, Rkey: string(syntax.NewTIDNow(0)), Record: record, }) if err != nil { return "", fmt.Errorf("create pull request record: %w", err) } return uri, nil}
func newPullRecord(input pullRecordInput, createdAt time.Time) (tangledlex.RepoPull, error) { now := createdAt.Format(time.RFC3339) patchBlob, err := patchBlob(input.Patch) if err != nil { return tangledlex.RepoPull{}, err } return tangledlex.RepoPull{ LexiconTypeID: pullCollection, Title: input.Title, Body: optionalString(input.Body), CreatedAt: now, Target: &tangledlex.RepoPull_Target{ Repo: input.TargetRepoDid, Branch: input.Base, }, Source: &tangledlex.RepoPull_Source{ Repo: optionalString(input.SourceRepoDid), Branch: input.Head, }, Rounds: []*tangledlex.RepoPull_Round{{ CreatedAt: now, PatchBlob: &patchBlob, }}, }, nil}
func patchBlob(blob *atproto.Blob) (lexutil.LexBlob, error) { if blob == nil || blob.Ref == nil { return lexutil.LexBlob{}, nil } data, err := json.Marshal(blob) if err != nil { return lexutil.LexBlob{}, fmt.Errorf("encode pull patch blob: %w", err) } var result lexutil.LexBlob if err := json.Unmarshal(data, &result); err != nil { return lexutil.LexBlob{}, fmt.Errorf("decode pull patch blob: %w", err) } return result, nil}