feat: publish app delivery branches
This commit is contained in:
parent
72fbbca0ac
commit
a45618d704
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/Pingu-Studio/MaidnCLI/internal/bootstrap"
|
"github.com/Pingu-Studio/MaidnCLI/internal/bootstrap"
|
||||||
"github.com/Pingu-Studio/MaidnCLI/internal/config"
|
"github.com/Pingu-Studio/MaidnCLI/internal/config"
|
||||||
|
"github.com/Pingu-Studio/MaidnCLI/internal/forgejo"
|
||||||
"github.com/Pingu-Studio/MaidnCLI/internal/openbao"
|
"github.com/Pingu-Studio/MaidnCLI/internal/openbao"
|
||||||
"github.com/Pingu-Studio/MaidnCLI/internal/ui"
|
"github.com/Pingu-Studio/MaidnCLI/internal/ui"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
@ -17,6 +18,7 @@ var bootstrapYes bool
|
||||||
var bootstrapPromptDemocraticCSI bool
|
var bootstrapPromptDemocraticCSI bool
|
||||||
var bootstrapPromptOperationalSecrets bool
|
var bootstrapPromptOperationalSecrets bool
|
||||||
var bootstrapInitializeOpenBaoRecovery bool
|
var bootstrapInitializeOpenBaoRecovery bool
|
||||||
|
var bootstrapPublishAppFrom string
|
||||||
|
|
||||||
var bootstrapCmd = &cobra.Command{
|
var bootstrapCmd = &cobra.Command{
|
||||||
Use: "bootstrap",
|
Use: "bootstrap",
|
||||||
|
|
@ -33,11 +35,34 @@ func init() {
|
||||||
bootstrapCmd.Flags().BoolVar(&bootstrapPromptDemocraticCSI, "prompt-democratic-csi", false, "Prompt for and save Democratic CSI settings in --config")
|
bootstrapCmd.Flags().BoolVar(&bootstrapPromptDemocraticCSI, "prompt-democratic-csi", false, "Prompt for and save Democratic CSI settings in --config")
|
||||||
bootstrapCmd.Flags().BoolVar(&bootstrapPromptOperationalSecrets, "prompt-operational-secrets", false, "Prompt for and encrypt operational secrets for --config")
|
bootstrapCmd.Flags().BoolVar(&bootstrapPromptOperationalSecrets, "prompt-operational-secrets", false, "Prompt for and encrypt operational secrets for --config")
|
||||||
bootstrapCmd.Flags().BoolVar(&bootstrapInitializeOpenBaoRecovery, "initialize-openbao-recovery", false, "Create and save a separate OpenBao recovery age identity for --config")
|
bootstrapCmd.Flags().BoolVar(&bootstrapInitializeOpenBaoRecovery, "initialize-openbao-recovery", false, "Create and save a separate OpenBao recovery age identity for --config")
|
||||||
|
bootstrapCmd.Flags().StringVar(&bootstrapPublishAppFrom, "publish-app-from", "", "Push this app checkout's current branch and create a Forgejo delivery PR")
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBootstrap(cmd *cobra.Command, args []string) error {
|
func runBootstrap(cmd *cobra.Command, args []string) error {
|
||||||
var cfg config.Config
|
var cfg config.Config
|
||||||
var err error
|
var err error
|
||||||
|
if bootstrapPublishAppFrom != "" {
|
||||||
|
if bootstrapConfigPath == "" {
|
||||||
|
return fmt.Errorf("--publish-app-from requires --config")
|
||||||
|
}
|
||||||
|
cfg, err = config.Load(bootstrapConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
branch, err := forgejo.CurrentBranch(bootstrapPublishAppFrom)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
owner, repo, err := forgejo.RepositoryFromURL(cfg.Delivery.AppRepoURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
manager := forgejo.NewRepoManager(cfg.Git.BaseURL, cfg.Git.Token, owner, cfg.Git.Username, "", "", cfg.Delivery.AppRepoRef, "")
|
||||||
|
if err := manager.PushBranch(bootstrapPublishAppFrom, cfg.Delivery.AppRepoURL, branch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return manager.CreatePullRequest(repo, "feat: migrate delivery to Tekton", branch, cfg.Delivery.AppRepoRef)
|
||||||
|
}
|
||||||
|
|
||||||
if bootstrapConfigPath != "" {
|
if bootstrapConfigPath != "" {
|
||||||
if bootstrapPromptDemocraticCSI || bootstrapPromptOperationalSecrets || bootstrapInitializeOpenBaoRecovery {
|
if bootstrapPromptDemocraticCSI || bootstrapPromptOperationalSecrets || bootstrapInitializeOpenBaoRecovery {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -163,7 +164,11 @@ func (rm *RepoManager) setupRepository(repoURL, repoName string, existing bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rm *RepoManager) createMigrationPullRequest(repo, branch string) error {
|
func (rm *RepoManager) createMigrationPullRequest(repo, branch string) error {
|
||||||
body, err := json.Marshal(pullRequestRequest{Title: "feat: bootstrap Maidn CI/CD structure", Head: branch, Base: rm.Branch})
|
return rm.CreatePullRequest(repo, "feat: bootstrap Maidn CI/CD structure", branch, rm.Branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *RepoManager) CreatePullRequest(repo, title, head, base string) error {
|
||||||
|
body, err := json.Marshal(pullRequestRequest{Title: title, Head: head, Base: base})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -177,6 +182,40 @@ func (rm *RepoManager) createMigrationPullRequest(repo, branch string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rm *RepoManager) PushBranch(dir, repoURL, branch string) error {
|
||||||
|
cleanupAskPass, environment, err := rm.gitEnvironment(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cleanupAskPass()
|
||||||
|
return runGit(dir, environment, "push", repoURL, "HEAD:refs/heads/"+branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CurrentBranch(dir string) (string, error) {
|
||||||
|
command := exec.Command("git", "branch", "--show-current")
|
||||||
|
command.Dir = dir
|
||||||
|
branch, err := command.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if value := strings.TrimSpace(string(branch)); value != "" {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("source repository is in detached HEAD state")
|
||||||
|
}
|
||||||
|
|
||||||
|
func RepositoryFromURL(repoURL string) (string, string, error) {
|
||||||
|
parsed, err := url.Parse(repoURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
parts := strings.Split(strings.Trim(strings.TrimSuffix(parsed.Path, ".git"), "/"), "/")
|
||||||
|
if len(parts) < 2 || parts[len(parts)-2] == "" || parts[len(parts)-1] == "" {
|
||||||
|
return "", "", fmt.Errorf("invalid Forgejo repository URL")
|
||||||
|
}
|
||||||
|
return parts[len(parts)-2], parts[len(parts)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
func (rm *RepoManager) EnsureWebhook(repo, webhookURL, authorization string) error {
|
func (rm *RepoManager) EnsureWebhook(repo, webhookURL, authorization string) error {
|
||||||
if webhookURL == "" || authorization == "" {
|
if webhookURL == "" || authorization == "" {
|
||||||
return fmt.Errorf("Forgejo webhook URL and authorization are required")
|
return fmt.Errorf("Forgejo webhook URL and authorization are required")
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,13 @@ func TestEnsureWebhookCreatesMissingWebhook(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRepositoryFromURL(t *testing.T) {
|
||||||
|
owner, repo, err := RepositoryFromURL("https://git.example.test/team/app.git")
|
||||||
|
if err != nil || owner != "team" || repo != "app" {
|
||||||
|
t.Fatalf("RepositoryFromURL() = (%q, %q, %v)", owner, repo, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRepoExistsReturnsFalseOnNotFound(t *testing.T) {
|
func TestRepoExistsReturnsFalseOnNotFound(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||||
writer.WriteHeader(http.StatusNotFound)
|
writer.WriteHeader(http.StatusNotFound)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue