aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/code.gitea.io/sdk/gitea/repo.go
blob: 62d13a3ab5776c142f01ce67123ffff52e7db228 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gitea

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/url"
	"strings"
	"time"
)

// Permission represents a set of permissions
type Permission struct {
	Admin bool `json:"admin"`
	Push  bool `json:"push"`
	Pull  bool `json:"pull"`
}

// InternalTracker represents settings for internal tracker
type InternalTracker struct {
	// Enable time tracking (Built-in issue tracker)
	EnableTimeTracker bool `json:"enable_time_tracker"`
	// Let only contributors track time (Built-in issue tracker)
	AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"`
	// Enable dependencies for issues and pull requests (Built-in issue tracker)
	EnableIssueDependencies bool `json:"enable_issue_dependencies"`
}

// ExternalTracker represents settings for external tracker
type ExternalTracker struct {
	// URL of external issue tracker.
	ExternalTrackerURL string `json:"external_tracker_url"`
	// External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index.
	ExternalTrackerFormat string `json:"external_tracker_format"`
	// External Issue Tracker Number Format, either `numeric` or `alphanumeric`
	ExternalTrackerStyle string `json:"external_tracker_style"`
}

// ExternalWiki represents setting for external wiki
type ExternalWiki struct {
	// URL of external wiki.
	ExternalWikiURL string `json:"external_wiki_url"`
}

// Repository represents a repository
type Repository struct {
	ID                        int64            `json:"id"`
	Owner                     *User            `json:"owner"`
	Name                      string           `json:"name"`
	FullName                  string           `json:"full_name"`
	Description               string           `json:"description"`
	Empty                     bool             `json:"empty"`
	Private                   bool             `json:"private"`
	Fork                      bool             `json:"fork"`
	Template                  bool             `json:"template"`
	Parent                    *Repository      `json:"parent"`
	Mirror                    bool             `json:"mirror"`
	Size                      int              `json:"size"`
	HTMLURL                   string           `json:"html_url"`
	SSHURL                    string           `json:"ssh_url"`
	CloneURL                  string           `json:"clone_url"`
	OriginalURL               string           `json:"original_url"`
	Website                   string           `json:"website"`
	Stars                     int              `json:"stars_count"`
	Forks                     int              `json:"forks_count"`
	Watchers                  int              `json:"watchers_count"`
	OpenIssues                int              `json:"open_issues_count"`
	OpenPulls                 int              `json:"open_pr_counter"`
	Releases                  int              `json:"release_counter"`
	DefaultBranch             string           `json:"default_branch"`
	Archived                  bool             `json:"archived"`
	Created                   time.Time        `json:"created_at"`
	Updated                   time.Time        `json:"updated_at"`
	Permissions               *Permission      `json:"permissions,omitempty"`
	HasIssues                 bool             `json:"has_issues"`
	InternalTracker           *InternalTracker `json:"internal_tracker,omitempty"`
	ExternalTracker           *ExternalTracker `json:"external_tracker,omitempty"`
	HasWiki                   bool             `json:"has_wiki"`
	ExternalWiki              *ExternalWiki    `json:"external_wiki,omitempty"`
	HasPullRequests           bool             `json:"has_pull_requests"`
	HasProjects               bool             `json:"has_projects"`
	IgnoreWhitespaceConflicts bool             `json:"ignore_whitespace_conflicts"`
	AllowMerge                bool             `json:"allow_merge_commits"`
	AllowRebase               bool             `json:"allow_rebase"`
	AllowRebaseMerge          bool             `json:"allow_rebase_explicit"`
	AllowSquash               bool             `json:"allow_squash_merge"`
	AvatarURL                 string           `json:"avatar_url"`
	Internal                  bool             `json:"internal"`
	MirrorInterval            string           `json:"mirror_interval"`
	DefaultMergeStyle         MergeStyle       `json:"default_merge_style"`
}

// RepoType represent repo type
type RepoType string

const (
	// RepoTypeNone dont specify a type
	RepoTypeNone RepoType = ""
	// RepoTypeSource is the default repo type
	RepoTypeSource RepoType = "source"
	// RepoTypeFork is a repo witch was forked from an other one
	RepoTypeFork RepoType = "fork"
	// RepoTypeMirror represents an mirror repo
	RepoTypeMirror RepoType = "mirror"
)

// TrustModel represent how git signatures are handled in a repository
type TrustModel string

