gitops

package
v6.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 29, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package gitops compares files kept in Git with what the platform holds, for `diff` and `apply --dry-run`, and moves whole projects between the two.

Index

Constants

This section is empty.

Variables

View Source
var Environment = Kind{
	Name:     "environment",
	ReadOnly: environment.ReadOnly,
	Identity: "id",

	Remote: func(ctx context.Context, c *platform.Client, local *jsyaml.Map) (string, *jsyaml.Map, error) {
		if id := str(local, "id"); id != "" {
			return byID(func(ctx context.Context, c *platform.Client, id openapi_types.UUID) (*http.Response, error) {
				return c.GetEnvironment(ctx, id)
			})(ctx, c, local)
		}
		name := str(local, "name")
		if name == "" {
			return "", nil, nil
		}
		var list struct {
			Environments []struct {
				ID   string `json:"id"`
				Name string `json:"name"`
			} `json:"environments"`
		}
		resp, err := c.GetEnvironments(ctx, &api.GetEnvironmentsParams{Search: &name})
		if _, err := platform.Decode(resp, err, &list); err != nil {
			return "", nil, err
		}
		for _, e := range list.Environments {
			if e.Name == name {
				u, _ := uuid(e.ID)
				remote, err := fetch(c.GetEnvironment(ctx, u))
				return e.ID, remote, err
			}
		}
		return "", nil, nil
	},
}
View Source
var ErrDifferent = errors.New("files differ from the platform")

ErrDifferent ends `diff` with exit status 2 when files and platform disagree, so a pipeline can tell drift (2) from a failure (1).

View Source
var Experiment = Kind{
	Name:     "experiment",
	ReadOnly: []string{"version", "created", "createdBy", "edited", "editedBy"},
	Identity: "key",

	Remote: func(ctx context.Context, c *platform.Client, local *jsyaml.Map) (string, *jsyaml.Map, error) {
		key := str(local, "key")
		if key == "" {
			externalID := str(local, "externalId")
			if externalID == "" {
				return "", nil, nil
			}
			var list struct {
				Experiments []struct {
					Key string `json:"key"`
				} `json:"experiments"`
			}
			ids := []string{externalID}
			resp, err := c.GetExperiments(ctx, &api.GetExperimentsParams{ExternalId: &ids})
			if _, err := platform.Decode(resp, err, &list); err != nil {
				return "", nil, err
			}
			if len(list.Experiments) == 0 {
				return "", nil, nil
			}
			key = list.Experiments[0].Key
		}
		remote, err := fetch(c.GetExperiment(ctx, key))
		return key, remote, err
	},
}
View Source
var Hub = Kind{
	Name:     "hub",
	ReadOnly: hub.ReadOnly,
	Identity: "id",
	Remote: byID(func(ctx context.Context, c *platform.Client, id openapi_types.UUID) (*http.Response, error) {
		return c.GetHubById(ctx, id)
	}),
}
View Source
var Integrations = map[string]Kind{
	integration.Webhook.Name:         integrationKind(integration.Webhook, "webhook integration", allTargetAttributes),
	integration.Slack.Name:           integrationKind(integration.Slack, "Slack integration", nil),
	integration.Preflight.Name:       integrationKind(integration.Preflight, "preflight webhook", allTargetAttributes),
	integration.PreflightAction.Name: integrationKind(integration.PreflightAction, "preflight action integration", nil),
}

Integrations are the kinds of integration by their command's name.

View Source
var PropertyDefinition = Kind{
	Name:     "property definition",
	ReadOnly: property.ReadOnly,
	Identity: "key",
	Remote: func(ctx context.Context, c *platform.Client, local *jsyaml.Map) (string, *jsyaml.Map, error) {
		key := str(local, "key")
		if key == "" {
			return "", nil, nil
		}
		remote, err := fetch(c.GetPropertyDefinition(ctx, key))
		return key, remote, err
	},
}
View Source
var Schedule = Kind{
	Name:     "experiment schedule",
	ReadOnly: []string{"editedBy", "lastUpdated", "nextExecution"},
	Defaults: map[string]any{"allowParallel": true},
	Identity: "id",
	Remote: func(ctx context.Context, c *platform.Client, local *jsyaml.Map) (string, *jsyaml.Map, error) {
		id := str(local, "id")
		if id == "" {
			return "", nil, nil
		}
		remote, err := fetch(c.GetSchedules(ctx, id))
		return id, remote, err
	},
}
View Source
var Service = Kind{
	Name:     "service",
	ReadOnly: []string{"version", "created", "createdBy", "edited", "editedBy"},
	Defaults: map[string]any{"logoId": "service", "logoColor": "blue"},
	Identity: "id",
	Remote: func(ctx context.Context, c *platform.Client, local *jsyaml.Map) (string, *jsyaml.Map, error) {
		id := str(local, "id")
		u, ok := uuid(id)
		if !ok {
			return "", nil, nil
		}
		remote, err := fetch(c.GetService(ctx, u))
		return id, remote, err
	},
}
View Source
var ServiceProfile = Kind{
	Name:     "service profile",
	ReadOnly: []string{"version", "created", "createdBy", "edited", "editedBy", "defaultProfile"},
	Defaults: map[string]any{"origin": "CUSTOM"},
	Identity: "id",

	Remote: func(ctx context.Context, c *platform.Client, local *jsyaml.Map) (string, *jsyaml.Map, error) {
		id := str(local, "id")
		if u, ok := uuid(id); ok {
			remote, err := fetch(c.GetProfile(ctx, u))
			return id, remote, err
		}
		name := str(local, "name")
		if name == "" {
			return "", nil, nil
		}
		type profile struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		}
		profiles, err := platform.AllPages[profile](func(page, size int32) (*http.Response, error) {
			return c.GetProfiles(ctx, &api.GetProfilesParams{Name: &name, Page: api.PageRequestAO{Page: &page, Size: &size}})
		})
		if err != nil {
			return "", nil, err
		}
		for _, p := range profiles {
			if p.Name == name {
				u, _ := uuid(p.ID)
				remote, err := fetch(c.GetProfile(ctx, u))
				return p.ID, remote, err
			}
		}
		return "", nil, nil
	},
}
View Source
var Team = Kind{
	Name:     "team",
	ReadOnly: append(append([]string{"id"}, team.ReadOnly...), prefixed("members.", team.MemberReadOnly)...),

	Defaults: map[string]any{"allowedActions": []any{"wait", "service-validation"}, "managedBy": "MANUAL"},
	Identity: "key",
	Remote: func(ctx context.Context, c *platform.Client, local *jsyaml.Map) (string, *jsyaml.Map, error) {
		key := str(local, "key")
		if key == "" {
			return "", nil, nil
		}
		remote, err := fetch(c.GetTeam(ctx, key))
		return key, remote, err
	},
}

Team is matched by its key, as the platform upserts it. The id differs from one platform to the next, so it is neither exported nor compared: a team kept in Git matches its namesake anywhere.

View Source
var Template = Kind{
	Name:     "experiment template",
	ReadOnly: template.ReadOnly,
	Identity: "id",
	Remote: byID(func(ctx context.Context, c *platform.Client, id openapi_types.UUID) (*http.Response, error) {
		return c.GetExperimentTemplate(ctx, id)
	}),
}

Functions

func ApplyProject

func ApplyProject(ctx context.Context, c *platform.Client, o ApplyOptions) error

ApplyProject applies every kind in a project, in the order of projectKinds.

func Changes

func Changes(diff string) int

Changes counts the changed lines of a diff, for dry runs.

func Comparable

func Comparable(local, remote any) any

Comparable projects the platform's version of a document onto the file's: every field the file sets, and only those fields the file leaves out that hold something. The platform fills in defaults (false, empty lists and maps) that a file written by hand omits; reporting those would bury every real difference.

func Diff

func Diff(file string, local, remote *jsyaml.Map, always ...string) (string, error)

Diff is a unified diff from the platform's version to the file's, empty when they agree. Both sides are rendered as YAML the way `get` writes it. Fields in always are reported whenever the platform holds them and the file does not.

func DiffFiles

func DiffFiles(ctx context.Context, c *platform.Client, k Kind, paths []string, recursive bool) error

DiffFiles prints how each file differs from the platform: a unified diff for changes, a line for files that would create something new.

func DiffProject

func DiffProject(ctx context.Context, c *platform.Client, dir string) error

DiffProject diffs every kind in a project, and reports drift once all were compared.

func DryRun

func DryRun(ctx context.Context, c *platform.Client, k Kind, paths []string, recursive bool) error

DryRun reports what applying the files would do, without applying anything.

func Export

func Export(ctx context.Context, c *platform.Client, o ExportOptions) error

Export writes a team's experiments, schedules and services, and the custom service profiles those services use, as a project; or the tenant's configuration. Files are only written, never removed, so something deleted on the platform keeps its file until it is removed by hand.

Types

type ApplyOptions

type ApplyOptions struct {
	Directory         string
	DeleteExperiments bool
	DryRun            bool
}

type ExportOptions

type ExportOptions struct {
	Directory string
	Team      string
	Tenant    bool
}

type Kind

type Kind struct {
	// Name is what messages call it, "experiment" or "service profile".
	Name string
	// ReadOnly fields are reported by the platform but not part of the file. In
	// "members.name", the field is dropped from each member.
	ReadOnly []string
	// Defaults are the values the platform gives fields a file leaves out, or leaves as
	// an empty list or map. Holding exactly that, such a field is not a difference;
	// holding anything else, applying the file would reset it, which is.
	Defaults map[string]any
	// Secrets are fields holding credentials, as integration.Kind names them. A mask in
	// a file stands for whatever the platform holds, and no value is ever printed.
	Secrets []string
	// Identity is the field that names it on the platform. A file matched otherwise, by
	// an externalId or a name, has none yet, which is not a difference.
	Identity string
	// Remote finds the platform's version of a file: its id and content, or nil when
	// applying the file would create something new.
	Remote func(ctx context.Context, c *platform.Client, local *jsyaml.Map) (string, *jsyaml.Map, error)
}

Kind is a type of file kept in Git and how to find its counterpart on the platform.

type Result

type Result struct {
	File  string
	ID    string
	State State
	Diff  string
}

func Compare

func Compare(ctx context.Context, c *platform.Client, k Kind, file string, local *output.Document) (Result, error)

Compare works out what applying the file would change on the platform.

func (Result) Describe

func (r Result) Describe(k Kind) string

Describe is the one-line outcome of a comparison, as a dry run reports it.

type State

type State int

State is what applying a file would do.

const (
	Unchanged State = iota
	Changed
	New
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL