GitOps for k8s
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

working.go 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package git
  2. import (
  3. "context"
  4. "os"
  5. "path/filepath"
  6. )
  7. // Config holds some values we use when working in the working clone of
  8. // a repo.
  9. type Config struct {
  10. Branch string // branch we're syncing to
  11. Path string // path within the repo containing files we care about
  12. SyncTag string
  13. NotesRef string
  14. UserName string
  15. UserEmail string
  16. SetAuthor bool
  17. SkipMessage string
  18. }
  19. // Checkout is a local working clone of the remote repo. It is
  20. // intended to be used for one-off "transactions", e.g,. committing
  21. // changes then pushing upstream. It has no locking.
  22. type Checkout struct {
  23. dir string
  24. config Config
  25. upstream Remote
  26. realNotesRef string // cache the notes ref, since we use it to push as well
  27. }
  28. type Commit struct {
  29. Revision string
  30. Message string
  31. }
  32. // CommitAction - struct holding commit information
  33. type CommitAction struct {
  34. Author string
  35. Message string
  36. }
  37. // Clone returns a local working clone of the sync'ed `*Repo`, using
  38. // the config given.
  39. func (r *Repo) Clone(ctx context.Context, conf Config) (*Checkout, error) {
  40. upstream := r.Origin()
  41. repoDir, err := r.workingClone(ctx, conf.Branch)
  42. if err != nil {
  43. return nil, CloningError(upstream.URL, err)
  44. }
  45. if err := config(ctx, repoDir, conf.UserName, conf.UserEmail); err != nil {
  46. os.RemoveAll(repoDir)
  47. return nil, err
  48. }
  49. // We'll need the notes ref for pushing it, so make sure we have
  50. // it. This assumes we're syncing it (otherwise we'll likely get conflicts)
  51. realNotesRef, err := getNotesRef(ctx, repoDir, conf.NotesRef)
  52. if err != nil {
  53. os.RemoveAll(repoDir)
  54. return nil, err
  55. }
  56. r.mu.RLock()
  57. if err := fetch(ctx, repoDir, r.dir, realNotesRef+":"+realNotesRef); err != nil {
  58. os.RemoveAll(repoDir)
  59. r.mu.RUnlock()
  60. return nil, err
  61. }
  62. r.mu.RUnlock()
  63. return &Checkout{
  64. dir: repoDir,
  65. upstream: upstream,
  66. realNotesRef: realNotesRef,
  67. config: conf,
  68. }, nil
  69. }
  70. // Clean a Checkout up (remove the clone)
  71. func (c *Checkout) Clean() {
  72. if c.dir != "" {
  73. os.RemoveAll(c.dir)
  74. }
  75. }
  76. // Dir returns the path to the repo
  77. func (c *Checkout) Dir() string {
  78. return c.dir
  79. }
  80. // ManifestDir returns the path to the manifests files
  81. func (c *Checkout) ManifestDir() string {
  82. return filepath.Join(c.dir, c.config.Path)
  83. }
  84. // CommitAndPush commits changes made in this checkout, along with any
  85. // extra data as a note, and pushes the commit and note to the remote repo.
  86. func (c *Checkout) CommitAndPush(ctx context.Context, commitAction CommitAction, note *Note) error {
  87. if !check(ctx, c.dir, c.config.Path) {
  88. return ErrNoChanges
  89. }
  90. commitAction.Message += c.config.SkipMessage
  91. if err := commit(ctx, c.dir, commitAction); err != nil {
  92. return err
  93. }
  94. if note != nil {
  95. rev, err := refRevision(ctx, c.dir, "HEAD")
  96. if err != nil {
  97. return err
  98. }
  99. if err := addNote(ctx, c.dir, rev, c.config.NotesRef, note); err != nil {
  100. return err
  101. }
  102. }
  103. refs := []string{c.config.Branch}
  104. ok, err := refExists(ctx, c.dir, c.realNotesRef)
  105. if ok {
  106. refs = append(refs, c.realNotesRef)
  107. } else if err != nil {
  108. return err
  109. }
  110. if err := push(ctx, c.dir, c.upstream.URL, refs); err != nil {
  111. return PushError(c.upstream.URL, err)
  112. }
  113. return nil
  114. }
  115. // GetNote gets a note for the revision specified, or nil if there is no such note.
  116. func (c *Checkout) GetNote(ctx context.Context, rev string) (*Note, error) {
  117. return getNote(ctx, c.dir, c.realNotesRef, rev)
  118. }
  119. func (c *Checkout) HeadRevision(ctx context.Context) (string, error) {
  120. return refRevision(ctx, c.dir, "HEAD")
  121. }
  122. func (c *Checkout) SyncRevision(ctx context.Context) (string, error) {
  123. return refRevision(ctx, c.dir, c.config.SyncTag)
  124. }
  125. func (c *Checkout) MoveSyncTagAndPush(ctx context.Context, ref, msg string) error {
  126. return moveTagAndPush(ctx, c.dir, c.config.SyncTag, ref, msg, c.upstream.URL)
  127. }
  128. // ChangedFiles does a git diff listing changed files
  129. func (c *Checkout) ChangedFiles(ctx context.Context, ref string) ([]string, error) {
  130. list, err := changedFiles(ctx, c.dir, c.config.Path, ref)
  131. if err == nil {
  132. for i, file := range list {
  133. list[i] = filepath.Join(c.dir, file)
  134. }
  135. }
  136. return list, err
  137. }
  138. func (c *Checkout) NoteRevList(ctx context.Context) (map[string]struct{}, error) {
  139. return noteRevList(ctx, c.dir, c.realNotesRef)
  140. }