Skip to content
quarry Artifact [ce27f22f2b]
Login

Artifact [ce27f22f2b]

Artifact ce27f22f2bef88543ecf2c10ad8fd7f619a840d7f4f65117ec7b3c5170d587bf:


package main

import (
	"encoding/json"
	"errors"
	"os"
	"path/filepath"

	"craftdesign.group/quarry/internal/ci"
)

// Log retention (RFC 0009 §logs, as amended for dagnats#624's scope):
// the engine owns the hot lane; a forge that needs a verdict to stay
// explainable for years stores the logs next to the verdict. On a
// run's terminal transition the poller drains every step's chunks into
// one object per run under the derived-data directory. An engine that
// predates the lane simply leaves nothing to drain.

// ciLogPath is the drained object: data/<repo>/ci-logs/<runID>.json.
func ciLogPath(dir, repo, runID string) string {
	return filepath.Join(dir, repo, "ci-logs", runID+".json")
}

// drainLogs pulls a finished run's chunks and stores them, each chunk
// stamped with its step. Draining twice is a cheap overwrite with the
// same bytes; an engine without the lane is not an error, it is an
// empty drain.
func drainLogs(e *ci.Engine, dir, repo, runID string, steps []string) error {
	var all []ci.LogChunk
	for _, step := range steps {
		chunks, err := e.Logs(runID, step, 0, false)
		if errors.Is(err, ci.ErrNoLogs) {
			return nil
		}
		if err != nil {
			return err
		}
		for _, c := range chunks {
			c.Step = step
			all = append(all, c)
		}
	}
	if len(all) == 0 {
		return nil
	}
	p := ciLogPath(dir, repo, runID)
	if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
		return err
	}
	b, err := json.Marshal(all)
	if err != nil {
		return err
	}
	return os.WriteFile(p, b, 0o644)
}

// drainedLogs is the read side: the stored chunks, or nothing.
type ciLogStore struct{ dir string }

func (s ciLogStore) DrainedLogs(repo, runID string) ([]ci.LogChunk, bool) {
	b, err := os.ReadFile(ciLogPath(s.dir, repo, runID))
	if err != nil {
		return nil, false
	}
	var chunks []ci.LogChunk
	if json.Unmarshal(b, &chunks) != nil {
		return nil, false
	}
	return chunks, true
}