Skip to content

Commit 3824376

Browse files
committed
feat: extra gemini twig into own command
1 parent 937b7ab commit 3824376

File tree

3 files changed

+215
-212
lines changed

3 files changed

+215
-212
lines changed

Diff for: README.md

-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ Instead of running all tools, you can choose to run specific tools using the `--
9090
- `phpstan` - PHP static analysis
9191
- `sw-cli` - Shopware CLI validation checks
9292
- `stylelint` - CSS/SCSS linting
93-
- `twig` - Twig template checks
9493
- `admin-twig` - Admin Twig template checks
9594
- `php-cs-fixer` - PHP code style fixing
9695
- `prettier` - Code formatting

Diff for: cmd_twig_upgrade.go

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"os/exec"
8+
"path"
9+
"path/filepath"
10+
"strings"
11+
"time"
12+
13+
"github.com/google/generative-ai-go/genai"
14+
"github.com/shopware/extension-verifier/internal/tool"
15+
"github.com/shopware/extension-verifier/internal/twig"
16+
"github.com/shopware/shopware-cli/extension"
17+
"github.com/spf13/cobra"
18+
"google.golang.org/api/option"
19+
)
20+
21+
var twigUpgradeCommand = &cobra.Command{
22+
Use: "twig-upgrade [path] [old-shopware-version] [new-shopware-version]",
23+
Short: "Experimental upgrade of Twig templates using AI",
24+
Args: cobra.ExactArgs(3),
25+
RunE: func(cmd *cobra.Command, args []string) error {
26+
ext, err := extension.GetExtensionByFolder(args[0])
27+
28+
if err != nil {
29+
return err
30+
}
31+
32+
toolCfg, err := tool.ConvertExtensionToToolConfig(ext)
33+
34+
if err != nil {
35+
return err
36+
}
37+
38+
apiKey := os.Getenv("GEMINI_API_KEY")
39+
40+
if apiKey == "" {
41+
return fmt.Errorf("GEMINI_API_KEY is not set")
42+
}
43+
44+
client, err := genai.NewClient(cmd.Context(), option.WithAPIKey(apiKey))
45+
46+
if err != nil {
47+
return err
48+
}
49+
50+
for _, sourceDirectory := range toolCfg.SourceDirectories {
51+
twigFolder := path.Join(sourceDirectory, "Resources", "views", "storefront")
52+
53+
if _, err := os.Stat(twigFolder); os.IsNotExist(err) {
54+
return nil
55+
}
56+
57+
oldVersion, err := cloneShopwareStorefront(args[1])
58+
59+
if err != nil {
60+
return err
61+
}
62+
63+
newVersion, err := cloneShopwareStorefront(args[2])
64+
65+
if err != nil {
66+
return err
67+
}
68+
69+
defer func() {
70+
if err := os.RemoveAll(oldVersion); err != nil {
71+
fmt.Fprintf(os.Stderr, "Failed to remove old version directory: %v\n", err)
72+
}
73+
}()
74+
defer func() {
75+
if err := os.RemoveAll(newVersion); err != nil {
76+
fmt.Fprintf(os.Stderr, "Failed to remove new version directory: %v\n", err)
77+
}
78+
}()
79+
80+
_ = filepath.Walk(twigFolder, func(file string, info os.FileInfo, _ error) error {
81+
if info.IsDir() {
82+
return nil
83+
}
84+
85+
if filepath.Ext(file) != ".twig" {
86+
return nil
87+
}
88+
89+
content, err := os.ReadFile(file)
90+
91+
if err != nil {
92+
return err
93+
}
94+
95+
ast, err := twig.ParseTemplate(string(content))
96+
97+
if err != nil {
98+
return err
99+
}
100+
101+
extends := ast.Extends()
102+
103+
if extends == nil {
104+
return nil
105+
}
106+
107+
tpl := extends.Template
108+
109+
if tpl[0] == '@' {
110+
tplParts := strings.Split(tpl, "/")
111+
tplParts = tplParts[1:]
112+
tpl = strings.Join(tplParts, "/")
113+
}
114+
115+
oldTemplateText, err := os.ReadFile(path.Join(oldVersion, "Resources", "views", tpl))
116+
117+
if err != nil {
118+
fmt.Printf("Template %s not found in old version\n", tpl)
119+
return nil
120+
}
121+
122+
newTemplateText, err := os.ReadFile(path.Join(newVersion, "Resources", "views", tpl))
123+
124+
if err != nil {
125+
fmt.Printf("Template %s not found in new version\n", tpl)
126+
return nil
127+
}
128+
129+
var str strings.Builder
130+
str.WriteString("You are a helper agent to help to upgrade Twig templates. I will give you the old and new template happend in the Software and as third the extended template. Apply the changes happen between old and new template to the extended template.\n")
131+
str.WriteString("Follow following rules while making adjustments to the extended template:\n")
132+
str.WriteString("- Do only the necessary changes to the extended template.\n")
133+
str.WriteString("- Do only modify the content inside the block and dont add new blocks\n")
134+
str.WriteString("- Please also only output the modified extended template nothing more.\n")
135+
str.WriteString("- Adjust also HTML elements to be more accessibility friendly.\n")
136+
str.WriteString("- If in a {% block %} is {{ parent() }}, ignore it and dont modify the content of the block\n")
137+
str.WriteString("\n")
138+
str.WriteString("This was the old template:\n")
139+
str.WriteString("```twig\n")
140+
str.WriteString(string(oldTemplateText))
141+
str.WriteString("\n```\n")
142+
str.WriteString("and this is the new one:\n")
143+
str.WriteString("```twig\n")
144+
str.WriteString(string(newTemplateText))
145+
str.WriteString("\n```\n")
146+
str.WriteString("and this is my template:\n")
147+
str.WriteString("```twig\n")
148+
str.WriteString(string(content))
149+
str.WriteString("\n```")
150+
151+
resp, err := generateContent(cmd.Context(), client, str.String())
152+
153+
if err != nil {
154+
return err
155+
}
156+
157+
text := string(resp.Candidates[0].Content.Parts[0].(genai.Text))
158+
159+
start := strings.Index(text, "```twig")
160+
end := strings.LastIndex(text, "```")
161+
162+
if start == -1 || end == -1 {
163+
return nil
164+
}
165+
166+
text = strings.TrimPrefix(text[start+7:end], "\n")
167+
168+
contentStr := string(content)
169+
if strings.TrimSpace(text) == strings.TrimSpace(contentStr) {
170+
return nil
171+
}
172+
173+
return os.WriteFile(file, []byte(text), os.ModePerm)
174+
})
175+
}
176+
return nil
177+
},
178+
}
179+
180+
func generateContent(ctx context.Context, client *genai.Client, message string) (*genai.GenerateContentResponse, error) {
181+
resp, err := client.GenerativeModel("gemini-2.0-pro-exp-02-05").GenerateContent(ctx, genai.Text(message))
182+
183+
if err != nil {
184+
if strings.Contains(err.Error(), "Resource has been exhausted") {
185+
fmt.Println("Resource exhausted, waiting 15 seconds before retrying")
186+
time.Sleep(15 * time.Second)
187+
188+
return generateContent(ctx, client, message)
189+
}
190+
}
191+
192+
return resp, err
193+
}
194+
195+
func cloneShopwareStorefront(version string) (string, error) {
196+
tempDir, err := os.MkdirTemp(os.TempDir(), "shopware")
197+
198+
if err != nil {
199+
return "", err
200+
}
201+
202+
git := exec.Command("git", "clone", "--branch", "v"+version, "https://github.com/shopware/storefront", tempDir, "--depth", "1")
203+
git.Stdout = os.Stdout
204+
git.Stderr = os.Stderr
205+
206+
if err := git.Run(); err != nil {
207+
return "", err
208+
}
209+
210+
return tempDir, nil
211+
}
212+
213+
func init() {
214+
rootCmd.AddCommand(twigUpgradeCommand)
215+
}

0 commit comments

Comments
 (0)