Compare commits
No commits in common. "04c7b67b5d0f4c134457eeed96389341c38c6abe" and "8016a1063f36db096d03f474eefb9525405606bf" have entirely different histories.
04c7b67b5d
...
8016a1063f
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
This is the approved target architecture. Central onboarding is available for
|
This is the approved target architecture. The current source-owned onboarding
|
||||||
new applications; existing source-owned registrations remain migration work.
|
implementation is being migrated and must not be used for new applications.
|
||||||
|
|
||||||
## Trust Boundary
|
## Trust Boundary
|
||||||
|
|
||||||
|
|
@ -24,10 +24,6 @@ operators and approved automation may push or merge into it. Flux must track
|
||||||
only that branch for chart content. Flux must never track an application `main`
|
only that branch for chart content. Flux must never track an application `main`
|
||||||
branch or `maidn/delivery-*` branch.
|
branch or `maidn/delivery-*` branch.
|
||||||
|
|
||||||
Onboarding requires an existing `maidn/platform-<app>` branch and verifies its
|
|
||||||
no-direct-push protection before it opens the central registration PR. It never
|
|
||||||
seeds a platform branch from developer-controlled `main`.
|
|
||||||
|
|
||||||
## Resource Flow
|
## Resource Flow
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,8 @@ go run . bootstrap init --config <private-bootstrap-config> --organization <owne
|
||||||
The source-owned onboarding implementation is retired. Central onboarding
|
The source-owned onboarding implementation is retired. Central onboarding
|
||||||
creates a reviewed cluster registration only; it never writes `.tekton` or
|
creates a reviewed cluster registration only; it never writes `.tekton` or
|
||||||
`.maidn` resources to an application repository. Before merging that
|
`.maidn` resources to an application repository. Before merging that
|
||||||
registration, a platform operator must create the corresponding
|
registration, a platform operator must create and protect the corresponding
|
||||||
`maidn/platform-<app>` package through a reviewed platform PR; onboarding
|
`maidn/platform-<app>` package through a reviewed platform PR. See [Delivery
|
||||||
verifies its existence and enforces its protection. See [Delivery
|
|
||||||
Ownership](architecture/delivery-ownership.md) for the approved architecture
|
Ownership](architecture/delivery-ownership.md) for the approved architecture
|
||||||
and migration rules.
|
and migration rules.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,9 +57,6 @@ func OnboardApp(cfg config.Config, sourceDir string) error {
|
||||||
if err := sourceManager.EnsureProtectedBranch(repository, resolved.Delivery.ProductionBranch); err != nil {
|
if err := sourceManager.EnsureProtectedBranch(repository, resolved.Delivery.ProductionBranch); err != nil {
|
||||||
return fmt.Errorf("protect Forgejo production branch: %w", err)
|
return fmt.Errorf("protect Forgejo production branch: %w", err)
|
||||||
}
|
}
|
||||||
if err := ensurePlatformBranch(sourceManager, repository, resolved.Delivery.AppRepoURL, resolved.Delivery.AppName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
registrationBranch := "maidn/register-" + resolved.Delivery.AppName
|
registrationBranch := "maidn/register-" + resolved.Delivery.AppName
|
||||||
clusterManager := newOnboardingRepoManager(resolved.Git.BaseURL, resolved.Git.Token, resolved.Git.Owner, resolved.Git.Username, "", "", resolved.Flux.Branch, "")
|
clusterManager := newOnboardingRepoManager(resolved.Git.BaseURL, resolved.Git.Token, resolved.Git.Owner, resolved.Git.Username, "", "", resolved.Flux.Branch, "")
|
||||||
if _, err := clusterManager.PublishRepositoryPullRequest(resolved.Flux.RepoName, "feat: register "+resolved.Delivery.AppName+" delivery", registrationBranch, resolved.Flux.Branch, func(dir string) error {
|
if _, err := clusterManager.PublishRepositoryPullRequest(resolved.Flux.RepoName, "feat: register "+resolved.Delivery.AppName+" delivery", registrationBranch, resolved.Flux.Branch, func(dir string) error {
|
||||||
|
|
@ -86,25 +83,6 @@ func OnboardApp(cfg config.Config, sourceDir string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func platformBranch(appName string) string {
|
|
||||||
return "maidn/platform-" + appName
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensurePlatformBranch(manager onboardingRepoManager, repository, repositoryURL, appName string) error {
|
|
||||||
branch := platformBranch(appName)
|
|
||||||
revision, err := manager.RemoteBranchRevision(repositoryURL, branch)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("read Forgejo platform branch: %w", err)
|
|
||||||
}
|
|
||||||
if revision == "" {
|
|
||||||
return fmt.Errorf("approved Forgejo platform branch %q must exist before central registration", branch)
|
|
||||||
}
|
|
||||||
if err := manager.EnsureProtectedBranch(repository, branch); err != nil {
|
|
||||||
return fmt.Errorf("protect Forgejo platform branch: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// publishInitialAppBranches establishes the immutable source baseline before central registration.
|
// publishInitialAppBranches establishes the immutable source baseline before central registration.
|
||||||
func publishInitialAppBranches(manager onboardingRepoManager, sourceDir, targetURL, sourceBranch, targetBranch, productionBranch string) error {
|
func publishInitialAppBranches(manager onboardingRepoManager, sourceDir, targetURL, sourceBranch, targetBranch, productionBranch string) error {
|
||||||
sourceRevision, err := forgejo.BranchRevision(sourceDir, sourceBranch)
|
sourceRevision, err := forgejo.BranchRevision(sourceDir, sourceBranch)
|
||||||
|
|
@ -231,8 +209,8 @@ spec:
|
||||||
secretRef:
|
secretRef:
|
||||||
name: forgejo-flux-credentials
|
name: forgejo-flux-credentials
|
||||||
ref:
|
ref:
|
||||||
branch: %s
|
branch: maidn/platform-%s
|
||||||
`, cfg.Delivery.AppName, cfg.Delivery.AppRepoURL, platformBranch(cfg.Delivery.AppName))
|
`, cfg.Delivery.AppName, cfg.Delivery.AppRepoURL, cfg.Delivery.AppName)
|
||||||
if len(secretAccess) != 0 {
|
if len(secretAccess) != 0 {
|
||||||
content += "---\n" + string(secretAccess)
|
content += "---\n" + string(secretAccess)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,6 @@ func TestPublishInitialAppBranchesCreatesAndPreservesProduction(t *testing.T) {
|
||||||
type onboardingManagerFake struct {
|
type onboardingManagerFake struct {
|
||||||
owner string
|
owner string
|
||||||
calls []string
|
calls []string
|
||||||
remoteRevisions map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *onboardingManagerFake) EnsureRepository(repo, _ string) (bool, error) {
|
func (m *onboardingManagerFake) EnsureRepository(repo, _ string) (bool, error) {
|
||||||
|
|
@ -170,9 +169,6 @@ func (m *onboardingManagerFake) EnsureRepository(repo, _ string) (bool, error) {
|
||||||
|
|
||||||
func (m *onboardingManagerFake) RemoteBranchRevision(targetURL, branch string) (string, error) {
|
func (m *onboardingManagerFake) RemoteBranchRevision(targetURL, branch string) (string, error) {
|
||||||
m.calls = append(m.calls, "remote "+targetURL+":"+branch)
|
m.calls = append(m.calls, "remote "+targetURL+":"+branch)
|
||||||
if m.remoteRevisions != nil {
|
|
||||||
return m.remoteRevisions[branch], nil
|
|
||||||
}
|
|
||||||
return "existing", nil
|
return "existing", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,8 +255,6 @@ func TestOnboardAppUsesCanonicalSourceAndCentralClusterManagers(t *testing.T) {
|
||||||
"remote https://git.example.test/Maidn/maidn-e2e-web.git:main",
|
"remote https://git.example.test/Maidn/maidn-e2e-web.git:main",
|
||||||
"remote https://git.example.test/Maidn/maidn-e2e-web.git:production",
|
"remote https://git.example.test/Maidn/maidn-e2e-web.git:production",
|
||||||
"protect Maidn/maidn-e2e-web:production",
|
"protect Maidn/maidn-e2e-web:production",
|
||||||
"remote https://git.example.test/Maidn/maidn-e2e-web.git:maidn/platform-web-ui",
|
|
||||||
"protect Maidn/maidn-e2e-web:maidn/platform-web-ui",
|
|
||||||
} {
|
} {
|
||||||
if !strings.Contains(sourceCalls, want) {
|
if !strings.Contains(sourceCalls, want) {
|
||||||
t.Fatalf("source manager calls = %q, missing %q", sourceCalls, want)
|
t.Fatalf("source manager calls = %q, missing %q", sourceCalls, want)
|
||||||
|
|
@ -273,14 +267,3 @@ func TestOnboardAppUsesCanonicalSourceAndCentralClusterManagers(t *testing.T) {
|
||||||
t.Fatalf("cluster manager calls = %q", got)
|
t.Fatalf("cluster manager calls = %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsurePlatformBranchRequiresExistingBranch(t *testing.T) {
|
|
||||||
manager := &onboardingManagerFake{owner: "Maidn", remoteRevisions: map[string]string{}}
|
|
||||||
err := ensurePlatformBranch(manager, "maidn-e2e-web", "https://git.example.test/Maidn/maidn-e2e-web.git", "web-ui")
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "maidn/platform-web-ui") {
|
|
||||||
t.Fatalf("ensurePlatformBranch() error = %v", err)
|
|
||||||
}
|
|
||||||
if strings.Contains(strings.Join(manager.calls, "\n"), "protect") {
|
|
||||||
t.Fatalf("missing platform branch was protected: %q", manager.calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -780,10 +780,10 @@ func (rm *RepoManager) TriggerWebhookTest(repo, webhookURL, branch string) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureProtectedBranch disables direct pushes to a managed branch.
|
// EnsureProtectedBranch disables direct pushes to the configured production branch.
|
||||||
func (rm *RepoManager) EnsureProtectedBranch(repo, branch string) error {
|
func (rm *RepoManager) EnsureProtectedBranch(repo, branch string) error {
|
||||||
if repo == "" || branch == "" {
|
if repo == "" || branch == "" {
|
||||||
return errors.New("Forgejo repository and branch are required")
|
return errors.New("Forgejo repository and production branch are required")
|
||||||
}
|
}
|
||||||
endpoint := fmt.Sprintf("%s/api/v1/repos/%s/%s/branch_protections", rm.BaseURL, rm.Owner, repo)
|
endpoint := fmt.Sprintf("%s/api/v1/repos/%s/%s/branch_protections", rm.BaseURL, rm.Owner, repo)
|
||||||
var protections []branchProtection
|
var protections []branchProtection
|
||||||
|
|
@ -801,11 +801,11 @@ func (rm *RepoManager) EnsureProtectedBranch(repo, branch string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(matching) > 1 {
|
if len(matching) > 1 {
|
||||||
return fmt.Errorf("multiple Forgejo branch protections match branch %q", branch)
|
return fmt.Errorf("multiple Forgejo branch protections match production branch %q", branch)
|
||||||
}
|
}
|
||||||
if len(matching) == 1 {
|
if len(matching) == 1 {
|
||||||
if matching[0].EnablePush || matching[0].EnablePushWhitelist {
|
if matching[0].EnablePush || matching[0].EnablePushWhitelist {
|
||||||
return fmt.Errorf("Forgejo branch %q permits direct pushes", branch)
|
return fmt.Errorf("Forgejo production branch %q permits direct pushes", branch)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue