1//
2// Copyright 2017, Sander van Harmelen
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17package gitlab
18
19import (
20 "bytes"
21 "fmt"
22 "io"
23 "net/url"
24)
25
26// RepositoriesService handles communication with the repositories related
27// methods of the GitLab API.
28//
29// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
30type RepositoriesService struct {
31 client *Client
32}
33
34// TreeNode represents a GitLab repository file or directory.
35//
36// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
37type TreeNode struct {
38 ID string `json:"id"`
39 Name string `json:"name"`
40 Type string `json:"type"`
41 Path string `json:"path"`
42 Mode string `json:"mode"`
43}
44
45func (t TreeNode) String() string {
46 return Stringify(t)
47}
48
49// ListTreeOptions represents the available ListTree() options.
50//
51// GitLab API docs:
52// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
53type ListTreeOptions struct {
54 ListOptions
55 Path *string `url:"path,omitempty" json:"path,omitempty"`
56 Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
57 Recursive *bool `url:"recursive,omitempty" json:"recursive,omitempty"`
58}
59
60// ListTree gets a list of repository files and directories in a project.
61//
62// GitLab API docs:
63// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
64func (s *RepositoriesService) ListTree(pid interface{}, opt *ListTreeOptions, options ...OptionFunc) ([]*TreeNode, *Response, error) {
65 project, err := parseID(pid)
66 if err != nil {
67 return nil, nil, err
68 }
69 u := fmt.Sprintf("projects/%s/repository/tree", pathEscape(project))
70
71 req, err := s.client.NewRequest("GET", u, opt, options)
72 if err != nil {
73 return nil, nil, err
74 }
75
76 var t []*TreeNode
77 resp, err := s.client.Do(req, &t)
78 if err != nil {
79 return nil, resp, err
80 }
81
82 return t, resp, err
83}
84
85// Blob gets information about blob in repository like size and content. Note
86// that blob content is Base64 encoded.
87//
88// GitLab API docs:
89// https://docs.gitlab.com/ce/api/repositories.html#get-a-blob-from-repository
90func (s *RepositoriesService) Blob(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
91 project, err := parseID(pid)
92 if err != nil {
93 return nil, nil, err
94 }
95 u := fmt.Sprintf("projects/%s/repository/blobs/%s", pathEscape(project), url.PathEscape(sha))
96
97 req, err := s.client.NewRequest("GET", u, nil, options)
98 if err != nil {
99 return nil, nil, err
100 }
101
102 var b bytes.Buffer
103 resp, err := s.client.Do(req, &b)
104 if err != nil {
105 return nil, resp, err
106 }
107
108 return b.Bytes(), resp, err
109}
110
111// RawBlobContent gets the raw file contents for a blob by blob SHA.
112//
113// GitLab API docs:
114// https://docs.gitlab.com/ce/api/repositories.html#raw-blob-content
115func (s *RepositoriesService) RawBlobContent(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
116 project, err := parseID(pid)
117 if err != nil {
118 return nil, nil, err
119 }
120 u := fmt.Sprintf("projects/%s/repository/blobs/%s/raw", pathEscape(project), url.PathEscape(sha))
121
122 req, err := s.client.NewRequest("GET", u, nil, options)
123 if err != nil {
124 return nil, nil, err
125 }
126
127 var b bytes.Buffer
128 resp, err := s.client.Do(req, &b)
129 if err != nil {
130 return nil, resp, err
131 }
132
133 return b.Bytes(), resp, err
134}
135
136// ArchiveOptions represents the available Archive() options.
137//
138// GitLab API docs:
139// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
140type ArchiveOptions struct {
141 Format *string `url:"-" json:"-"`
142 SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
143}
144
145// Archive gets an archive of the repository.
146//
147// GitLab API docs:
148// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
149func (s *RepositoriesService) Archive(pid interface{}, opt *ArchiveOptions, options ...OptionFunc) ([]byte, *Response, error) {
150 project, err := parseID(pid)
151 if err != nil {
152 return nil, nil, err
153 }
154 u := fmt.Sprintf("projects/%s/repository/archive", pathEscape(project))
155
156 // Set an optional format for the archive.
157 if opt != nil && opt.Format != nil {
158 u = fmt.Sprintf("%s.%s", u, *opt.Format)
159 }
160
161 req, err := s.client.NewRequest("GET", u, opt, options)
162 if err != nil {
163 return nil, nil, err
164 }
165
166 var b bytes.Buffer
167 resp, err := s.client.Do(req, &b)
168 if err != nil {
169 return nil, resp, err
170 }
171
172 return b.Bytes(), resp, err
173}
174
175// StreamArchive streams an archive of the repository to the provided
176// io.Writer.
177//
178// GitLab API docs:
179// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
180func (s *RepositoriesService) StreamArchive(pid interface{}, w io.Writer, opt *ArchiveOptions, options ...OptionFunc) (*Response, error) {
181 project, err := parseID(pid)
182 if err != nil {
183 return nil, err
184 }
185 u := fmt.Sprintf("projects/%s/repository/archive", pathEscape(project))
186
187 // Set an optional format for the archive.
188 if opt != nil && opt.Format != nil {
189 u = fmt.Sprintf("%s.%s", u, *opt.Format)
190 }
191
192 req, err := s.client.NewRequest("GET", u, opt, options)
193 if err != nil {
194 return nil, err
195 }
196
197 return s.client.Do(req, w)
198}
199
200// Compare represents the result of a comparison of branches, tags or commits.
201//
202// GitLab API docs:
203// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
204type Compare struct {
205 Commit *Commit `json:"commit"`
206 Commits []*Commit `json:"commits"`
207 Diffs []*Diff `json:"diffs"`
208 CompareTimeout bool `json:"compare_timeout"`
209 CompareSameRef bool `json:"compare_same_ref"`
210}
211
212func (c Compare) String() string {
213 return Stringify(c)
214}
215
216// CompareOptions represents the available Compare() options.
217//
218// GitLab API docs:
219// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
220type CompareOptions struct {
221 From *string `url:"from,omitempty" json:"from,omitempty"`
222 To *string `url:"to,omitempty" json:"to,omitempty"`
223 Straight *bool `url:"straight,omitempty" json:"straight,omitempty"`
224}
225
226// Compare compares branches, tags or commits.
227//
228// GitLab API docs:
229// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
230func (s *RepositoriesService) Compare(pid interface{}, opt *CompareOptions, options ...OptionFunc) (*Compare, *Response, error) {
231 project, err := parseID(pid)
232 if err != nil {
233 return nil, nil, err
234 }
235 u := fmt.Sprintf("projects/%s/repository/compare", pathEscape(project))
236
237 req, err := s.client.NewRequest("GET", u, opt, options)
238 if err != nil {
239 return nil, nil, err
240 }
241
242 c := new(Compare)
243 resp, err := s.client.Do(req, c)
244 if err != nil {
245 return nil, resp, err
246 }
247
248 return c, resp, err
249}
250
251// Contributor represents a GitLap contributor.
252//
253// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
254type Contributor struct {
255 Name string `json:"name"`
256 Email string `json:"email"`
257 Commits int `json:"commits"`
258 Additions int `json:"additions"`
259 Deletions int `json:"deletions"`
260}
261
262func (c Contributor) String() string {
263 return Stringify(c)
264}
265
266// ListContributorsOptions represents the available ListContributors() options.
267//
268// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
269type ListContributorsOptions ListOptions
270
271// Contributors gets the repository contributors list.
272//
273// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
274func (s *RepositoriesService) Contributors(pid interface{}, opt *ListContributorsOptions, options ...OptionFunc) ([]*Contributor, *Response, error) {
275 project, err := parseID(pid)
276 if err != nil {
277 return nil, nil, err
278 }
279 u := fmt.Sprintf("projects/%s/repository/contributors", pathEscape(project))
280
281 req, err := s.client.NewRequest("GET", u, opt, options)
282 if err != nil {
283 return nil, nil, err
284 }
285
286 var c []*Contributor
287 resp, err := s.client.Do(req, &c)
288 if err != nil {
289 return nil, resp, err
290 }
291
292 return c, resp, err
293}
294
295// MergeBaseOptions represents the available MergeBase() options.
296//
297// GitLab API docs:
298// https://docs.gitlab.com/ce/api/repositories.html#merge-base
299type MergeBaseOptions struct {
300 Ref []string `url:"refs[],omitempty" json:"refs,omitempty"`
301}
302
303// MergeBase gets the common ancestor for 2 refs (commit SHAs, branch
304// names or tags).
305//
306// GitLab API docs:
307// https://docs.gitlab.com/ce/api/repositories.html#merge-base
308func (s *RepositoriesService) MergeBase(pid interface{}, opt *MergeBaseOptions, options ...OptionFunc) (*Commit, *Response, error) {
309 project, err := parseID(pid)
310 if err != nil {
311 return nil, nil, err
312 }
313 u := fmt.Sprintf("projects/%s/repository/merge_base", pathEscape(project))
314
315 req, err := s.client.NewRequest("GET", u, opt, options)
316 if err != nil {
317 return nil, nil, err
318 }
319
320 c := new(Commit)
321 resp, err := s.client.Do(req, c)
322 if err != nil {
323 return nil, resp, err
324 }
325
326 return c, resp, err
327}