feat(project_create): mirror_to_github opt-in, default false (infra#34 ADR)
Per the Gitea-as-true-master ADR (infra#34), GitHub mirror is now an
explicit opt-in via mirror_to_github=true. Default (omit / false) provisions
a Gitea repo + staging namespace + experiment-brief issue only — no GitHub
repo, no push-mirror.
Rationale: US cloud providers (Microsoft/GitHub) are subject to CLOUD Act
and NSL. Client code, business logic, and infra-adjacent repos should
never live on US-owned infrastructure. Only open-source projects intended
for public community (hyperguild, gitea-mcp, template-*) should opt in.
Changes
- internal/skills/project/handlers.go
- createArgs gains MirrorToGitHub bool (json:"mirror_to_github,omitempty").
- res.GitHubURL is set only when MirrorToGitHub is true; empty string otherwise.
- Steps 2 (create_github_repo) + 3 (mirror) are wrapped in `if args.MirrorToGitHub`.
- experimentBrief renders "Gitea-only" line by default and the existing
"Push-mirror configured" line only on opt-in.
- internal/skills/project/skill.go
- Tool schema gains mirror_to_github (boolean, default false) with description
spelling out when to opt in. Tool Description updated to reflect new default.
- internal/skills/project/handlers_test.go
- Added mirroredArgs() helper (happyArgs + mirror_to_github:true).
- Tests that exercise the GitHub flow (HappyPath, GitHubExists_Idempotent,
GitHubFails, NoGitHubClient_DegradedMode, Idempotent_RepoExists,
MirrorFails, InfraCommitFails) switched to mirroredArgs.
- Added TestProjectCreate_DefaultSkipsGitHubMirror covering the Gitea-only
path: 3 gitea-mcp calls, zero GitHub calls, empty github_url, reached=
[create_repo, infra_commit, issue], body reflects Gitea-only.
Closes gitea/mathias/hyperguild#17. Moves infra#34 acceptance item
"project_create updated: mirror_to_github defaults to false".
This commit is contained in:
@@ -13,12 +13,13 @@ import (
|
||||
)
|
||||
|
||||
type createArgs struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Hypothesis string `json:"hypothesis"`
|
||||
Folder string `json:"folder"`
|
||||
Stack string `json:"stack"`
|
||||
Private bool `json:"private"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Hypothesis string `json:"hypothesis"`
|
||||
Folder string `json:"folder"`
|
||||
Stack string `json:"stack"`
|
||||
Private bool `json:"private"`
|
||||
MirrorToGitHub bool `json:"mirror_to_github,omitempty"`
|
||||
}
|
||||
|
||||
type createResult struct {
|
||||
@@ -59,11 +60,12 @@ func (s *Skill) handleCreate(ctx context.Context, raw json.RawMessage) (json.Raw
|
||||
|
||||
tmpl := templateFor(args.Stack)
|
||||
giteaURL := fmt.Sprintf("http://gitea.d-ma.be/%s/%s", s.cfg.GiteaOwner, args.Name)
|
||||
githubURL := fmt.Sprintf("https://github.com/%s/%s", s.cfg.GitHubOwner, args.Name)
|
||||
|
||||
res := createResult{
|
||||
GiteaURL: giteaURL,
|
||||
GitHubURL: githubURL,
|
||||
GiteaURL: giteaURL,
|
||||
}
|
||||
if args.MirrorToGitHub {
|
||||
res.GitHubURL = fmt.Sprintf("https://github.com/%s/%s", s.cfg.GitHubOwner, args.Name)
|
||||
}
|
||||
|
||||
// Step 1: create_project_from_template. If the repo already exists,
|
||||
@@ -75,25 +77,32 @@ func (s *Skill) handleCreate(ctx context.Context, raw json.RawMessage) (json.Raw
|
||||
}
|
||||
res.Reached = append(res.Reached, stepCreateRepo)
|
||||
|
||||
// Step 2: create empty GitHub repo. Gitea's push-mirror cannot push
|
||||
// to a non-existent remote, so the destination must exist before
|
||||
// step 3 configures the mirror. Skipped when GitHub client is unset
|
||||
// (degraded mode — see Config.GitHub doc).
|
||||
if s.cfg.GitHub != nil {
|
||||
if err := s.callCreateGitHubRepo(ctx, args); err != nil && !errors.Is(err, githubclient.ErrAlreadyExists) {
|
||||
return marshalPartial(res, stepCreateGitHub, err)
|
||||
// Steps 2+3 are skipped when MirrorToGitHub is false. Default per
|
||||
// infra ADR (Gitea as true master, GitHub as optional opt-in): keep
|
||||
// client / business-logic / personal repos Gitea-only. Set
|
||||
// `mirror_to_github: true` for open-source projects that want a
|
||||
// public GitHub mirror (hyperguild, gitea-mcp, template-*).
|
||||
if args.MirrorToGitHub {
|
||||
// Step 2: create empty GitHub repo. Gitea's push-mirror cannot push
|
||||
// to a non-existent remote, so the destination must exist before
|
||||
// step 3 configures the mirror. Skipped when GitHub client is unset
|
||||
// (degraded mode — see Config.GitHub doc).
|
||||
if s.cfg.GitHub != nil {
|
||||
if err := s.callCreateGitHubRepo(ctx, args); err != nil && !errors.Is(err, githubclient.ErrAlreadyExists) {
|
||||
return marshalPartial(res, stepCreateGitHub, err)
|
||||
}
|
||||
res.Reached = append(res.Reached, stepCreateGitHub)
|
||||
}
|
||||
res.Reached = append(res.Reached, stepCreateGitHub)
|
||||
}
|
||||
|
||||
// Step 3: configure push mirror to GitHub. Idempotent: if a mirror with
|
||||
// the same remote already exists, gitea-mcp returns Conflict; we swallow it.
|
||||
if err := s.callMirror(ctx, args.Name); err != nil {
|
||||
if !isConflict(err) {
|
||||
return marshalPartial(res, stepMirror, err)
|
||||
// Step 3: configure push mirror to GitHub. Idempotent: if a mirror with
|
||||
// the same remote already exists, gitea-mcp returns Conflict; we swallow it.
|
||||
if err := s.callMirror(ctx, args.Name); err != nil {
|
||||
if !isConflict(err) {
|
||||
return marshalPartial(res, stepMirror, err)
|
||||
}
|
||||
}
|
||||
res.Reached = append(res.Reached, stepMirror)
|
||||
}
|
||||
res.Reached = append(res.Reached, stepMirror)
|
||||
|
||||
// Step 3: commit staging namespace manifest to infra repo. Done before
|
||||
// the issue so the staging env is reconciling by the time the issue lands.
|
||||
@@ -228,7 +237,11 @@ func experimentBrief(args createArgs, existed bool) string {
|
||||
b.WriteString("- Repo created from `template-")
|
||||
b.WriteString(args.Stack)
|
||||
b.WriteString("` on Gitea.\n")
|
||||
b.WriteString("- Push-mirror configured to GitHub.\n")
|
||||
if args.MirrorToGitHub {
|
||||
b.WriteString("- Push-mirror configured to GitHub.\n")
|
||||
} else {
|
||||
b.WriteString("- Gitea-only (no GitHub mirror — set `mirror_to_github: true` to opt in).\n")
|
||||
}
|
||||
b.WriteString("- Staging namespace manifest committed to infra repo.\n\n")
|
||||
if existed {
|
||||
b.WriteString("> Note: this repo already existed when `project_create` ran — provisioning steps were re-applied idempotently.\n")
|
||||
|
||||
Reference in New Issue
Block a user