reconcile

package
v1.76.9 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package reconcile is what the GitHub controller decides, with no network in it: from the policy's bindings, the holders of the groups they name, and what GitHub holds, what one organisation should look like and what to change to get there.

It runs in two steps because removing somebody needs one more question than adding them. Derive works out everything the inputs settle, and names the addresses whose removal would rest on ABSENCE from a holders list — which is never evidence on its own: an unreadable workspace contributes no holders at all. [Decide] finishes once each of those addresses has been asked about one at a time, and removes only on an answer the directory vouches for.

Who a GitHub account belongs to is what its person LINKED: they authorized the link App as that account, and GitHub verified the work addresses the link proves. GitHub discloses nothing else about a member's addresses outside its Enterprise Cloud plan, where the organisation's verified-domain addresses count as well.

What is never touched, whatever the inputs: a member nobody linked (nobody can say who they are), a team no binding names, and an owner's place in the organisation. An owner is added to teams and promoted in them like anybody, and never removed from a team or demoted in one: owners are managed outside, and a break-glass seat keeps what it has. The one removal that does not ask the directory is an account whose link GitHub itself says is gone — the person removed the address, or revoked the authorization.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action struct {
	Kind status.Action
	// Team is the slug it concerns; empty for the organisation itself.
	Team  string
	Login string
	Email string
	Role  status.Role
	// Teams are the ids an invitation places the invitee in.
	Teams []int64
	// Account is the linked account an invitation is for.
	Account int64
	// Reason is why a removal happens, when it is not the directory's.
	Reason string
}

Action is one change to GitHub.

func Guard

func Guard(report *status.Org, actions []Action, g Guards) []Action

Guard applies the three organisation-wide rules to a decided organisation, in order, and returns what is left to do:

  1. An account that let two invitations expire since it last linked is not invited again. Linking again starts the count over.
  2. Nobody is invited without a free seat, and nobody at all while the seats cannot be read: an invitation past the last seat either buys one or is refused, and neither is the controller's to do.
  3. A pass whose removals concern more than half the organisation's members removes nobody, unless an operator confirmed exactly that set. A policy mistake — a group renamed — looks exactly like everybody leaving at once.

func (Action) String

func (a Action) String() string

type Confirmation

type Confirmation = rails.Vouch

Confirmation is the directory's answer about one address, asked before anybody is removed on its account: the generic rails.Vouch.

type Draft

type Draft struct {
	// contains filtered or unexported fields
}

Draft is an organisation derived as far as its inputs allow.

func Derive

func Derive(org string, binding policy.GitHubOrg, holders Holders, state State) *Draft

Derive works out one organisation from its binding, the holders of the groups it names, and what GitHub holds.

func (*Draft) Confirm

func (d *Draft) Confirm() []string

Confirm is the addresses whose removal needs asking about, sorted.

func (*Draft) Decide

func (d *Draft) Decide(confirmations map[string]Confirmation) (status.Org, []Action)

Decide finishes the organisation: its report, and the changes to make. Every change is returned whether or not the organisation is enabled — the report of a disabled one says what WOULD happen — and held changes are in the report and not in the list.

type Guards

type Guards struct {
	// Plan is the organisation's seats; Known false when GitHub would not
	// say.
	Plan  githubapp.Plan
	Known bool
	// Pending is how many invitations are waiting to be accepted.
	Pending int
	// Failed are invitations that expired.
	Failed []githubapp.FailedInvitation
	// LinkedAt is when each linked account was last linked, by id.
	LinkedAt map[int64]time.Time
	// Members is how many members the organisation has.
	Members int
	// Confirmed is the fingerprint of the removals an operator confirmed.
	Confirmed string
}

Guards are what a decided organisation is checked against before anything is done: facts GitHub holds about the organisation that no single member's row can settle.

type Holder

type Holder = rails.Holder

Holder is one account holding a group, as the directory reports it. It is the generic rails.Holder.

type Holders

type Holders = rails.Holders

Holders are each bound group's holders.

type Link struct {
	ID     int64
	Login  string
	Emails []string
	// Lost is GitHub's own answer that the link's proof is gone. The
	// account leaves the organisation.
	Lost   bool
	Reason string
	// LinkedAt is when the person last linked: invitations that expired
	// before it do not count against them.
	LinkedAt time.Time
}

Link is one linked GitHub account.

type State

type State struct {
	Members     []githubapp.Member
	Invitations []githubapp.Invitation
	Teams       []githubapp.Team
	// TeamMembers are each bound team's members, by slug. A bound team
	// missing here does not exist on GitHub.
	TeamMembers map[string][]githubapp.TeamMember
	// Links are the accounts people linked, as last checked. A link that
	// could not be checked is not here at all: it neither adds anybody nor
	// removes anybody.
	Links []Link
}

State is what GitHub holds for one organisation.

Jump to

Keyboard shortcuts

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