githubsync

package
v1.482.0 Latest Latest
Warning

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

Go to latest
Published: Jun 19, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecryptField

func DecryptField(dek []byte, value string) (string, error)

DecryptField decrypts a value produced by EncryptField. If the value does not have the enc:v1: prefix it is returned as-is (plaintext pass-through).

func EncryptField

func EncryptField(dek []byte, plaintext string) (string, error)

EncryptField encrypts a plaintext value with AES-256-GCM using a deterministic nonce. The nonce = HMAC-SHA256(DEK, plaintext)[:12] — same plaintext+DEK always yields the same ciphertext, keeping git diffs clean. Returns "enc:v1:<base64(nonce||ciphertext)>".

func IsEncrypted

func IsEncrypted(value string) bool

IsEncrypted returns true when the value carries the enc:v1: prefix.

func IsSensitiveKey

func IsSensitiveKey(key string) bool

IsSensitiveKey returns true when an env-var or header key name suggests its value is secret.

Types

type GitHubSyncClient

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

GitHubSyncClient wraps the GitHub Git Data API for atomic batch push/pull.

func NewGitHubSyncClient

func NewGitHubSyncClient(ctx context.Context, token, repoFullName string) (*GitHubSyncClient, error)

NewGitHubSyncClient creates a client authenticated with the given PAT. repoFullName must be "owner/repo". When the GITHUB_API environment variable is set, an Enterprise client is created using that URL so that operators who configure GitHub Enterprise in Helm do not need separate git-sync settings.

func (*GitHubSyncClient) GetFile

func (c *GitHubSyncClient) GetFile(ctx context.Context, branch, path string) ([]byte, error)

GetFile returns the decoded content of a single file from the repository.

func (*GitHubSyncClient) ListFiles

func (c *GitHubSyncClient) ListFiles(ctx context.Context, branch, prefix string) ([]string, error)

ListFiles returns repo-relative paths of all blobs under the given prefix on the branch.

func (*GitHubSyncClient) PushFiles

func (c *GitHubSyncClient) PushFiles(ctx context.Context, branch, commitMessage string, files map[string][]byte) (string, error)

PushFiles commits files to the repository in a single atomic commit. files maps repo-relative paths to content. nil content entries delete the file. Returns the new commit SHA.

type Handlers

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

Handlers implements the custom route handler for GitHub sync endpoints.

func NewHandlers

func NewHandlers(
	settingsRepo portrepos.SettingsRepository,
	scheduleRepo schedule.Manager,
	webhookRepo portrepos.WebhookRepository,
	memoryRepo portrepos.MemoryRepository,
	taskRepo portrepos.TaskRepository,
	taskGroupRepo portrepos.TaskGroupRepository,
	userFileRepo portrepos.UserFileRepository,
	slackbotRepo portrepos.SlackBotRepository,
	kmsKeyARN, awsRegion string,
	githubAppInstallID string,
) *Handlers

NewHandlers creates a Handlers instance registering all required repositories. githubAppInstallID is optional — when set, it enables GitHub App token fallback for users who have not configured a personal GitHub token. kmsKeyARN and awsRegion are no longer used by Handlers directly; they are managed by the SettingsController and stored in settings.GitSync.Encryption.

func (*Handlers) GetName

func (h *Handlers) GetName() string

GetName implements app.CustomHandler.

func (*Handlers) Pull

func (h *Handlers) Pull(c echo.Context) error

Pull handles POST /sync/pull/:name

func (*Handlers) Push

func (h *Handlers) Push(c echo.Context) error

Push handles POST /settings/:name/sync/push

func (*Handlers) RegisterRoutes

func (h *Handlers) RegisterRoutes(e *echo.Echo) error

RegisterRoutes implements app.CustomHandler.

func (*Handlers) RotateKey

func (h *Handlers) RotateKey(c echo.Context) error

RotateKey handles POST /sync/rotate-key/:name

func (*Handlers) SyncAll added in v1.402.0

func (h *Handlers) SyncAll(c echo.Context) error

SyncAll handles POST /sync/all (admin only). It pushes and/or pulls all settings that have GitHub sync enabled.

func (*Handlers) Syncer

func (h *Handlers) Syncer() *Syncer

Syncer returns the underlying Syncer for use by the periodic worker.

type LeaderWorker

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

LeaderWorker wraps Worker with Kubernetes leader election so only one replica in the cluster runs the periodic sync at a time.