const (
	// TrustModelDefault use TM set by global config
	TrustModelDefault TrustModel = "default"
	// TrustModelCollaborator gpg signature has to be owned by a repo collaborator
	TrustModelCollaborator TrustModel = "collaborator"
	// TrustModelCommitter gpg signature has to match committer
	TrustModelCommitter TrustModel = "committer"
	// TrustModelCollaboratorCommitter gpg signature has to match committer and owned by a repo collaborator
	TrustModelCollaboratorCommitter TrustModel = "collaboratorcommitter"
)

// ListReposOptions options for listing repositories
type ListReposOptions struct {
	ListOptions
}

// ListMyRepos lists all repositories for the authenticated user that has access to.
func (c *Client) ListMyRepos(opt ListReposOptions) ([]*Repository, *Response, error) {
	opt.setDefaults()
	repos := make([]*Repository, 0, opt.PageSize)
	resp, err := c.getParsedResponse("GET", fmt.Sprintf("/user/repos?%s", opt.getURLQuery().Encode()), nil, nil, &repos)
	return repos, resp, err
}

// ListUserRepos list all repositories of one user by user's name
func (c *Client) ListUserRepos(user string, opt ListReposOptions) ([]*Repository, *Response, error) {
	if err := escapeValidatePathSegments(&user); err != nil {
		return nil, nil, err
	}
	opt.setDefaults()
	repos := make([]*Repository, 0, opt.PageSize)
	resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos?%s", user, opt.getURLQuery().Encode()), nil, nil, &repos)
	return repos, resp, err
}

// ListOrgReposOptions options for a organization's repositories
type ListOrgReposOptions struct {
	ListOptions
}

// ListOrgRepos list all repositories of one organization by organization's name
func (c *Client) ListOrgRepos(org string, opt ListOrgReposOptions) ([]*Repository, *Response, error) {
	if err := escapeValidatePathSegments(&org); err != nil {
		return nil, nil, err
	}
	opt.setDefaults()
	repos := make([]*Repository, 0, opt.PageSize)
	resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos?%s", org, opt.getURLQuery().Encode()), nil, nil, &repos)
	return repos, resp, err
}

// SearchRepoOptions options for searching repositories
type SearchRepoOptions struct {
	ListOptions

	// The keyword to query
	Keyword string
	// Limit search to repositories with keyword as topic
	KeywordIsTopic bool
	// Include search of keyword within repository description
	KeywordInDescription bool

	/*
		User Filter
	*/

	// Repo Owner
	OwnerID int64
	// Stared By UserID
	StarredByUserID int64

	/*
		Repo Attributes
	*/

	// pubic, private or all repositories (defaults to all)
	IsPrivate *bool
	// archived, non-archived or all repositories (defaults to all)
	IsArchived *bool
	// Exclude template repos from search
	ExcludeTemplate bool
	// Filter by "fork", "source", "mirror"
	Type RepoType

	/*
		Sort Filters
	*/

	// sort repos by attribute. Supported values are "alpha", "created", "updated", "size", and "id". Default is "alpha"
	Sort string
	// sort order, either "asc" (ascending) or "desc" (descending). Default is "asc", ignored if "sort" is not specified.
	Order string
	// Repo owner to prioritize in the results
	PrioritizedByOwnerID int64

	/*
		Cover EdgeCases
	*/
	// if set all other options are ignored and this string is used as query
	RawQuery string
}

// QueryEncode turns options into querystring argument
func (opt *SearchRepoOptions) QueryEncode() string {
	query := opt.getURLQuery()
	if opt.Keyword != "" {
		query.Add("q", opt.Keyword)
	}
	if opt.KeywordIsTopic {
		query.Add("topic", "true")
	}
	if opt.KeywordInDescription {
		query.Add("includeDesc", "true")
	}

	// User Filter
	if opt.OwnerID > 0 {
		query.Add("uid", fmt.Sprintf("%d", opt.OwnerID))
		query.Add("exclusive", "true")
	}
	if opt.StarredByUserID > 0 {
		query.Add("starredBy", fmt.Sprintf("%d", opt.StarredByUserID))
	}

	// Repo Attributes
	if opt.IsPrivate != nil {
		query.Add("is_private", fmt.Sprintf("%v", opt.IsPrivate))
	}
	if opt.IsArchived != nil {
		query.Add("archived", fmt.Sprintf("%v", opt.IsArchived))
	}
	if opt.ExcludeTemplate {
		query.Add("template", "false")
	}
	if len(opt.Type) != 0 {
		query.Add("mode", string(opt.Type))
	}

	// Sort Filters
	if opt.Sort != "" {
		query.Add("sort", opt.Sort)
	}
	if opt.PrioritizedByOwnerID > 0 {
		query.Add("priority_owner_id", fmt.Sprintf("%d", opt.PrioritizedByOwnerID))
	}
	if opt.Order != "" {
		query.Add("order", opt.Order)
	}

	return query.Encode()
}

