Skip to content
quarry Artifact [f6991b892a]
Login

Artifact [f6991b892a]

Artifact f6991b892a1d074de845a5dbce9720eaa6784c60147a7dc17fb9ace0525e48be:


package ci

import (
	"errors"
	"log"
)

// TagStore is the record-keeping the tag walk needs beyond Store's: the
// tag watermark, the newest tag already considered, so a tag starts its
// workflows once (RFC 0020's `tag` event).
type TagStore interface {
	Store
	CITagWatermark(repo string) (TagMark, bool)
	SetCITagWatermark(repo string, m TagMark) error
}

// TagRepo is the read side the tag walk needs beyond Repo's.
type TagRepo interface {
	Repo
	// TagsAfter lists the tags added to check-ins after a mark, oldest
	// first; NewestTag is the newest one's mark, the zero mark when the
	// repository has none.
	TagsAfter(after TagMark, limit int) ([]Tagged, error)
	NewestTag() (TagMark, error)
}

// TagMark names one T card that added a tag to a check-in, in the order
// the tag walk takes them: the carrying artifact's date, the artifact,
// the tagged check-in, the tag's name. The zero mark precedes every tag.
type TagMark struct {
	Date   string
	Src    string
	Target string
	Name   string // bare: "version-0.2.0", never "sym-version-0.2.0"
}

// Tagged is a tag added to a check-in, and the check-in it was added
// to, branch included. Which T cards count is the index's rule
// (index.TagsAfter): a symbolic tag added, not a branch's own name.
type Tagged struct {
	TagMark
	At Checkin
}

// TriggerTags starts the workflows of tags added since the tag
// watermark (RFC 0020's `tag` event). A tag is usually added after its
// check-in, by a control artifact, so Trigger's check-in walk never
// sees it; the tag walk is its own, with the check-in walk's rules: a
// first sight starts nothing, a transient failure holds the watermark,
// a content fault advances it.
//
// A tag launch neither waits for a worker slot nor supersedes: a
// release is rare, deliberate, and not made stale by the next tag.
func TriggerTags(e *Engine, st TagStore, repo string, r TagRepo, cloneURL string) error {
	if !e.Enabled() {
		return nil
	}
	mark, ok := st.CITagWatermark(repo)
	if !ok {
		newest, err := r.NewestTag()
		if err != nil {
			return err
		}
		return st.SetCITagWatermark(repo, newest)
	}
	fresh, err := r.TagsAfter(mark, 20)
	if err != nil {
		return err
	}
	for _, t := range fresh {
		c := t.At
		c.Tag = t.Name
		pipelines, err := pipelinesAt(r, c)
		if err == nil {
			err = launch(e, st, repo, r, c, cloneURL, pipelines, scopeTag)
		}
		if errors.Is(err, errTransient) {
			log.Printf("ci: %s %.12s tag %s: %v — tag watermark held, retrying next round", repo, c.Hash, t.Name, err)
			return nil
		}
		if err != nil {
			log.Printf("ci: %s %.12s tag %s: %v", repo, c.Hash, t.Name, err)
		}
		if err := st.SetCITagWatermark(repo, t.TagMark); err != nil {
			return err
		}
	}
	return nil
}