7
7
8
8
"github.com/moby/buildkit/util/sshutil"
9
9
"github.com/pkg/errors"
10
+ "github.com/tonistiigi/go-csvvalue"
10
11
)
11
12
12
13
const (
@@ -66,12 +67,59 @@ type GitURLFragment struct {
66
67
67
68
// splitGitFragment splits a git URL fragment into its respective git
68
69
// reference and subdirectory components.
69
- func splitGitFragment (fragment string ) * GitURLFragment {
70
+ func splitGitFragment (fragment string ) (* GitURLFragment , error ) {
71
+ if strings .HasPrefix (fragment , "#" ) {
72
+ // Double-hash in the unparsed URL.
73
+ // e.g., https://github.com/user/repo.git##tag=tag,subdir=/dir
74
+ return splitGitFragmentCSVForm (fragment )
75
+ }
76
+ // Single-hash in the unparsed URL.
77
+ // e.g., https://github.com/user/repo.git#branch_or_tag_or_commit:dir
70
78
if fragment == "" {
71
- return nil
79
+ return nil , nil
72
80
}
73
81
ref , subdir , _ := strings .Cut (fragment , ":" )
74
- return & GitURLFragment {Ref : ref , Subdir : subdir }
82
+ return & GitURLFragment {Ref : ref , Subdir : subdir }, nil
83
+ }
84
+
85
+ func splitGitFragmentCSVForm (fragment string ) (* GitURLFragment , error ) {
86
+ fragment = strings .TrimPrefix (fragment , "#" )
87
+ if fragment == "" {
88
+ return nil , nil
89
+ }
90
+ fields , err := csvvalue .Fields (fragment , nil )
91
+ if err != nil {
92
+ return nil , errors .Wrapf (err , "failed to parse CSV %q" , fragment )
93
+ }
94
+
95
+ res := & GitURLFragment {}
96
+ refs := make (map [string ]string )
97
+ for _ , field := range fields {
98
+ key , value , ok := strings .Cut (field , "=" )
99
+ if ! ok {
100
+ return nil , errors .Errorf ("invalid field '%s' must be a key=value pair" , field )
101
+ }
102
+ key = strings .ToLower (key )
103
+ switch key {
104
+ case "tag" , "branch" , "commit" :
105
+ refs [key ] = value
106
+ case "subdir" :
107
+ res .Subdir = value
108
+ default :
109
+ return nil , errors .Errorf ("unexpected key '%s' in '%s' (supported keys: tag, branch, commit, subdir)" , key , field )
110
+ }
111
+ }
112
+ if len (refs ) > 0 {
113
+ if len (refs ) > 1 {
114
+ // TODO: allow specifying tag and commit together https://github.com/moby/buildkit/issues/5871
115
+ return nil , errors .New ("tag, branch, and commit are exclusive" )
116
+ }
117
+ for _ , v := range refs {
118
+ res .Ref = v
119
+ break
120
+ }
121
+ }
122
+ return res , nil
75
123
}
76
124
77
125
// ParseURL parses a BuildKit-style Git URL (that may contain additional
@@ -86,11 +134,11 @@ func ParseURL(remote string) (*GitURL, error) {
86
134
if err != nil {
87
135
return nil , err
88
136
}
89
- return fromURL (url ), nil
137
+ return fromURL (url )
90
138
}
91
139
92
140
if url , err := sshutil .ParseSCPStyleURL (remote ); err == nil {
93
- return fromSCPStyleURL (url ), nil
141
+ return fromSCPStyleURL (url )
94
142
}
95
143
96
144
return nil , ErrUnknownProtocol
@@ -105,28 +153,37 @@ func IsGitTransport(remote string) bool {
105
153
return sshutil .IsImplicitSSHTransport (remote )
106
154
}
107
155
108
- func fromURL (url * url.URL ) * GitURL {
156
+ func fromURL (url * url.URL ) ( * GitURL , error ) {
109
157
withoutFragment := * url
110
158
withoutFragment .Fragment = ""
111
- return & GitURL {
159
+ fragment , err := splitGitFragment (url .Fragment )
160
+ if err != nil {
161
+ return nil , errors .Wrapf (err , "failed to parse URL fragment %q" , url .Fragment )
162
+ }
163
+ gitURL := & GitURL {
112
164
Scheme : url .Scheme ,
113
165
User : url .User ,
114
166
Host : url .Host ,
115
167
Path : url .Path ,
116
- Fragment : splitGitFragment ( url . Fragment ) ,
168
+ Fragment : fragment ,
117
169
Remote : withoutFragment .String (),
118
170
}
171
+ return gitURL , nil
119
172
}
120
173
121
- func fromSCPStyleURL (url * sshutil.SCPStyleURL ) * GitURL {
174
+ func fromSCPStyleURL (url * sshutil.SCPStyleURL ) ( * GitURL , error ) {
122
175
withoutFragment := * url
123
176
withoutFragment .Fragment = ""
177
+ fragment , err := splitGitFragment (url .Fragment )
178
+ if err != nil {
179
+ return nil , errors .Wrapf (err , "failed to parse URL fragment %q" , url .Fragment )
180
+ }
124
181
return & GitURL {
125
182
Scheme : SSHProtocol ,
126
183
User : url .User ,
127
184
Host : url .Host ,
128
185
Path : url .Path ,
129
- Fragment : splitGitFragment ( url . Fragment ) ,
186
+ Fragment : fragment ,
130
187
Remote : withoutFragment .String (),
131
- }
188
+ }, nil
132
189
}
0 commit comments