Skip to content

Commit 5f33986

Browse files
committed
Align CI Go version and harden GitHub URL parsing
1 parent 999034d commit 5f33986

File tree

3 files changed

+60
-16
lines changed

3 files changed

+60
-16
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ on:
99
jobs:
1010
build:
1111
runs-on: ubuntu-latest
12+
env:
13+
GOTOOLCHAIN: local
1214
steps:
1315
- uses: actions/checkout@v4
1416

1517
- name: Set up Go
1618
uses: actions/setup-go@v5
1719
with:
18-
go-version: '1.21'
20+
go-version: '1.25.x'
1921

2022
- name: Build
2123
run: go build -v ./...
@@ -25,13 +27,15 @@ jobs:
2527

2628
lint:
2729
runs-on: ubuntu-latest
30+
env:
31+
GOTOOLCHAIN: local
2832
steps:
2933
- uses: actions/checkout@v4
3034

3135
- name: Set up Go
3236
uses: actions/setup-go@v5
3337
with:
34-
go-version: '1.21'
38+
go-version: '1.25.x'
3539

3640
- name: golangci-lint
3741
uses: golangci/golangci-lint-action@v4

internal/config/config.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package config
22

33
import (
44
"encoding/json"
5+
"fmt"
56
"os"
67
"path/filepath"
78
)
@@ -42,7 +43,10 @@ func Load() *Config {
4243
return cfg
4344
}
4445

45-
json.Unmarshal(data, cfg)
46+
if err := json.Unmarshal(data, cfg); err != nil {
47+
fmt.Fprintln(os.Stderr, "Warning: failed to parse config file, using defaults:", err)
48+
return cfg
49+
}
4650
return cfg
4751
}
4852

internal/github/github.go

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"net/http"
8+
"net/url"
89
"os"
910
"path/filepath"
1011
"regexp"
@@ -19,6 +20,7 @@ type RepoInfo struct {
1920
Repo string
2021
Path string // subdirectory path (for monorepo skills)
2122
Branch string
23+
TreeRef string
2224
FullURL string
2325
CloneURL string
2426
}
@@ -38,14 +40,43 @@ func ParseGitHubURL(input string) (*RepoInfo, error) {
3840

3941
// Full GitHub URL
4042
if strings.HasPrefix(input, "https://github.com/") {
41-
// Pattern: https://github.com/owner/repo/tree/branch/path
42-
treePattern := regexp.MustCompile(`https://github\.com/([^/]+)/([^/]+)/tree/([^/]+)(?:/(.+))?`)
43-
if matches := treePattern.FindStringSubmatch(input); len(matches) >= 4 {
44-
info.Owner = matches[1]
45-
info.Repo = matches[2]
46-
info.Branch = matches[3]
47-
if len(matches) > 4 {
48-
info.Path = matches[4]
43+
raw := input
44+
if idx := strings.IndexAny(raw, "?#"); idx != -1 {
45+
raw = raw[:idx]
46+
}
47+
48+
if strings.Contains(raw, "/tree/") {
49+
u, err := url.Parse(raw)
50+
if err != nil {
51+
return nil, fmt.Errorf("invalid GitHub URL format: %s", input)
52+
}
53+
54+
parts := strings.Split(strings.Trim(u.Path, "/"), "/")
55+
if len(parts) < 4 || parts[2] != "tree" {
56+
return nil, fmt.Errorf("invalid GitHub URL format: %s", input)
57+
}
58+
59+
info.Owner = parts[0]
60+
info.Repo = parts[1]
61+
62+
treeParts := parts[3:]
63+
if len(treeParts) == 0 {
64+
return nil, fmt.Errorf("invalid GitHub URL format: %s", input)
65+
}
66+
67+
info.TreeRef = strings.Join(treeParts, "/")
68+
if strings.Contains(info.TreeRef, "%2F") || strings.Contains(info.TreeRef, "%2f") {
69+
decoded, err := url.PathUnescape(info.TreeRef)
70+
if err != nil {
71+
return nil, fmt.Errorf("invalid GitHub URL format: %s", input)
72+
}
73+
info.Branch = decoded
74+
info.Path = ""
75+
} else {
76+
info.Branch = treeParts[0]
77+
if len(treeParts) > 1 {
78+
info.Path = strings.Join(treeParts[1:], "/")
79+
}
4980
}
5081
} else {
5182
// Pattern: https://github.com/owner/repo
@@ -93,9 +124,9 @@ func DownloadAndExtract(info *RepoInfo, targetName string) error {
93124
if err != nil {
94125
return fmt.Errorf("failed to download: %w", err)
95126
}
96-
defer resp.Body.Close()
97127

98128
if resp.StatusCode != http.StatusOK {
129+
resp.Body.Close()
99130
// Try 'master' branch if 'main' fails
100131
if info.Branch == "main" {
101132
info.Branch = "master"
@@ -105,12 +136,13 @@ func DownloadAndExtract(info *RepoInfo, targetName string) error {
105136
if err != nil {
106137
return fmt.Errorf("failed to download: %w", err)
107138
}
108-
defer resp.Body.Close()
109-
}
110-
if resp.StatusCode != http.StatusOK {
111-
return fmt.Errorf("download failed with status: %s", resp.Status)
112139
}
113140
}
141+
defer resp.Body.Close()
142+
143+
if resp.StatusCode != http.StatusOK {
144+
return fmt.Errorf("download failed with status: %s", resp.Status)
145+
}
114146

115147
// Create temp file for zip
116148
tmpFile, err := os.CreateTemp("", "sk-*.zip")
@@ -148,6 +180,10 @@ func DownloadAndExtract(info *RepoInfo, targetName string) error {
148180
}
149181

150182
if err != nil {
183+
if info.TreeRef != "" && strings.Contains(info.TreeRef, "/") &&
184+
!strings.Contains(info.TreeRef, "%2F") && !strings.Contains(info.TreeRef, "%2f") {
185+
return fmt.Errorf("failed to extract: %w (if your branch contains '/', URL-encode it, e.g. feature%%2Ffoo)", err)
186+
}
151187
return fmt.Errorf("failed to extract: %w", err)
152188
}
153189

0 commit comments

Comments
 (0)