func NewLeaderWorker

func NewLeaderWorker(
	syncer *Syncer,
	settingsRepo portrepos.SettingsRepository,
	interval time.Duration,
	client kubernetes.Interface,
	electionConfig schedule.LeaderElectionConfig,
) *LeaderWorker

NewLeaderWorker creates a LeaderWorker.

func (*LeaderWorker) Run

func (lw *LeaderWorker) Run(ctx context.Context)

Run starts the leader election loop. Only the elected leader runs the sync worker.

type PullRequest

type PullRequest struct {
	// DeleteOrphans removes local resources that no longer exist in GitHub.
	DeleteOrphans bool `json:"delete_orphans,omitempty"`
}

PullRequest is the HTTP request body for POST /sync/pull.

type PullResponse

type PullResponse struct {
	PulledAt time.Time   `json:"pulled_at"`
	Summary  SyncSummary `json:"summary"`
}

PullResponse is the HTTP response body for POST /sync/pull.

type PushRequest

type PushRequest struct {
	// CommitMessage overrides the default commit message.
	CommitMessage string `json:"commit_message,omitempty"`
}

PushRequest is the HTTP request body for POST /sync/push.

type PushResponse

type PushResponse struct {
	CommitSHA string      `json:"commit_sha"`
	PushedAt  time.Time   `json:"pushed_at"`
	Summary   SyncSummary `json:"summary"`
}

PushResponse is the HTTP response body for POST /sync/push.

type RotateKeyResponse

type RotateKeyResponse struct {
	CommitSHA  string    `json:"commit_sha"`
	RotatedAt  time.Time `json:"rotated_at"`
	DEKVersion int       `json:"dek_version"`
}

RotateKeyResponse is the HTTP response body for POST /sync/rotate-key.

type SyncAllRequest added in v1.402.0

type SyncAllRequest struct {
	DeleteOrphans bool   `json:"delete_orphans,omitempty"`
	CommitMessage string `json:"commit_message,omitempty"`
}

SyncAllRequest is the HTTP request body for POST /sync/all.

type SyncAllResponse added in v1.402.0

type SyncAllResponse struct {
	SyncedAt time.Time       `json:"synced_at"`
	Results  []SyncAllResult `json:"results"`
}

SyncAllResponse summarises the result of syncing all tenants.

type SyncAllResult added in v1.402.0

type SyncAllResult struct {
	SettingsName string        `json:"settings_name"`
	Direction    string        `json:"direction"`
	Push         *PushResponse `json:"push,omitempty"`
	Pull         *PullResponse `json:"pull,omitempty"`
	Error        string        `json:"error,omitempty"`
}

SyncAllResult holds the result for a single settings tenant. Direction is "push" or "pull", determined automatically by comparing the remote .sync-meta.yaml syncedAt against the local LastPushedAt.

type SyncEncryptor

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

SyncEncryptor manages fixed-DEK AWS KMS envelope encryption for GitHub sync. The DEK is generated once per GitSyncConfig and stored encrypted in Settings.

func NewSyncEncryptor

func NewSyncEncryptor(ctx context.Context, kmsKeyARN, awsRegion string) (*SyncEncryptor, error)

NewSyncEncryptor creates a SyncEncryptor using AWS default credentials (IRSA > env vars > ~/.aws).

func (*SyncEncryptor) DecryptDEK

func (e *SyncEncryptor) DecryptDEK(ctx context.Context, encryptedDEK string) ([]byte, error)

DecryptDEK decrypts the base64-encoded encrypted DEK stored in GitSyncConfig.

func (*SyncEncryptor) GenerateAndEncryptDEK

func (e *SyncEncryptor) GenerateAndEncryptDEK(ctx context.Context) (dek []byte, encryptedDEK string, err error)

GenerateAndEncryptDEK generates a fresh random 256-bit DEK, encrypts it with KMS, and returns both the raw DEK bytes (for immediate use) and the base64 encrypted DEK (for storage in GitSyncConfig.Encryption.EncryptedDEK).

type SyncEventRecord

type SyncEventRecord struct {
	At        time.Time   `json:"at"`
	CommitSHA string      `json:"commit_sha,omitempty"`
	Summary   SyncSummary `json:"summary"`
}

SyncEventRecord records the result of the last push or pull.

type SyncMeta

type SyncMeta struct {
	APIVersion string      `yaml:"apiVersion"`
	Kind       string      `yaml:"kind"`
	SyncedAt   time.Time   `yaml:"syncedAt"`
	Version    string      `yaml:"version"`
	Encryption SyncMetaEnc `yaml:"encryption"`
}

SyncMeta is stored as .sync-meta.yaml at the rootPath in the GitHub repository. It records the last sync timestamp and encryption parameters (NOT the DEK itself).

type SyncMetaEnc

type SyncMetaEnc struct {
	Provider   string `yaml:"provider"` // always "aws_kms"
	KMSKeyARN  string `yaml:"kmsKeyArn"`
	Algorithm  string `yaml:"algorithm"` // "AES-256-GCM"
	DEKVersion int    `yaml:"dekVersion"`
}

SyncMetaEnc holds encryption metadata stored in .sync-meta.yaml. The encryptedDEK is NOT stored here — it lives in Settings (K8s Secret).

type SyncSummary

type SyncSummary struct {
	FilesWritten int `json:"files_written"`
	FilesDeleted int `json:"files_deleted"`
}

SyncSummary summarises what happened during a push or pull.

type Syncer

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

Syncer orchestrates bidirectional GitHub sync for a user or team.

File layout in GitHub:

<rootPath>/<settingsName>/schedules/<id>.yaml
<rootPath>/<settingsName>/webhooks/<id>.yaml
<rootPath>/<settingsName>/settings.yaml
<rootPath>/<settingsName>/files/<id>.yaml (personal only)
<rootPath>/<settingsName>/slackbots/<id>.yaml
<rootPath>/<settingsName>/session-profiles/<id>.yaml
<rootPath>/<settingsName>/.sync-meta.yaml

For personal sync settingsName == userID; for team sync settingsName is the team name. Each settings has its own subdirectory under rootPath so multiple settings can share the same repository without conflicting.

func NewSyncer

NewSyncer creates a Syncer. Non-nil repos are synced. githubAppInstallID is used to generate a GitHub App installation token when a user has no personal GitHub token configured (empty string disables fallback).

func (*Syncer) Pull

func (s *Syncer) Pull(ctx context.Context, settingsName, userID string, deleteOrphans bool) (*PullResponse, error)

Pull downloads resources from GitHub and imports them.

func (*Syncer) Push

func (s *Syncer) Push(ctx context.Context, settingsName, userID string, commitMessage string) (*PushResponse, error)

Push exports all resources for settingsName and commits them to GitHub.

func (*Syncer) RotateKey

func (s *Syncer) RotateKey(ctx context.Context, settingsName, userID string) (*RotateKeyResponse, error)

RotateKey generates a fresh DEK, re-encrypts all GitHub files, and updates Settings.

func (*Syncer) SetSessionProfileRepository added in v1.416.0

func (s *Syncer) SetSessionProfileRepository(repo portrepos.SessionProfileRepository)

SetSessionProfileRepository sets the session profile repository for sync

func (*Syncer) SyncAll added in v1.402.0

func (s *Syncer) SyncAll(ctx context.Context, deleteOrphans bool, commitMessage string) (*SyncAllResponse, error)

SyncAll syncs all settings that have GitHub sync enabled. The direction (push/pull) is determined automatically per tenant by comparing the remote .sync-meta.yaml syncedAt against the local LastPushedAt: if GitHub is newer → pull; otherwise → push. Each tenant is processed independently; errors are captured in results without aborting others.

type Worker

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

Worker runs periodic bidirectional sync for all enabled git_sync configs.

Each settings is synced on a per-tenant schedule: the first sync time is offset by hash(settingsName) % interval so tenants don't hit GitHub simultaneously. Subsequent syncs happen at lastSyncedAt + interval.

Direction is determined by comparing the remote .sync-meta.yaml syncedAt timestamp against the locally stored LastPushedAt:

remoteSyncedAt > LastPushedAt  →  Pull  (GitHub is newer)
otherwise                      →  Push  (local is newer or equal)

func NewWorker

func NewWorker(syncer *Syncer, settingsRepo portrepos.SettingsRepository, interval time.Duration) *Worker

NewWorker creates a Worker. interval must be > 0.

func (*Worker) Start

func (w *Worker) Start(ctx context.Context) error

Start begins the periodic sync loop. It returns immediately; the loop runs in the background.

func (*Worker) Stop

func (w *Worker) Stop()

Stop signals the sync loop to stop and waits for it to exit.

Jump to

Keyboard shortcuts

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