Skip to content

Commit 0f72a57

Browse files
committed
initial version
1 parent 8b9af4a commit 0f72a57

File tree

3 files changed

+243
-3
lines changed

3 files changed

+243
-3
lines changed

cmd/manageDeploymentRepo.go

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
package cmd
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"time"
9+
10+
"github.com/go-git/go-git/v5"
11+
"github.com/go-git/go-git/v5/config"
12+
"github.com/go-git/go-git/v5/plumbing"
13+
"github.com/go-git/go-git/v5/plumbing/object"
14+
"github.com/spf13/cobra"
15+
16+
gitconfig "github.com/openmcp-project/bootstrapper/internal/git-config"
17+
"github.com/openmcp-project/bootstrapper/internal/template"
18+
)
19+
20+
const (
21+
FlagGitConfig = "git-config"
22+
)
23+
24+
// manageDeploymentRepoCmd represents the manageDeploymentRepo command
25+
var manageDeploymentRepoCmd = &cobra.Command{
26+
Use: "manageDeploymentRepo",
27+
Short: "A brief description of your command",
28+
Long: `A longer description that spans multiple lines and likely contains examples
29+
and usage of using your command. For example:
30+
31+
Cobra is a CLI library for Go that empowers applications.
32+
This application is a tool to generate the needed files
33+
to quickly create a Cobra application.`,
34+
Args: cobra.ExactArgs(3),
35+
ArgAliases: []string{
36+
"deploymentRepository",
37+
"deploymentRepositoryBranch",
38+
"templateDirectory",
39+
},
40+
RunE: func(cmd *cobra.Command, args []string) error {
41+
deploymentRepository := args[0]
42+
deploymentRepositoryBranch := args[1]
43+
templateDirectory := args[2]
44+
45+
gitConfig, err := gitconfig.ParseConfig(cmd.Flag(FlagGitConfig).Value.String())
46+
if err != nil {
47+
return err
48+
}
49+
if err := gitConfig.Validate(); err != nil {
50+
return err
51+
}
52+
53+
cloneOptions := &git.CloneOptions{
54+
URL: deploymentRepository,
55+
}
56+
if err := gitConfig.ConfigureCloneOptions(cloneOptions); err != nil {
57+
return err
58+
}
59+
60+
repo, tmpDir, err := CloneRepo(deploymentRepository, gitConfig)
61+
fmt.Printf("Cloned repository to temporary directory: %s\n", tmpDir)
62+
if err != nil {
63+
return fmt.Errorf("failed to clone repository: %w", err)
64+
}
65+
defer func(path string) {
66+
err := os.RemoveAll(path)
67+
if err != nil {
68+
fmt.Fprintf(os.Stderr, "failed to remove temporary directory %s: %v\n", path, err)
69+
}
70+
}(tmpDir)
71+
72+
// Check if branch exists
73+
branchExists := false
74+
branches, err := repo.Branches()
75+
if err != nil {
76+
return fmt.Errorf("failed to list branches: %w", err)
77+
}
78+
branchRefName := plumbing.NewBranchReferenceName(deploymentRepositoryBranch)
79+
branches.ForEach(func(ref *plumbing.Reference) error {
80+
if ref.Name() == branchRefName {
81+
branchExists = true
82+
}
83+
return nil
84+
})
85+
86+
workTree, err := repo.Worktree()
87+
if err != nil {
88+
return fmt.Errorf("failed to get worktree: %w", err)
89+
}
90+
91+
if !branchExists {
92+
// Create and checkout new branch
93+
fmt.Printf("Branch %s does not exist. Creating...\n", deploymentRepositoryBranch)
94+
err = workTree.Checkout(&git.CheckoutOptions{
95+
Branch: branchRefName,
96+
Create: true,
97+
})
98+
if err != nil {
99+
return fmt.Errorf("failed to create branch: %w", err)
100+
}
101+
102+
pushOptions := &git.PushOptions{
103+
RefSpecs: []config.RefSpec{
104+
config.RefSpec(branchRefName + ":" + branchRefName),
105+
},
106+
}
107+
108+
if err := gitConfig.ConfigurePushOptions(pushOptions); err != nil {
109+
return fmt.Errorf("failed to configure push options: %w", err)
110+
}
111+
112+
// Push new branch to remote
113+
err = repo.Push(pushOptions)
114+
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
115+
return fmt.Errorf("failed to push new branch: %w", err)
116+
}
117+
} else {
118+
// Checkout existing branch
119+
err = workTree.Checkout(&git.CheckoutOptions{
120+
Branch: branchRefName,
121+
})
122+
if err != nil {
123+
return fmt.Errorf("failed to checkout branch: %w", err)
124+
}
125+
}
126+
127+
// open the template directory
128+
templateDir, err := os.Open(templateDirectory)
129+
if err != nil {
130+
return fmt.Errorf("failed to open template directory: %w", err)
131+
}
132+
defer func() {
133+
if err := templateDir.Close(); err != nil {
134+
fmt.Fprintf(os.Stderr, "failed to close template directory: %v\n", err)
135+
}
136+
}()
137+
138+
te := template.NewTemplateExecution()
139+
templateInput := make(map[string]interface{})
140+
141+
// Recursively walk through all files in the template directory
142+
err = filepath.WalkDir(templateDirectory, func(path string, d os.DirEntry, err error) error {
143+
if err != nil {
144+
return err
145+
}
146+
if !d.IsDir() {
147+
// Process the file (path)
148+
fmt.Printf("Found file: %s\n", path)
149+
template, err := os.ReadFile(path)
150+
if err != nil {
151+
return fmt.Errorf("failed to read template file %s: %w", path, err)
152+
}
153+
154+
templateResult, err := te.Execute(path, string(template), templateInput)
155+
if err != nil {
156+
return fmt.Errorf("failed to execute template %s: %w", path, err)
157+
}
158+
159+
pathInRepo, err := filepath.Rel(templateDirectory, path)
160+
fileInWt, err := workTree.Filesystem.OpenFile(pathInRepo, os.O_WRONLY|os.O_CREATE, 0644)
161+
if err != nil {
162+
return fmt.Errorf("failed to open file in worktree %s: %w", path, err)
163+
}
164+
defer fileInWt.Close()
165+
_, err = fileInWt.Write(templateResult)
166+
if err != nil {
167+
return fmt.Errorf("failed to write to file in worktree %s: %w", path, err)
168+
}
169+
// Add the file to the git index
170+
if _, err := workTree.Add(pathInRepo); err != nil {
171+
return fmt.Errorf("failed to add file to git index: %w", err)
172+
}
173+
}
174+
return nil
175+
})
176+
if err != nil {
177+
return fmt.Errorf("failed to walk template directory: %w", err)
178+
}
179+
180+
// Commit the changes
181+
commitMsg := "Update deployment repo with template files"
182+
_, err = workTree.Commit(commitMsg, &git.CommitOptions{
183+
Author: &object.Signature{
184+
Name: "openmcp-bootstrapper",
185+
186+
When: time.Now(),
187+
},
188+
})
189+
if err != nil {
190+
if !errors.Is(err, git.ErrEmptyCommit) {
191+
return fmt.Errorf("failed to commit changes: %w", err)
192+
}
193+
}
194+
195+
// Push the changes
196+
pushOptions := &git.PushOptions{}
197+
if err := gitConfig.ConfigurePushOptions(pushOptions); err != nil {
198+
return fmt.Errorf("failed to configure push options: %w", err)
199+
}
200+
err = repo.Push(pushOptions)
201+
if err != nil {
202+
if !errors.Is(err, git.NoErrAlreadyUpToDate) {
203+
return fmt.Errorf("failed to push changes: %w", err)
204+
}
205+
}
206+
207+
return nil
208+
},
209+
}
210+
211+
func init() {
212+
RootCmd.AddCommand(manageDeploymentRepoCmd)
213+
214+
manageDeploymentRepoCmd.PersistentFlags().StringP("ocm-config", "", "", "ocm configuration file")
215+
manageDeploymentRepoCmd.Flags().StringP(FlagGitConfig, "", "", "Git configuration file")
216+
if err := manageDeploymentRepoCmd.MarkFlagRequired(FlagGitConfig); err != nil {
217+
panic(err)
218+
}
219+
}
220+
221+
func CloneRepo(repoURL string, config *gitconfig.Config) (*git.Repository, string, error) {
222+
cloneOptions := &git.CloneOptions{
223+
URL: repoURL,
224+
}
225+
226+
if err := config.ConfigureCloneOptions(cloneOptions); err != nil {
227+
return nil, "", err
228+
}
229+
230+
tmpDir, err := os.MkdirTemp("", "deployment-repo-")
231+
if err != nil {
232+
return nil, "", fmt.Errorf("failed to create temp dir: %w", err)
233+
}
234+
235+
repo, err := git.PlainClone(tmpDir, false, cloneOptions)
236+
if err != nil {
237+
return nil, tmpDir, fmt.Errorf("failed to clone repository: %w", err)
238+
}
239+
240+
return repo, tmpDir, nil
241+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ require (
2929
github.com/pmezard/go-difflib v1.0.0 // indirect
3030
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
3131
github.com/skeema/knownhosts v1.3.1 // indirect
32-
github.com/spf13/pflag v1.0.7 // indirect
32+
github.com/spf13/pflag v1.0.6 // indirect
3333
github.com/xanzy/ssh-agent v0.3.3 // indirect
3434
go.yaml.in/yaml/v2 v2.4.2 // indirect
3535
golang.org/x/crypto v0.37.0 // indirect

go.sum

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,8 @@ github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnB
6868
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
6969
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
7070
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
71+
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
7172
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
72-
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
73-
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
7473
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7574
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
7675
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=

0 commit comments

Comments
 (0)