type searchRepoResponse struct {
	Repos []*Repository `json:"data"`
}

// SearchRepos searches for repositories matching the given filters
func (c *Client) SearchRepos(opt SearchRepoOptions) ([]*Repository, *Response, error) {
	opt.setDefaults()
	repos := new(searchRepoResponse)

	link, _ := url.Parse("/repos/search")

	if len(opt.RawQuery) != 0 {
		link.RawQuery = opt.RawQuery
	} else {
		link.RawQuery = opt.QueryEncode()
		// IsPrivate only works on gitea >= 1.12.0
		if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil && opt.IsPrivate != nil {
			if *opt.IsPrivate {
				// private repos only not supported on gitea <= 1.11.x
				return nil, nil, err
			}
			link.Query().Add("private", "false")
		}
	}

	resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &repos)
	return repos.Repos, resp, err
}

// CreateRepoOption options when creating repository
type CreateRepoOption struct {
	// Name of the repository to create
	Name string `json:"name"`
	// Description of the repository to create
	Description string `json:"description"`
	// Whether the repository is private
	Private bool `json:"private"`
	// Issue Label set to use
	IssueLabels string `json:"issue_labels"`
	// Whether the repository should be auto-intialized?
	AutoInit bool `json:"auto_init"`
	// Whether the repository is template
	Template bool `json:"template"`
	// Gitignores to use
	Gitignores string `json:"gitignores"`
	// License to use
	License string `json:"license"`
	// Readme of the repository to create
	Readme string `json:"readme"`
	// DefaultBranch of the repository (used when initializes and in template)
	DefaultBranch string `json:"default_branch"`
	// TrustModel of the repository
	TrustModel TrustModel `json:"trust_model"`
}

// Validate the CreateRepoOption struct
func (opt CreateRepoOption) Validate(c *Client) error {
	if len(strings.TrimSpace(opt.Name)) == 0 {
		return fmt.Errorf("name is empty")
	}
	if len(opt.Name) > 100 {
		return fmt.Errorf("name has more than 100 chars")
	}
	if len(opt.Description) > 255 {
		return fmt.Errorf("name has more than 255 chars")
	}
	if len(opt.DefaultBranch) > 100 {
		return fmt.Errorf("name has more than 100 chars")
	}
	if len(opt.TrustModel) != 0 {
		if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
			return err
		}
	}
	return nil
}

// CreateRepo creates a repository for authenticated user.
func (c *Client) CreateRepo(opt CreateRepoOption) (*Repository, *Response, error) {
	if err := opt.Validate(c); err != nil {
		return nil, nil, err
	}
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, nil, err
	}
	repo := new(Repository)
	resp, err := c.getParsedResponse("POST", "/user/repos", jsonHeader, bytes.NewReader(body), repo)
	return repo, resp, err
}

// CreateOrgRepo creates an organization repository for authenticated user.
func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *Response, error) {
	if err := escapeValidatePathSegments(&org); err != nil {
		return nil, nil, err
	}
	if err := opt.Validate(c); err != nil {
		return nil, nil, err
	}
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, nil, err
	}
	repo := new(Repository)
	resp, err := c.getParsedResponse("POST", fmt.Sprintf("/org/%s/repos", org), jsonHeader, bytes.NewReader(body), repo)
	return repo, resp, err
}

// GetRepo returns information of a repository of given owner.
func (c *Client) GetRepo(owner, reponame string) (*Repository, *Response, error) {
	if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
		return nil, nil, err
	}
	repo := new(Repository)
	resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo)
	return repo, resp, err
}

// GetRepoByID returns information of a repository by a giver repository ID.
func (c *Client) GetRepoByID(id int64) (*Repository, *Response, error) {
	repo := new(Repository)
	resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repositories/%d", id), nil, nil, repo)
	return repo, resp, err
}

// EditRepoOption options when editing a repository's properties
type EditRepoOption struct {
	// name of the repository
	Name *string `json:"name,omitempty"`
	// a short description of the repository.
	Description *string `json:"description,omitempty"`
	// a URL with more information about the repository.
	Website *string `json:"website,omitempty"`
	// either `true` to make the repository private or `false` to make it public.
	// Note: you will get a 422 error if the organization restricts changing repository visibility to organization
	// owners and a non-owner tries to change the value of private.
	Private *bool `json:"private,omitempty"`
	// either `true` to make this repository a template or `false` to make it a normal repository
	Template *bool `json:"template,omitempty"`
	// either `true` to enable issues for this repository or `false` to disable them.
	HasIssues *bool `json:"has_issues,omitempty"`
	// set this structure to configure internal issue tracker (requires has_issues)
	InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
	// set this structure to use external issue tracker (requires has_issues)
	ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
	// either `true` to enable the wiki for this repository or `false` to disable it.
	HasWiki *bool `json:"has_wiki,omitempty"`
	// set this structure to use external wiki instead of internal (requires has_wiki)
	ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
	// sets the default branch for this repository.
	DefaultBranch *string `json:"default_branch,omitempty"`
	// either `true` to allow pull requests, or `false` to prevent pull request.
	HasPullRequests *bool `json:"has_pull_requests,omitempty"`
	// either `true` to enable project unit, or `false` to disable them.
	HasProjects *bool `json:"has_projects,omitempty"`
	// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.
	IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"`
	// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.
	AllowMerge *bool `json:"allow_merge_commits,omitempty"`
	// either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.
	AllowRebase *bool `json:"allow_rebase,omitempty"`
	// either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.
	AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
	// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.
	AllowSquash *bool `json:"allow_squash_merge,omitempty"`
	// set to `true` to archive this repository.
	Archived *bool `json:"archived,omitempty"`
	// set to a string like `8h30m0s` to set the mirror interval time
	MirrorInterval *string `json:"mirror_interval,omitempty"`
	// either `true` to allow mark pr as merged manually, or `false` to prevent it. `has_pull_requests` must be `true`.
	AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
	// either `true` to enable AutodetectManualMerge, or `false` to prevent it. `has_pull_requests` must be `true`, Note: In some special cases, misjudgments can occur.
	AutodetectManualMerge *bool `json:"autodetect_manual_merge,omitempty"`
	// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". `has_pull_requests` must be `true`.
	DefaultMergeStyle *MergeStyle `json:"default_merge_style,omitempty"`
	// set to `true` to archive this repository.
}

// EditRepo edit the properties of a repository
func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Repository, *Response, error) {
	if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
		return nil, nil, err
	}
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, nil, err
	}
	repo := new(Repository)
	resp, err := c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s", owner, reponame), jsonHeader, bytes.NewReader(body), repo)
	return repo, resp, err
}

// DeleteRepo deletes a repository of user or organization.
func (c *Client) DeleteRepo(owner, repo string) (*Response, error) {
	if err := escapeValidatePathSegments(&owner, &repo); err != nil {
		return nil, err
	}
	_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil)
	return resp, err
}

// MirrorSync adds a mirrored repository to the mirror sync queue.
func (c *Client) MirrorSync(owner, repo string) (*Response, error) {
	if err := escapeValidatePathSegments(&owner, &repo); err != nil {
		return nil, err
	}
	_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), nil, nil)
	return resp, err
}

// GetRepoLanguages return language stats of a repo
func (c *Client) GetRepoLanguages(owner, repo string) (map[string]int64, *Response, error) {
	if err := escapeValidatePathSegments(&owner, &repo); err != nil {
		return nil, nil, err
	}
	langMap := make(map[string]int64)

	data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/languages", owner, repo), jsonHeader, nil)
	if err != nil {
		return nil, resp, err
	}
	if err = json.Unmarshal(data, &langMap); err != nil {
		return nil, resp, err
	}
	return langMap, resp, nil
}

// ArchiveType represent supported archive formats by gitea
type ArchiveType string

const (
	// ZipArchive represent zip format
	ZipArchive ArchiveType = ".zip"
	// TarGZArchive represent tar.gz format
	TarGZArchive ArchiveType = ".tar.gz"
)

// GetArchive get an archive of a repository by git reference
// e.g.: ref -> master, 70b7c74b33, v1.2.1, ...
func (c *Client) GetArchive(owner, repo, ref string, ext ArchiveType) ([]byte, *Response, error) {
	if err := escapeValidatePathSegments(&owner, &repo); err != nil {
		return nil, nil, err
	}
	ref = pathEscapeSegments(ref)
	return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
}

// GetArchiveReader gets a `git archive` for a particular tree-ish git reference
// such as a branch name (`master`), a commit hash (`70b7c74b33`), a tag
// (`v1.2.1`). The archive is returned as a byte stream in a ReadCloser. It is
// the responsibility of the client to close the reader.
func (c *Client) GetArchiveReader(owner, repo, ref string, ext ArchiveType) (io.ReadCloser, *Response, error) {
	if err := escapeValidatePathSegments(&owner, &repo); err != nil {
		return nil, nil, err
	}
	ref = pathEscapeSegments(ref)
	resp, err := c.doRequest("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
	if err != nil {
		return nil, resp, err
	}

	if _, err := statusCodeToErr(resp); err != nil {
		return nil, resp, err
	}

	return resp.Body, resp, nil
}