From 2f21bc33a528894c085bf944666f668725f04d8a Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Thu, 9 May 2019 13:22:35 -0400 Subject: [PATCH 01/12] Use AppURL for Oauth user link (#6894) * Use AppURL for Oauth user link Fix #6843 * Update oauth.go * Update oauth.go --- routers/user/oauth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/user/oauth.go b/routers/user/oauth.go index f69c0047ae..b85ea8125e 100644 --- a/routers/user/oauth.go +++ b/routers/user/oauth.go @@ -259,7 +259,7 @@ func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) { ctx.Data["Application"] = app ctx.Data["RedirectURI"] = form.RedirectURI ctx.Data["State"] = form.State - ctx.Data["ApplicationUserLink"] = "@" + app.User.Name + "" + ctx.Data["ApplicationUserLink"] = "@" + app.User.Name + "" ctx.Data["ApplicationRedirectDomainHTML"] = "" + form.RedirectURI + "" // TODO document SESSION <=> FORM ctx.Session.Set("client_id", app.ClientID) From 10ff527faefb14b1e06e300d8a4cb3c436c37445 Mon Sep 17 00:00:00 2001 From: Antoine GIRARD Date: Fri, 10 May 2019 00:41:43 +0200 Subject: [PATCH 02/12] internal/ssh: ignore env command totally (#6825) * ssh: ignore env command totally * Remove commented code Needed fix described in issue #6889 --- modules/ssh/ssh.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index f42c576cdb..98ff50bfec 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -52,18 +52,6 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { for req := range in { payload := cleanCommand(string(req.Payload)) switch req.Type { - case "env": - args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v") - if len(args) != 2 { - log.Warn("SSH: Invalid env arguments: '%#v'", args) - continue - } - args[0] = strings.TrimLeft(args[0], "\x04") - _, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) - if err != nil { - log.Error("env: %v", err) - return - } case "exec": cmdName := strings.TrimLeft(payload, "'()") log.Trace("SSH: Payload: %v", cmdName) From d64a646c0a9c4ea1ca7f6213e4d353132cb735c1 Mon Sep 17 00:00:00 2001 From: zeripath Date: Fri, 10 May 2019 18:48:28 +0100 Subject: [PATCH 03/12] Escape the commit message on issues update and title in telegram hook (#6901) --- models/action.go | 3 ++- models/webhook_telegram.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/models/action.go b/models/action.go index 01a6a91704..f397298699 100644 --- a/models/action.go +++ b/models/action.go @@ -8,6 +8,7 @@ package models import ( "encoding/json" "fmt" + "html" "path" "regexp" "strconv" @@ -580,7 +581,7 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra } refMarked[issue.ID] = true - message := fmt.Sprintf(`%s`, repo.Link(), c.Sha1, c.Message) + message := fmt.Sprintf(`%s`, repo.Link(), c.Sha1, html.EscapeString(c.Message)) if err = CreateRefComment(doer, refRepo, issue, message, c.Sha1); err != nil { return err } diff --git a/models/webhook_telegram.go b/models/webhook_telegram.go index 9aef0e3ece..7976d14c57 100644 --- a/models/webhook_telegram.go +++ b/models/webhook_telegram.go @@ -7,6 +7,7 @@ package models import ( "encoding/json" "fmt" + "html" "strings" "code.gitea.io/gitea/modules/git" @@ -169,7 +170,7 @@ func getTelegramIssuesPayload(p *api.IssuePayload) (*TelegramPayload, error) { func getTelegramIssueCommentPayload(p *api.IssueCommentPayload) (*TelegramPayload, error) { url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)) - title := fmt.Sprintf(`#%d %s`, url, p.Issue.Index, p.Issue.Title) + title := fmt.Sprintf(`#%d %s`, url, p.Issue.Index, html.EscapeString(p.Issue.Title)) var text string switch p.Action { case api.HookIssueCommentCreated: From 1dc401635b8acb20f4222216ad0828ef39617989 Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Fri, 10 May 2019 16:49:29 -0400 Subject: [PATCH 04/12] update sdk to latest (#6903) --- go.mod | 2 +- go.sum | 2 + templates/swagger/v1_json.tmpl | 17 +++++-- vendor/code.gitea.io/sdk/gitea/org.go | 26 +++++----- vendor/code.gitea.io/sdk/gitea/org_type.go | 49 +++++++++++++++++++ vendor/code.gitea.io/sdk/gitea/repo_commit.go | 2 +- vendor/code.gitea.io/sdk/gitea/user_app.go | 3 +- vendor/modules.txt | 2 +- 8 files changed, 82 insertions(+), 21 deletions(-) create mode 100644 vendor/code.gitea.io/sdk/gitea/org_type.go diff --git a/go.mod b/go.mod index f7e7a683ac..2657a27994 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module code.gitea.io/gitea go 1.12 require ( - code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7 + code.gitea.io/sdk v0.0.0-20190510041517-9251befd3702 github.com/BurntSushi/toml v0.3.1 // indirect github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 github.com/RoaringBitmap/roaring v0.4.7 // indirect diff --git a/go.sum b/go.sum index ca64a7a5e7..ad9a0a9be4 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7 h1:YggbbCVgggcOjKYmcB2wVOsEtJHgHUNFFJZDB6QcYTg= code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7/go.mod h1:5bZt0dRznpn2JysytQnV0yCru3FwDv9O5G91jo+lDAk= +code.gitea.io/sdk v0.0.0-20190510041517-9251befd3702 h1:MyOmF6QejeIZfsEQYoJVHOm1PGNyGZMAZmo6cO8Hq80= +code.gitea.io/sdk v0.0.0-20190510041517-9251befd3702/go.mod h1:5bZt0dRznpn2JysytQnV0yCru3FwDv9O5G91jo+lDAk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 h1:UsHpWO0Elp6NaWVARdZHjiYwkhrspHVEGsyIKPb9OI8= diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index bc095f7237..198c18919e 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7126,6 +7126,9 @@ "type": "string", "x-go-name": "UserName" }, + "visibility": { + "$ref": "#/definitions/VisibleType" + }, "website": { "type": "string", "x-go-name": "Website" @@ -8595,6 +8598,9 @@ "type": "string", "x-go-name": "UserName" }, + "visibility": { + "$ref": "#/definitions/VisibleType" + }, "website": { "type": "string", "x-go-name": "Website" @@ -9447,6 +9453,12 @@ }, "x-go-package": "code.gitea.io/gitea/models" }, + "VisibleType": { + "description": "VisibleType defines the visibility (Organization only)", + "type": "integer", + "format": "int64", + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, "WatchInfo": { "description": "WatchInfo represents an API watch status of one repository", "type": "object", @@ -9484,9 +9496,6 @@ "AccessToken": { "description": "AccessToken represents an API access token.", "headers": { - "hashed_token": { - "type": "string" - }, "id": { "type": "integer", "format": "int64" @@ -9494,7 +9503,7 @@ "name": { "type": "string" }, - "token": { + "sha1": { "type": "string" }, "token_last_eight": { diff --git a/vendor/code.gitea.io/sdk/gitea/org.go b/vendor/code.gitea.io/sdk/gitea/org.go index 19238d1028..0897da3ef6 100644 --- a/vendor/code.gitea.io/sdk/gitea/org.go +++ b/vendor/code.gitea.io/sdk/gitea/org.go @@ -12,13 +12,14 @@ import ( // Organization represents an organization type Organization struct { - ID int64 `json:"id"` - UserName string `json:"username"` - FullName string `json:"full_name"` - AvatarURL string `json:"avatar_url"` - Description string `json:"description"` - Website string `json:"website"` - Location string `json:"location"` + ID int64 `json:"id"` + UserName string `json:"username"` + FullName string `json:"full_name"` + AvatarURL string `json:"avatar_url"` + Description string `json:"description"` + Website string `json:"website"` + Location string `json:"location"` + Visibility VisibleType `json:"visibility"` } // ListMyOrgs list all of current user's organizations @@ -42,11 +43,12 @@ func (c *Client) GetOrg(orgname string) (*Organization, error) { // CreateOrgOption options for creating an organization type CreateOrgOption struct { // required: true - UserName string `json:"username" binding:"Required"` - FullName string `json:"full_name"` - Description string `json:"description"` - Website string `json:"website"` - Location string `json:"location"` + UserName string `json:"username" binding:"Required"` + FullName string `json:"full_name"` + Description string `json:"description"` + Website string `json:"website"` + Location string `json:"location"` + Visibility VisibleType `json:"visibility"` } // EditOrgOption options for editing an organization diff --git a/vendor/code.gitea.io/sdk/gitea/org_type.go b/vendor/code.gitea.io/sdk/gitea/org_type.go new file mode 100644 index 0000000000..778ec3b0a7 --- /dev/null +++ b/vendor/code.gitea.io/sdk/gitea/org_type.go @@ -0,0 +1,49 @@ +// Copyright 2019 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 + +// VisibleType defines the visibility (Organization only) +type VisibleType int + +const ( + // VisibleTypePublic Visible for everyone + VisibleTypePublic VisibleType = iota + + // VisibleTypeLimited Visible for every connected user + VisibleTypeLimited + + // VisibleTypePrivate Visible only for organization's members + VisibleTypePrivate +) + +// VisibilityModes is a map of org Visibility types +var VisibilityModes = map[string]VisibleType{ + "public": VisibleTypePublic, + "limited": VisibleTypeLimited, + "private": VisibleTypePrivate, +} + +// IsPublic returns true if VisibleType is public +func (vt VisibleType) IsPublic() bool { + return vt == VisibleTypePublic +} + +// IsLimited returns true if VisibleType is limited +func (vt VisibleType) IsLimited() bool { + return vt == VisibleTypeLimited +} + +// IsPrivate returns true if VisibleType is private +func (vt VisibleType) IsPrivate() bool { + return vt == VisibleTypePrivate +} + +// ExtractKeysFromMapString provides a slice of keys from map +func ExtractKeysFromMapString(in map[string]VisibleType) (keys []string) { + for k := range in { + keys = append(keys, k) + } + return +} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_commit.go b/vendor/code.gitea.io/sdk/gitea/repo_commit.go index 74c52c06fb..1df66428aa 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo_commit.go +++ b/vendor/code.gitea.io/sdk/gitea/repo_commit.go @@ -50,5 +50,5 @@ type Commit struct { // GetSingleCommit returns a single commit func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, error) { commit := new(Commit) - return commit, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s", user, repo, commitID), nil, nil, &commit) + return commit, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit) } diff --git a/vendor/code.gitea.io/sdk/gitea/user_app.go b/vendor/code.gitea.io/sdk/gitea/user_app.go index 023837f83b..c5367be5cd 100644 --- a/vendor/code.gitea.io/sdk/gitea/user_app.go +++ b/vendor/code.gitea.io/sdk/gitea/user_app.go @@ -23,8 +23,7 @@ func BasicAuthEncode(user, pass string) string { type AccessToken struct { ID int64 `json:"id"` Name string `json:"name"` - Token string `json:"token"` - HashedToken string `json:"hashed_token"` + Token string `json:"sha1"` TokenLastEight string `json:"token_last_eight"` } diff --git a/vendor/modules.txt b/vendor/modules.txt index 370cfece98..715f20a130 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7 +# code.gitea.io/sdk v0.0.0-20190510041517-9251befd3702 code.gitea.io/sdk/gitea # github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml From f378cb572a73aa826a18f40c28cfcd4cd1e661fc Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Sat, 11 May 2019 09:42:19 +0200 Subject: [PATCH 05/12] improve description of branch protection (fix #6886) (#6906) The branch protection description text were not quite accurate. --- options/locale/locale_en-US.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 570498958b..a85221ab74 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1269,9 +1269,9 @@ settings.protected_branch_can_push_yes = You can push settings.protected_branch_can_push_no = You can not push settings.branch_protection = Branch Protection for Branch '%s' settings.protect_this_branch = Enable Branch Protection -settings.protect_this_branch_desc = Prevent deletion and disable Git force pushing to the branch. +settings.protect_this_branch_desc = Prevent deletion and disable any Git pushing to the branch. settings.protect_whitelist_committers = Enable Push Whitelist -settings.protect_whitelist_committers_desc = Allow whitelisted users or teams to bypass push restrictions. +settings.protect_whitelist_committers_desc = Allow whitelisted users or teams to push to this branch (but not force push). settings.protect_whitelist_users = Whitelisted users for pushing: settings.protect_whitelist_search_users = Search users… settings.protect_whitelist_teams = Whitelisted teams for pushing: From e3166744fef607c6623f48d3d53351d9553d81ba Mon Sep 17 00:00:00 2001 From: zeripath Date: Sat, 11 May 2019 10:13:57 +0100 Subject: [PATCH 06/12] Fix logging documentation (#6904) * ENABLE_MACARON_REDIRECT should be REDIRECT_MACARON_LOG * Allow DISABLE_ROUTER_LOG to be set in the [log] section --- docs/content/doc/advanced/logging-documentation.en-us.md | 4 ++-- modules/setting/log.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/content/doc/advanced/logging-documentation.en-us.md b/docs/content/doc/advanced/logging-documentation.en-us.md index 3af75c0d8f..d9a21affce 100644 --- a/docs/content/doc/advanced/logging-documentation.en-us.md +++ b/docs/content/doc/advanced/logging-documentation.en-us.md @@ -66,7 +66,7 @@ multiple subloggers that will log to files. By default Macaron will log to its own go `log` instance. This writes to `os.Stdout`. You can redirect this log to a Gitea configurable logger -through setting the `ENABLE_MACARON_REDIRECT` setting in the `[log]` +through setting the `REDIRECT_MACARON_LOG` setting in the `[log]` section which you can configure the outputs of by setting the `MACARON` value in the `[log]` section of the configuration. `MACARON` defaults to `file` if unset. @@ -89,7 +89,7 @@ log using the value: `MACARON = ,` There are two types of Router log. By default Macaron send its own router log which will be directed to Macaron's go `log`, however if you -`ENABLE_MACARON_REDIRECT` you will enable Gitea's router log. You can +`REDIRECT_MACARON_LOG` you will enable Gitea's router log. You can disable both types of Router log by setting `DISABLE_ROUTER_LOG`. If you enable the redirect, you can configure the outputs of this diff --git a/modules/setting/log.go b/modules/setting/log.go index aafb4b3725..cd2c5cc8c1 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -211,6 +211,8 @@ func newAccessLogService() { func newRouterLogService() { Cfg.Section("log").Key("ROUTER").MustString("console") + // Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG + DisableRouterLog = Cfg.Section("log").Key("DISABLE_ROUTER_LOG").MustBool(DisableRouterLog) if !DisableRouterLog && RedirectMacaronLog { options := newDefaultLogOptions() From 1658cd04e9918109867f2745ddf760d260681878 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Sat, 11 May 2019 09:16:08 +0000 Subject: [PATCH 07/12] [skip ci] Updated translations via Crowdin --- options/locale/locale_cs-CZ.ini | 2 -- options/locale/locale_de-DE.ini | 2 -- options/locale/locale_fr-FR.ini | 2 -- options/locale/locale_it-IT.ini | 2 -- options/locale/locale_ja-JP.ini | 2 -- options/locale/locale_lv-LV.ini | 2 -- options/locale/locale_pt-BR.ini | 2 -- options/locale/locale_ru-RU.ini | 2 -- options/locale/locale_sv-SE.ini | 2 -- options/locale/locale_uk-UA.ini | 2 -- options/locale/locale_zh-CN.ini | 2 -- 11 files changed, 22 deletions(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 25b116bb99..9c9740c1e8 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1231,9 +1231,7 @@ settings.protected_branch_can_push_yes=Můžete nahrávat settings.protected_branch_can_push_no=Nemůžete nahrávat settings.branch_protection=Ochrana větví pro větev „%s“ settings.protect_this_branch=Povolit ochranu větví -settings.protect_this_branch_desc=Zamezit smazání a zakázat vynucená nahrávání do větve. settings.protect_whitelist_committers=Povolit vyjmenovaným nahrávání -settings.protect_whitelist_committers_desc=Povolit vyjmenovaným uživatelům a týmům obejít omezení nahrávání. settings.protect_whitelist_users=Povolení uživatelé pro nahrávání: settings.protect_whitelist_search_users=Hledat uživatele… settings.protect_whitelist_teams=Povolené týmy pro nahrávání: diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index cdb432cea5..5ab5dd39cf 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1258,9 +1258,7 @@ settings.protected_branch_can_push_yes=Du kannst pushen settings.protected_branch_can_push_no=Du kannst nicht pushen settings.branch_protection=Branch-Schutz für Branch „%s“ settings.protect_this_branch=Branch-Schutz aktivieren -settings.protect_this_branch_desc=Verhindere Löschen und deaktiviere das „force pushing” von Git auf diesen Branch. settings.protect_whitelist_committers=Push-Whitelist aktivieren -settings.protect_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Push-Beschränkungen zu umgehen. settings.protect_whitelist_users=Nutzer, die pushen dürfen: settings.protect_whitelist_search_users=Benutzer suchen… settings.protect_whitelist_teams=Teams, die pushen dürfen: diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 4b9e8fa72c..a6c57d2d04 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1258,9 +1258,7 @@ settings.protected_branch_can_push_yes=Vous pouvez pousser settings.protected_branch_can_push_no=Vous ne pouvez pas pousser settings.branch_protection=Protection de la branche "%s settings.protect_this_branch=Protection de la branche -settings.protect_this_branch_desc=Interdire de supprimer la branche et désactiver les mises à jours forcées. settings.protect_whitelist_committers=Activer la liste blanche pour les mises à jour -settings.protect_whitelist_committers_desc=Autoriser les utilisateurs sur la liste blanche et les équipes de contourner les restrictions. settings.protect_whitelist_users=Utilisateurs en liste blanche : settings.protect_whitelist_search_users=Rechercher des utilisateurs… settings.protect_whitelist_teams=Équipes en liste blanche : diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index e1821d787d..6028cb2c09 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -1034,9 +1034,7 @@ settings.protected_branch_can_push_yes=Puoi pushare settings.protected_branch_can_push_no=Non puoi pushare settings.branch_protection=Protezione branch per il branch '%s' settings.protect_this_branch=Attiva protezione branch -settings.protect_this_branch_desc=Impedisci l'eliminazione e disabilita i push forzati di Git nel branch. settings.protect_whitelist_committers=Attiva la whitelist per i push -settings.protect_whitelist_committers_desc=Consenti agli utenti o ai team nella whitelist di ignorare le restrizioni di push. settings.protect_whitelist_users=Utenti nella whitelist per pushare: settings.protect_whitelist_search_users=Cerca utenti… settings.protect_whitelist_teams=Team nella whitelist per pushare: diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 2f481ca3f2..788a2201fe 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1268,9 +1268,7 @@ settings.protected_branch_can_push_yes=プッシュできます settings.protected_branch_can_push_no=プッシュできません settings.branch_protection=ブランチ '%s' の保護 settings.protect_this_branch=ブランチの保護を有効にする -settings.protect_this_branch_desc=ブランチの削除を防止し、ブランチへの強制プッシュを無効にします。 settings.protect_whitelist_committers=プッシュ・ホワイトリストを有効にする -settings.protect_whitelist_committers_desc=ホワイトリストに登録したユーザーまたはチームに、プッシュ制限が適用されないようにします。 settings.protect_whitelist_users=プッシュ・ホワイトリストに含むユーザー: settings.protect_whitelist_search_users=ユーザーを検索… settings.protect_whitelist_teams=プッシュ・ホワイトリストに含むチーム: diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 1c319b57bf..bb996bddb0 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1205,9 +1205,7 @@ settings.protected_branch_can_push_yes=Jūs varat nosūtīt izmaiņas settings.protected_branch_can_push_no=Jūs nevarat nosūtīt izmaiņas settings.branch_protection=Atzara aizsardzība atzaram '%s' settings.protect_this_branch=Iespējot atzara aizsardzību -settings.protect_this_branch_desc=Neļauj dzēšanu un piespiedu Git izmaiņu nosūtīšanu uz atzaru. settings.protect_whitelist_committers=Iespējot izmaiņu nosūtīšanas ierobežojumus -settings.protect_whitelist_committers_desc=Atļaut tikai noteiktiem lietotājiem vai komandām apiet izmaiņu nosūtīšanas ierobežojumus. settings.protect_whitelist_users=Lietotāji, kas var veikt izmaiņu nosūtīšanu: settings.protect_whitelist_search_users=Meklēt lietotājus… settings.protect_whitelist_teams=Komandas, kas var veikt izmaiņu nosūtīšanu: diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 295ff63932..7905699e58 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1268,9 +1268,7 @@ settings.protected_branch_can_push_yes=Você pode fazer push settings.protected_branch_can_push_no=Você não pode fazer push settings.branch_protection=Proteção de branch para '%s' settings.protect_this_branch=Habilitar proteção de branch -settings.protect_this_branch_desc=Evitar exclusão e desabilitar Git force pushing para o branch. settings.protect_whitelist_committers=Habilitar controle de permissão de push -settings.protect_whitelist_committers_desc=Permitir que determinados usuários ou equipes possam realizar push. settings.protect_whitelist_users=Usuários com permissão para realizar push: settings.protect_whitelist_search_users=Pesquisar usuários... settings.protect_whitelist_teams=Equipes com permissão para realizar push: diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index b8bb1d9855..d761e93bf4 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1167,9 +1167,7 @@ settings.protected_branch_can_push_yes=Вы можете выполнять push settings.protected_branch_can_push_no=Вы не можете выполнять push settings.branch_protection=Защита ветки %s settings.protect_this_branch=Защитить эту ветку -settings.protect_this_branch_desc=Выключить принудительный push и защитить от удаления. settings.protect_whitelist_committers=Белый список тех, кто может делать push в эту ветку -settings.protect_whitelist_committers_desc=Добавить пользователей или команды в белый список ветки. На них не будут распространяться обычные ограничения на push. settings.protect_whitelist_users=Пользователи, которые могут делать push в эту ветку: settings.protect_whitelist_search_users=Поиск пользователей… settings.protect_whitelist_teams=Команды, члены которых могут делать push в эту ветку: diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 82f06fcae7..a32854ccb5 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -1065,9 +1065,7 @@ settings.protected_branch_can_push_yes=Du kan pusha settings.protected_branch_can_push_no=Du kan inte pusha settings.branch_protection=Branchskydd för '%s' settings.protect_this_branch=Aktivera branchskydd -settings.protect_this_branch_desc=Förhindra borttagning och inaktivera tvingad git push mot branchen. settings.protect_whitelist_committers=Aktivera vitlistning för Push -settings.protect_whitelist_committers_desc=Tillåt vitlistade användare eller team att förbigå push restriktioner. settings.protect_whitelist_users=Vitlistade användare för pushning: settings.protect_whitelist_search_users=Sök användare… settings.protect_whitelist_teams=Vitlistade team för pushning: diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index c2bd8cbf5d..648e5f66ef 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1114,9 +1114,7 @@ settings.protected_branch_can_push_yes=Ви можете виконувати pu settings.protected_branch_can_push_no=Ви не можете виконувати push settings.branch_protection=Захист гілки %s settings.protect_this_branch=Захистити цю гілку -settings.protect_this_branch_desc=Вимкнути примусовий push і захистити від видалення. settings.protect_whitelist_committers=Білий список тих, хто може робити push в цю гілку -settings.protect_whitelist_committers_desc=Додати користувачів або команди в білий список гілки. На них не будуть поширюватися звичайні обмеження на push. settings.protect_whitelist_users=Користувачі, які можуть робити push в цю гілку: settings.protect_whitelist_search_users=Пошук користувачів… settings.protect_whitelist_teams=Команди, учасники яких можуть робити push в цю гілку: diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 4e68f8d493..e5362d8e0e 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1268,9 +1268,7 @@ settings.protected_branch_can_push_yes=你可以推 settings.protected_branch_can_push_no=你不能推 settings.branch_protection=分支 '%s' 的分支保护 settings.protect_this_branch=启用分支保护 -settings.protect_this_branch_desc=防止删除并禁用 Git 强制推送到分支。 settings.protect_whitelist_committers=启用推送白名单 -settings.protect_whitelist_committers_desc=允许列入白名单用户或团队绕过推送限制。 settings.protect_whitelist_users=推送白名单用户: settings.protect_whitelist_search_users=搜索用户... settings.protect_whitelist_teams=推送白名单团队: From 34eee25bd42d19287e3e33afd169cc979ab61f37 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 11 May 2019 18:21:34 +0800 Subject: [PATCH 08/12] Move sdk structs to modules/structs (#6905) * move sdk structs to moduels/structs * fix tests * fix fmt * fix swagger * fix vendor --- go.mod | 1 - go.sum | 4 - integrations/api_admin_test.go | 2 +- integrations/api_branch_test.go | 2 +- integrations/api_comment_test.go | 2 +- integrations/api_fork_test.go | 2 +- integrations/api_gpg_keys_test.go | 2 +- .../api_helper_for_declarative_test.go | 2 +- integrations/api_issue_label_test.go | 2 +- integrations/api_issue_test.go | 2 +- integrations/api_keys_test.go | 2 +- integrations/api_org_test.go | 2 +- integrations/api_pull_test.go | 2 +- integrations/api_releases_test.go | 2 +- integrations/api_repo_file_content_test.go | 2 +- integrations/api_repo_file_create_test.go | 2 +- integrations/api_repo_file_delete_test.go | 2 +- integrations/api_repo_file_helpers.go | 2 +- integrations/api_repo_file_update_test.go | 2 +- integrations/api_repo_git_blobs_test.go | 2 +- integrations/api_repo_git_hook_test.go | 2 +- integrations/api_repo_lfs_locks_test.go | 2 +- integrations/api_repo_tags_test.go | 2 +- integrations/api_repo_test.go | 2 +- integrations/api_team_test.go | 2 +- integrations/api_token_test.go | 2 +- integrations/api_user_orgs_test.go | 2 +- integrations/api_user_search_test.go | 2 +- integrations/benchmarks_test.go | 2 +- integrations/links_test.go | 2 +- integrations/pull_status_test.go | 2 +- integrations/repo_commits_test.go | 2 +- integrations/ssh_key_test.go | 2 +- integrations/version_test.go | 4 +- models/action.go | 2 +- models/attachment.go | 2 +- models/issue.go | 2 +- models/issue_assignees.go | 2 +- models/issue_comment.go | 2 +- models/issue_label.go | 2 +- models/issue_label_test.go | 2 +- models/issue_milestone.go | 2 +- models/issue_milestone_test.go | 2 +- models/issue_tracked_time.go | 2 +- models/lfs_lock.go | 2 +- models/pull.go | 2 +- models/release.go | 2 +- models/repo.go | 2 +- models/review.go | 2 +- models/status.go | 2 +- models/user.go | 2 +- models/webhook.go | 2 +- models/webhook_dingtalk.go | 2 +- models/webhook_discord.go | 2 +- models/webhook_msteams.go | 2 +- models/webhook_slack.go | 2 +- models/webhook_telegram.go | 2 +- models/webhook_test.go | 2 +- modules/lfs/locks.go | 2 +- modules/repofiles/blob.go | 2 +- modules/repofiles/blob_test.go | 2 +- modules/repofiles/content.go | 2 +- modules/repofiles/content_test.go | 6 +- modules/repofiles/delete.go | 2 +- modules/repofiles/delete_test.go | 2 +- modules/repofiles/file.go | 2 +- modules/repofiles/file_test.go | 2 +- modules/repofiles/tree.go | 2 +- modules/repofiles/tree_test.go | 2 +- modules/repofiles/update.go | 4 +- modules/repofiles/update_test.go | 2 +- modules/repofiles/verification.go | 6 +- .../gitea => modules/structs}/admin_user.go | 44 +---- modules/structs/attachment.go | 27 +++ .../sdk/gitea => modules/structs}/doc.go | 2 +- modules/structs/fork.go | 11 ++ .../sdk/gitea => modules/structs}/git_blob.go | 2 +- modules/structs/git_hook.go | 20 ++ .../sdk/gitea => modules/structs}/hook.go | 80 +------- .../sdk/gitea => modules/structs}/issue.go | 67 +------ modules/structs/issue_comment.go | 35 ++++ modules/structs/issue_label.go | 36 ++++ modules/structs/issue_milestone.go | 39 ++++ modules/structs/issue_tracked_time.go | 30 +++ .../sdk/gitea => modules/structs}/lfs_lock.go | 2 +- .../structs}/miscellaneous.go | 8 +- .../sdk/gitea => modules/structs}/org.go | 36 +--- modules/structs/org_member.go | 10 + .../sdk/gitea => modules/structs}/org_team.go | 2 +- .../sdk/gitea => modules/structs}/pull.go | 61 +----- modules/structs/release.go | 50 +++++ .../sdk/gitea => modules/structs}/repo.go | 109 ++++------- modules/structs/repo_branch.go | 11 ++ modules/structs/repo_collaborator.go | 10 + .../gitea => modules/structs}/repo_commit.go | 12 +- .../gitea => modules/structs}/repo_file.go | 12 +- modules/structs/repo_key.go | 41 +++++ modules/structs/repo_refs.go | 19 ++ .../sdk/gitea => modules/structs}/repo_tag.go | 12 +- .../gitea => modules/structs}/repo_tree.go | 18 +- modules/structs/repo_watch.go | 19 ++ .../sdk/gitea => modules/structs}/status.go | 34 +--- .../sdk/gitea => modules/structs}/user.go | 17 +- modules/structs/user_app.go | 34 ++++ modules/structs/user_email.go | 25 +++ .../gitea => modules/structs}/user_gpgkey.go | 39 +--- modules/structs/user_key.go | 23 +++ modules/structs/user_search.go | 5 + .../sdk/gitea => modules/structs}/utils.go | 2 +- routers/api/v1/admin/org.go | 2 +- routers/api/v1/admin/repo.go | 2 +- routers/api/v1/admin/user.go | 2 +- routers/api/v1/api.go | 2 +- routers/api/v1/convert/convert.go | 2 +- routers/api/v1/misc/markdown.go | 2 +- routers/api/v1/misc/markdown_test.go | 2 +- routers/api/v1/misc/version.go | 4 +- routers/api/v1/org/hook.go | 2 +- routers/api/v1/org/member.go | 2 +- routers/api/v1/org/org.go | 2 +- routers/api/v1/org/team.go | 2 +- routers/api/v1/repo/branch.go | 2 +- routers/api/v1/repo/collaborators.go | 2 +- routers/api/v1/repo/commits.go | 2 +- routers/api/v1/repo/file.go | 2 +- routers/api/v1/repo/fork.go | 2 +- routers/api/v1/repo/git_hook.go | 2 +- routers/api/v1/repo/git_ref.go | 2 +- routers/api/v1/repo/hook.go | 2 +- routers/api/v1/repo/issue.go | 2 +- routers/api/v1/repo/issue_comment.go | 2 +- routers/api/v1/repo/issue_label.go | 2 +- routers/api/v1/repo/issue_tracked_time.go | 2 +- routers/api/v1/repo/key.go | 2 +- routers/api/v1/repo/label.go | 2 +- routers/api/v1/repo/milestone.go | 2 +- routers/api/v1/repo/pull.go | 2 +- routers/api/v1/repo/release.go | 2 +- routers/api/v1/repo/release_attachment.go | 2 +- routers/api/v1/repo/repo.go | 2 +- routers/api/v1/repo/star.go | 2 +- routers/api/v1/repo/status.go | 2 +- routers/api/v1/repo/subscriber.go | 2 +- routers/api/v1/repo/tag.go | 2 +- routers/api/v1/swagger/issue.go | 2 +- routers/api/v1/swagger/key.go | 2 +- routers/api/v1/swagger/misc.go | 2 +- routers/api/v1/swagger/options.go | 2 +- routers/api/v1/swagger/org.go | 2 +- routers/api/v1/swagger/repo.go | 2 +- routers/api/v1/swagger/user.go | 2 +- routers/api/v1/user/app.go | 2 +- routers/api/v1/user/email.go | 2 +- routers/api/v1/user/follower.go | 2 +- routers/api/v1/user/gpg_key.go | 2 +- routers/api/v1/user/key.go | 2 +- routers/api/v1/user/repo.go | 2 +- routers/api/v1/user/star.go | 2 +- routers/api/v1/user/user.go | 2 +- routers/api/v1/user/watch.go | 2 +- routers/api/v1/utils/hook.go | 2 +- routers/repo/webhook.go | 2 +- templates/swagger/v1_json.tmpl | 174 +++++++++--------- vendor/code.gitea.io/sdk/LICENSE | 20 -- vendor/code.gitea.io/sdk/gitea/admin_org.go | 22 --- vendor/code.gitea.io/sdk/gitea/admin_repo.go | 22 --- vendor/code.gitea.io/sdk/gitea/attachment.go | 92 --------- vendor/code.gitea.io/sdk/gitea/fork.go | 39 ---- vendor/code.gitea.io/sdk/gitea/git_hook.go | 54 ------ vendor/code.gitea.io/sdk/gitea/gitea.go | 125 ------------- .../code.gitea.io/sdk/gitea/issue_comment.go | 76 -------- vendor/code.gitea.io/sdk/gitea/issue_label.go | 122 ------------ .../sdk/gitea/issue_milestone.go | 80 -------- .../sdk/gitea/issue_tracked_time.go | 68 ------- vendor/code.gitea.io/sdk/gitea/org_member.go | 26 --- vendor/code.gitea.io/sdk/gitea/org_type.go | 49 ----- vendor/code.gitea.io/sdk/gitea/release.go | 105 ----------- vendor/code.gitea.io/sdk/gitea/repo_branch.go | 27 --- .../sdk/gitea/repo_collaborator.go | 57 ------ vendor/code.gitea.io/sdk/gitea/repo_key.go | 72 -------- vendor/code.gitea.io/sdk/gitea/repo_refs.go | 69 ------- vendor/code.gitea.io/sdk/gitea/repo_watch.go | 41 ----- vendor/code.gitea.io/sdk/gitea/user_app.go | 65 ------- vendor/code.gitea.io/sdk/gitea/user_email.go | 56 ------ vendor/code.gitea.io/sdk/gitea/user_follow.go | 55 ------ vendor/code.gitea.io/sdk/gitea/user_key.go | 60 ------ vendor/code.gitea.io/sdk/gitea/user_search.go | 14 -- vendor/modules.txt | 2 - 188 files changed, 717 insertions(+), 2144 deletions(-) rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/admin_user.go (59%) create mode 100644 modules/structs/attachment.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/doc.go (76%) create mode 100644 modules/structs/fork.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/git_blob.go (96%) create mode 100644 modules/structs/git_hook.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/hook.go (83%) rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/issue.go (52%) create mode 100644 modules/structs/issue_comment.go create mode 100644 modules/structs/issue_label.go create mode 100644 modules/structs/issue_milestone.go create mode 100644 modules/structs/issue_tracked_time.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/lfs_lock.go (99%) rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/miscellaneous.go (81%) rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/org.go (53%) create mode 100644 modules/structs/org_member.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/org_team.go (98%) rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/pull.go (57%) create mode 100644 modules/structs/release.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/repo.go (50%) create mode 100644 modules/structs/repo_branch.go create mode 100644 modules/structs/repo_collaborator.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/repo_commit.go (81%) rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/repo_file.go (88%) create mode 100644 modules/structs/repo_key.go create mode 100644 modules/structs/repo_refs.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/repo_tag.go (58%) rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/repo_tree.go (58%) create mode 100644 modules/structs/repo_watch.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/status.go (62%) rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/user.go (69%) create mode 100644 modules/structs/user_app.go create mode 100644 modules/structs/user_email.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/user_gpgkey.go (52%) create mode 100644 modules/structs/user_key.go create mode 100644 modules/structs/user_search.go rename {vendor/code.gitea.io/sdk/gitea => modules/structs}/utils.go (96%) delete mode 100644 vendor/code.gitea.io/sdk/LICENSE delete mode 100644 vendor/code.gitea.io/sdk/gitea/admin_org.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/admin_repo.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/attachment.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/fork.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/git_hook.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/gitea.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/issue_comment.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/issue_label.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/issue_milestone.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/org_member.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/org_type.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/release.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/repo_branch.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/repo_collaborator.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/repo_key.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/repo_refs.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/repo_watch.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/user_app.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/user_email.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/user_follow.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/user_key.go delete mode 100644 vendor/code.gitea.io/sdk/gitea/user_search.go diff --git a/go.mod b/go.mod index 2657a27994..45f62e676b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module code.gitea.io/gitea go 1.12 require ( - code.gitea.io/sdk v0.0.0-20190510041517-9251befd3702 github.com/BurntSushi/toml v0.3.1 // indirect github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 github.com/RoaringBitmap/roaring v0.4.7 // indirect diff --git a/go.sum b/go.sum index ad9a0a9be4..df3c606eb4 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,4 @@ cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7 h1:YggbbCVgggcOjKYmcB2wVOsEtJHgHUNFFJZDB6QcYTg= -code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7/go.mod h1:5bZt0dRznpn2JysytQnV0yCru3FwDv9O5G91jo+lDAk= -code.gitea.io/sdk v0.0.0-20190510041517-9251befd3702 h1:MyOmF6QejeIZfsEQYoJVHOm1PGNyGZMAZmo6cO8Hq80= -code.gitea.io/sdk v0.0.0-20190510041517-9251befd3702/go.mod h1:5bZt0dRznpn2JysytQnV0yCru3FwDv9O5G91jo+lDAk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 h1:UsHpWO0Elp6NaWVARdZHjiYwkhrspHVEGsyIKPb9OI8= diff --git a/integrations/api_admin_test.go b/integrations/api_admin_test.go index 41add45458..825aed9d50 100644 --- a/integrations/api_admin_test.go +++ b/integrations/api_admin_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_branch_test.go b/integrations/api_branch_test.go index aff3f223c0..fddbe4e40e 100644 --- a/integrations/api_branch_test.go +++ b/integrations/api_branch_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_comment_test.go b/integrations/api_comment_test.go index 60bb2cfb7b..7bd3644912 100644 --- a/integrations/api_comment_test.go +++ b/integrations/api_comment_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_fork_test.go b/integrations/api_fork_test.go index 2c5eb55166..fec288d96f 100644 --- a/integrations/api_fork_test.go +++ b/integrations/api_fork_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) func TestCreateForkNoLogin(t *testing.T) { diff --git a/integrations/api_gpg_keys_test.go b/integrations/api_gpg_keys_test.go index 0973fd33a3..0384dcb22a 100644 --- a/integrations/api_gpg_keys_test.go +++ b/integrations/api_gpg_keys_test.go @@ -10,7 +10,7 @@ import ( "strconv" "testing" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_helper_for_declarative_test.go b/integrations/api_helper_for_declarative_test.go index 32a4ce8047..943981ead2 100644 --- a/integrations/api_helper_for_declarative_test.go +++ b/integrations/api_helper_for_declarative_test.go @@ -10,7 +10,7 @@ import ( "net/http" "testing" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_issue_label_test.go b/integrations/api_issue_label_test.go index 57ca07337d..bf50252ed8 100644 --- a/integrations/api_issue_label_test.go +++ b/integrations/api_issue_label_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_issue_test.go b/integrations/api_issue_test.go index 0ac2f3b67e..24535057e2 100644 --- a/integrations/api_issue_test.go +++ b/integrations/api_issue_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_keys_test.go b/integrations/api_keys_test.go index 91cbd72f91..ca3d4b2d7a 100644 --- a/integrations/api_keys_test.go +++ b/integrations/api_keys_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/assert" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) func TestViewDeployKeysNoLogin(t *testing.T) { diff --git a/integrations/api_org_test.go b/integrations/api_org_test.go index 3dd4b759b1..b36650f2e8 100644 --- a/integrations/api_org_test.go +++ b/integrations/api_org_test.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_pull_test.go b/integrations/api_pull_test.go index c378e563bd..8d24cdc188 100644 --- a/integrations/api_pull_test.go +++ b/integrations/api_pull_test.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go index 5cc4522f0c..897f863eb3 100644 --- a/integrations/api_releases_test.go +++ b/integrations/api_releases_test.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_file_content_test.go b/integrations/api_repo_file_content_test.go index cb4666070a..1f535ef3a0 100644 --- a/integrations/api_repo_file_content_test.go +++ b/integrations/api_repo_file_content_test.go @@ -13,7 +13,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_file_create_test.go b/integrations/api_repo_file_create_test.go index da35127a51..a43855b06c 100644 --- a/integrations/api_repo_file_create_test.go +++ b/integrations/api_repo_file_create_test.go @@ -16,7 +16,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_file_delete_test.go b/integrations/api_repo_file_delete_test.go index 2c2cd56437..b619f9c43c 100644 --- a/integrations/api_repo_file_delete_test.go +++ b/integrations/api_repo_file_delete_test.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_file_helpers.go b/integrations/api_repo_file_helpers.go index d06f484dd6..80fefe8ab3 100644 --- a/integrations/api_repo_file_helpers.go +++ b/integrations/api_repo_file_helpers.go @@ -7,7 +7,7 @@ package integrations import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/repofiles" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) func createFileInBranch(user *models.User, repo *models.Repository, treePath, branchName string) (*api.FileResponse, error) { diff --git a/integrations/api_repo_file_update_test.go b/integrations/api_repo_file_update_test.go index d62a0e279b..71994564ff 100644 --- a/integrations/api_repo_file_update_test.go +++ b/integrations/api_repo_file_update_test.go @@ -16,7 +16,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_git_blobs_test.go b/integrations/api_repo_git_blobs_test.go index 69bb6cde23..ad81299ba4 100644 --- a/integrations/api_repo_git_blobs_test.go +++ b/integrations/api_repo_git_blobs_test.go @@ -9,7 +9,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_git_hook_test.go b/integrations/api_repo_git_hook_test.go index 11632e6fc9..1c6de78677 100644 --- a/integrations/api_repo_git_hook_test.go +++ b/integrations/api_repo_git_hook_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_lfs_locks_test.go b/integrations/api_repo_lfs_locks_test.go index 35b26e45ab..657933dd5c 100644 --- a/integrations/api_repo_lfs_locks_test.go +++ b/integrations/api_repo_lfs_locks_test.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_tags_test.go b/integrations/api_repo_tags_test.go index f9026b4d29..13b446fb96 100644 --- a/integrations/api_repo_tags_test.go +++ b/integrations/api_repo_tags_test.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index d126e149fe..d03ddcb353 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -13,7 +13,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_team_test.go b/integrations/api_team_test.go index 7bb0e5b1bc..a884085f5c 100644 --- a/integrations/api_team_test.go +++ b/integrations/api_team_test.go @@ -11,8 +11,8 @@ import ( "testing" "code.gitea.io/gitea/models" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/convert" - api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go index 5768d3b48e..76660d160f 100644 --- a/integrations/api_token_test.go +++ b/integrations/api_token_test.go @@ -9,7 +9,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // TestAPICreateAndDeleteToken tests that token that was just created can be deleted diff --git a/integrations/api_user_orgs_test.go b/integrations/api_user_orgs_test.go index 9b250c0636..63e67f4356 100644 --- a/integrations/api_user_orgs_test.go +++ b/integrations/api_user_orgs_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/api_user_search_test.go b/integrations/api_user_search_test.go index 8e7c429e77..b4c95db2f3 100644 --- a/integrations/api_user_search_test.go +++ b/integrations/api_user_search_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/benchmarks_test.go b/integrations/benchmarks_test.go index 3f009f62ea..4be3008f52 100644 --- a/integrations/benchmarks_test.go +++ b/integrations/benchmarks_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) func BenchmarkRepo(b *testing.B) { diff --git a/integrations/links_test.go b/integrations/links_test.go index 803d992055..84be7e0591 100644 --- a/integrations/links_test.go +++ b/integrations/links_test.go @@ -11,8 +11,8 @@ import ( "testing" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) diff --git a/integrations/pull_status_test.go b/integrations/pull_status_test.go index ad8bb236ef..2381444676 100644 --- a/integrations/pull_status_test.go +++ b/integrations/pull_status_test.go @@ -10,7 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/repo_commits_test.go b/integrations/repo_commits_test.go index 48aac1802b..1f70d69251 100644 --- a/integrations/repo_commits_test.go +++ b/integrations/repo_commits_test.go @@ -9,7 +9,7 @@ import ( "path" "testing" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/ssh_key_test.go b/integrations/ssh_key_test.go index f6df5ff8f4..e8fbf17c55 100644 --- a/integrations/ssh_key_test.go +++ b/integrations/ssh_key_test.go @@ -15,7 +15,7 @@ import ( "time" "code.gitea.io/gitea/modules/git" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/integrations/version_test.go b/integrations/version_test.go index 9a30bfb53b..eaba3e0859 100644 --- a/integrations/version_test.go +++ b/integrations/version_test.go @@ -9,7 +9,7 @@ import ( "testing" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/sdk/gitea" + "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) @@ -21,7 +21,7 @@ func TestVersion(t *testing.T) { req := NewRequest(t, "GET", "/api/v1/version") resp := MakeRequest(t, req, http.StatusOK) - var version gitea.ServerVersion + var version structs.ServerVersion DecodeJSON(t, resp, &version) assert.Equal(t, setting.AppVer, string(version.Version)) } diff --git a/models/action.go b/models/action.go index f397298699..b4f4b1cb6d 100644 --- a/models/action.go +++ b/models/action.go @@ -20,8 +20,8 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" "github.com/go-xorm/builder" diff --git a/models/attachment.go b/models/attachment.go index bbb88939b1..1740f065d0 100644 --- a/models/attachment.go +++ b/models/attachment.go @@ -11,8 +11,8 @@ import ( "path" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/go-xorm/xorm" gouuid "github.com/satori/go.uuid" diff --git a/models/issue.go b/models/issue.go index ddc7fa24af..f9394594be 100644 --- a/models/issue.go +++ b/models/issue.go @@ -14,8 +14,8 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" "github.com/go-xorm/builder" diff --git a/models/issue_assignees.go b/models/issue_assignees.go index 5806dd976d..f6a2afe2c9 100644 --- a/models/issue_assignees.go +++ b/models/issue_assignees.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/modules/log" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/go-xorm/xorm" ) diff --git a/models/issue_comment.go b/models/issue_comment.go index a7d6e2e3e8..60fb5b0420 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -18,7 +18,7 @@ import ( "github.com/go-xorm/builder" "github.com/go-xorm/xorm" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" diff --git a/models/issue_label.go b/models/issue_label.go index 3c593e72f9..38266f3e7c 100644 --- a/models/issue_label.go +++ b/models/issue_label.go @@ -13,7 +13,7 @@ import ( "github.com/go-xorm/xorm" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})") diff --git a/models/issue_label_test.go b/models/issue_label_test.go index d103778df3..5cdc059cff 100644 --- a/models/issue_label_test.go +++ b/models/issue_label_test.go @@ -8,7 +8,7 @@ import ( "html/template" "testing" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 5304d85e97..3bde4b558c 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -9,8 +9,8 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/go-xorm/xorm" ) diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index 3ea63d2d6b..fb1ee8b7a5 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index bb3e767127..56ba525776 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -8,7 +8,7 @@ import ( "time" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/go-xorm/builder" "github.com/go-xorm/xorm" diff --git a/models/lfs_lock.go b/models/lfs_lock.go index d12c7cb538..a27a6f495e 100644 --- a/models/lfs_lock.go +++ b/models/lfs_lock.go @@ -12,7 +12,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/go-xorm/xorm" ) diff --git a/models/pull.go b/models/pull.go index 71a2439b2c..7382cbd126 100644 --- a/models/pull.go +++ b/models/pull.go @@ -23,9 +23,9 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" "github.com/go-xorm/xorm" diff --git a/models/release.go b/models/release.go index b2d5348248..b7ec4461f2 100644 --- a/models/release.go +++ b/models/release.go @@ -13,8 +13,8 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/go-xorm/builder" ) diff --git a/models/repo.go b/models/repo.go index e8af9aa2db..66c1bdbab1 100644 --- a/models/repo.go +++ b/models/repo.go @@ -28,9 +28,9 @@ import ( "code.gitea.io/gitea/modules/options" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/cae/zip" "github.com/Unknwon/com" diff --git a/models/review.go b/models/review.go index ea2ccebed5..5f856fbd89 100644 --- a/models/review.go +++ b/models/review.go @@ -8,8 +8,8 @@ import ( "fmt" "code.gitea.io/gitea/modules/log" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/go-xorm/builder" "github.com/go-xorm/core" diff --git a/models/status.go b/models/status.go index 9cfd9e896f..a3db47f455 100644 --- a/models/status.go +++ b/models/status.go @@ -12,8 +12,8 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/go-xorm/xorm" ) diff --git a/models/user.go b/models/user.go index a51f52afb9..0c445b5afd 100644 --- a/models/user.go +++ b/models/user.go @@ -32,8 +32,8 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" "github.com/go-xorm/builder" diff --git a/models/webhook.go b/models/webhook.go index 8a7350bb6e..51b91879ee 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -20,9 +20,9 @@ import ( "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" gouuid "github.com/satori/go.uuid" ) diff --git a/models/webhook_dingtalk.go b/models/webhook_dingtalk.go index 7086732707..6a4bdaf06e 100644 --- a/models/webhook_dingtalk.go +++ b/models/webhook_dingtalk.go @@ -10,7 +10,7 @@ import ( "strings" "code.gitea.io/gitea/modules/git" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" dingtalk "github.com/lunny/dingtalk_webhook" ) diff --git a/models/webhook_discord.go b/models/webhook_discord.go index 8f1cb16940..0029e94fca 100644 --- a/models/webhook_discord.go +++ b/models/webhook_discord.go @@ -13,7 +13,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) type ( diff --git a/models/webhook_msteams.go b/models/webhook_msteams.go index b0fc4e7a2e..f42defbd1a 100644 --- a/models/webhook_msteams.go +++ b/models/webhook_msteams.go @@ -10,7 +10,7 @@ import ( "strings" "code.gitea.io/gitea/modules/git" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) type ( diff --git a/models/webhook_slack.go b/models/webhook_slack.go index 2bb84c2c4b..f422953e41 100644 --- a/models/webhook_slack.go +++ b/models/webhook_slack.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // SlackMeta contains the slack metadata diff --git a/models/webhook_telegram.go b/models/webhook_telegram.go index 7976d14c57..ead669dd08 100644 --- a/models/webhook_telegram.go +++ b/models/webhook_telegram.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/markup" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) type ( diff --git a/models/webhook_test.go b/models/webhook_test.go index 518be8be8a..d2fb3ea6ed 100644 --- a/models/webhook_test.go +++ b/models/webhook_test.go @@ -8,7 +8,7 @@ import ( "encoding/json" "testing" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) diff --git a/modules/lfs/locks.go b/modules/lfs/locks.go index 7a7d3ad42a..525a93645f 100644 --- a/modules/lfs/locks.go +++ b/modules/lfs/locks.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) //checkIsValidRequest check if it a valid request in case of bad request it write the response to ctx. diff --git a/modules/repofiles/blob.go b/modules/repofiles/blob.go index 2f9ca72bd4..e9d85a0dcf 100644 --- a/modules/repofiles/blob.go +++ b/modules/repofiles/blob.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetBlobBySHA get the GitBlobResponse of a repository using a sha hash. diff --git a/modules/repofiles/blob_test.go b/modules/repofiles/blob_test.go index 55320345f9..1dc183a8af 100644 --- a/modules/repofiles/blob_test.go +++ b/modules/repofiles/blob_test.go @@ -8,8 +8,8 @@ import ( "testing" "code.gitea.io/gitea/models" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) diff --git a/modules/repofiles/content.go b/modules/repofiles/content.go index d35c5a6a67..3098087dc6 100644 --- a/modules/repofiles/content.go +++ b/modules/repofiles/content.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetFileContents gets the meta data on a file's contents diff --git a/modules/repofiles/content_test.go b/modules/repofiles/content_test.go index 0257284a53..ce3f5f3678 100644 --- a/modules/repofiles/content_test.go +++ b/modules/repofiles/content_test.go @@ -9,8 +9,8 @@ import ( "testing" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) @@ -30,7 +30,7 @@ func TestGetFileContents(t *testing.T) { treePath := "README.md" ref := ctx.Repo.Repository.DefaultBranch - expectedFileContentResponse := &gitea.FileContentResponse{ + expectedFileContentResponse := &structs.FileContentResponse{ Name: treePath, Path: treePath, SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", @@ -40,7 +40,7 @@ func TestGetFileContents(t *testing.T) { GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/4b4851ad51df6a7d9f25c979345979eaeb5b349f", DownloadURL: "https://try.gitea.io/user2/repo1/raw/branch/master/README.md", Type: "blob", - Links: &gitea.FileLinksResponse{ + Links: &structs.FileLinksResponse{ Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md", GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/4b4851ad51df6a7d9f25c979345979eaeb5b349f", HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md", diff --git a/modules/repofiles/delete.go b/modules/repofiles/delete.go index ce7993dc54..ccf90f43b3 100644 --- a/modules/repofiles/delete.go +++ b/modules/repofiles/delete.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // DeleteRepoFileOptions holds the repository delete file options diff --git a/modules/repofiles/delete_test.go b/modules/repofiles/delete_test.go index 0b0558e766..9d034066f5 100644 --- a/modules/repofiles/delete_test.go +++ b/modules/repofiles/delete_test.go @@ -8,8 +8,8 @@ import ( "testing" "code.gitea.io/gitea/models" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) diff --git a/modules/repofiles/file.go b/modules/repofiles/file.go index 913a9ed535..de3ee71dba 100644 --- a/modules/repofiles/file.go +++ b/modules/repofiles/file.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetFileResponseFromCommit Constructs a FileResponse from a Commit object diff --git a/modules/repofiles/file_test.go b/modules/repofiles/file_test.go index c9ee7f21e2..5f6320a938 100644 --- a/modules/repofiles/file_test.go +++ b/modules/repofiles/file_test.go @@ -9,8 +9,8 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) diff --git a/modules/repofiles/tree.go b/modules/repofiles/tree.go index e189fe1229..4eb54a2598 100644 --- a/modules/repofiles/tree.go +++ b/modules/repofiles/tree.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetTreeBySHA get the GitTreeResponse of a repository using a sha hash. diff --git a/modules/repofiles/tree_test.go b/modules/repofiles/tree_test.go index c211cfcfd2..ecff8b9071 100644 --- a/modules/repofiles/tree_test.go +++ b/modules/repofiles/tree_test.go @@ -8,8 +8,8 @@ import ( "testing" "code.gitea.io/gitea/models" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go index 8bc3b50ae0..66e3f2babc 100644 --- a/modules/repofiles/update.go +++ b/modules/repofiles/update.go @@ -19,7 +19,7 @@ import ( "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/sdk/gitea" + "code.gitea.io/gitea/modules/structs" ) // IdentityOptions for a person's identity like an author or committer @@ -108,7 +108,7 @@ func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string } // CreateOrUpdateRepoFile adds or updates a file in the given repository -func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepoFileOptions) (*gitea.FileResponse, error) { +func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepoFileOptions) (*structs.FileResponse, error) { // If no branch name is set, assume master if opts.OldBranch == "" { opts.OldBranch = repo.DefaultBranch diff --git a/modules/repofiles/update_test.go b/modules/repofiles/update_test.go index bf28021793..a3a0b0a420 100644 --- a/modules/repofiles/update_test.go +++ b/modules/repofiles/update_test.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - api "code.gitea.io/sdk/gitea" "github.com/stretchr/testify/assert" ) diff --git a/modules/repofiles/verification.go b/modules/repofiles/verification.go index 75ead92d0f..be56f9b8b8 100644 --- a/modules/repofiles/verification.go +++ b/modules/repofiles/verification.go @@ -7,12 +7,12 @@ package repofiles import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" - "code.gitea.io/sdk/gitea" + "code.gitea.io/gitea/modules/structs" ) // GetPayloadCommitVerification returns the verification information of a commit -func GetPayloadCommitVerification(commit *git.Commit) *gitea.PayloadCommitVerification { - verification := &gitea.PayloadCommitVerification{} +func GetPayloadCommitVerification(commit *git.Commit) *structs.PayloadCommitVerification { + verification := &structs.PayloadCommitVerification{} commitVerification := models.ParseCommitWithSignature(commit) if commit.Signature != nil { verification.Signature = commit.Signature.Signature diff --git a/vendor/code.gitea.io/sdk/gitea/admin_user.go b/modules/structs/admin_user.go similarity index 59% rename from vendor/code.gitea.io/sdk/gitea/admin_user.go rename to modules/structs/admin_user.go index a4df703b17..7a447e44f5 100644 --- a/vendor/code.gitea.io/sdk/gitea/admin_user.go +++ b/modules/structs/admin_user.go @@ -3,13 +3,7 @@ // 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" -) +package structs // CreateUserOption create user options type CreateUserOption struct { @@ -27,16 +21,6 @@ type CreateUserOption struct { SendNotify bool `json:"send_notify"` } -// AdminCreateUser create a user -func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - user := new(User) - return user, c.getParsedResponse("POST", "/admin/users", jsonHeader, bytes.NewReader(body), user) -} - // EditUserOption edit user options type EditUserOption struct { SourceID int64 `json:"source_id"` @@ -57,29 +41,3 @@ type EditUserOption struct { ProhibitLogin *bool `json:"prohibit_login"` AllowCreateOrganization *bool `json:"allow_create_organization"` } - -// AdminEditUser modify user informations -func (c *Client) AdminEditUser(user string, opt EditUserOption) error { - body, err := json.Marshal(&opt) - if err != nil { - return err - } - _, err = c.getResponse("PATCH", fmt.Sprintf("/admin/users/%s", user), jsonHeader, bytes.NewReader(body)) - return err -} - -// AdminDeleteUser delete one user according name -func (c *Client) AdminDeleteUser(user string) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil) - return err -} - -// AdminCreateUserPublicKey create one user with options -func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - key := new(PublicKey) - return key, c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/keys", user), jsonHeader, bytes.NewReader(body), key) -} diff --git a/modules/structs/attachment.go b/modules/structs/attachment.go new file mode 100644 index 0000000000..954956f328 --- /dev/null +++ b/modules/structs/attachment.go @@ -0,0 +1,27 @@ +// Copyright 2017 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 structs // import "code.gitea.io/gitea/modules/structs" +import ( + "time" +) + +// Attachment a generic attachment +// swagger:model +type Attachment struct { + ID int64 `json:"id"` + Name string `json:"name"` + Size int64 `json:"size"` + DownloadCount int64 `json:"download_count"` + // swagger:strfmt date-time + Created time.Time `json:"created_at"` + UUID string `json:"uuid"` + DownloadURL string `json:"browser_download_url"` +} + +// EditAttachmentOptions options for editing attachments +// swagger:model +type EditAttachmentOptions struct { + Name string `json:"name"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/doc.go b/modules/structs/doc.go similarity index 76% rename from vendor/code.gitea.io/sdk/gitea/doc.go rename to modules/structs/doc.go index 6bd327db46..4c2fdbf73d 100644 --- a/vendor/code.gitea.io/sdk/gitea/doc.go +++ b/modules/structs/doc.go @@ -2,4 +2,4 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea // import "code.gitea.io/sdk/gitea" +package structs diff --git a/modules/structs/fork.go b/modules/structs/fork.go new file mode 100644 index 0000000000..dd6e24e8b7 --- /dev/null +++ b/modules/structs/fork.go @@ -0,0 +1,11 @@ +// Copyright 2016 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 structs + +// CreateForkOption options for creating a fork +type CreateForkOption struct { + // organization name, if forking into an organization + Organization *string `json:"organization"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/git_blob.go b/modules/structs/git_blob.go similarity index 96% rename from vendor/code.gitea.io/sdk/gitea/git_blob.go rename to modules/structs/git_blob.go index abdd95aaff..5715f2585b 100644 --- a/vendor/code.gitea.io/sdk/gitea/git_blob.go +++ b/modules/structs/git_blob.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs // GitBlobResponse represents a git blob type GitBlobResponse struct { diff --git a/modules/structs/git_hook.go b/modules/structs/git_hook.go new file mode 100644 index 0000000000..8a36b0b266 --- /dev/null +++ b/modules/structs/git_hook.go @@ -0,0 +1,20 @@ +// Copyright 2019 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 structs + +// GitHook represents a Git repository hook +type GitHook struct { + Name string `json:"name"` + IsActive bool `json:"is_active"` + Content string `json:"content,omitempty"` +} + +// GitHookList represents a list of Git hooks +type GitHookList []*GitHook + +// EditGitHookOption options when modifying one Git hook +type EditGitHookOption struct { + Content string `json:"content"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/hook.go b/modules/structs/hook.go similarity index 83% rename from vendor/code.gitea.io/sdk/gitea/hook.go rename to modules/structs/hook.go index d36e4d4375..8dae578ec6 100644 --- a/vendor/code.gitea.io/sdk/gitea/hook.go +++ b/modules/structs/hook.go @@ -3,13 +3,11 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( - "bytes" "encoding/json" "errors" - "fmt" "strings" "time" ) @@ -36,30 +34,6 @@ type Hook struct { // HookList represents a list of API hook. type HookList []*Hook -// ListOrgHooks list all the hooks of one organization -func (c *Client) ListOrgHooks(org string) (HookList, error) { - hooks := make([]*Hook, 0, 10) - return hooks, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks", org), nil, nil, &hooks) -} - -// ListRepoHooks list all the hooks of one repository -func (c *Client) ListRepoHooks(user, repo string) (HookList, error) { - hooks := make([]*Hook, 0, 10) - return hooks, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks", user, repo), nil, nil, &hooks) -} - -// GetOrgHook get a hook of an organization -func (c *Client) GetOrgHook(org string, id int64) (*Hook, error) { - h := new(Hook) - return h, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil, h) -} - -// GetRepoHook get a hook of a repository -func (c *Client) GetRepoHook(user, repo string, id int64) (*Hook, error) { - h := new(Hook) - return h, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil, h) -} - // CreateHookOption options when create a hook type CreateHookOption struct { // required: true @@ -72,26 +46,6 @@ type CreateHookOption struct { Active bool `json:"active"` } -// CreateOrgHook create one hook for an organization, with options -func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - h := new(Hook) - return h, c.getParsedResponse("POST", fmt.Sprintf("/orgs/%s/hooks", org), jsonHeader, bytes.NewReader(body), h) -} - -// CreateRepoHook create one hook for a repository, with options -func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - h := new(Hook) - return h, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/hooks", user, repo), jsonHeader, bytes.NewReader(body), h) -} - // EditHookOption options when modify one hook type EditHookOption struct { Config map[string]string `json:"config"` @@ -99,38 +53,6 @@ type EditHookOption struct { Active *bool `json:"active"` } -// EditOrgHook modify one hook of an organization, with hook id and options -func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) error { - body, err := json.Marshal(&opt) - if err != nil { - return err - } - _, err = c.getResponse("PATCH", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), jsonHeader, bytes.NewReader(body)) - return err -} - -// EditRepoHook modify one hook of a repository, with hook id and options -func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) error { - body, err := json.Marshal(&opt) - if err != nil { - return err - } - _, err = c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), jsonHeader, bytes.NewReader(body)) - return err -} - -// DeleteOrgHook delete one hook from an organization, with hook id -func (c *Client) DeleteOrgHook(org string, id int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/org/%s/hooks/%d", org, id), nil, nil) - return err -} - -// DeleteRepoHook delete one hook from a repository, with hook id -func (c *Client) DeleteRepoHook(user, repo string, id int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil) - return err -} - // Payloader payload is some part of one hook type Payloader interface { SetSecret(string) diff --git a/vendor/code.gitea.io/sdk/gitea/issue.go b/modules/structs/issue.go similarity index 52% rename from vendor/code.gitea.io/sdk/gitea/issue.go rename to modules/structs/issue.go index 0a1defb6f3..af6aa6e541 100644 --- a/vendor/code.gitea.io/sdk/gitea/issue.go +++ b/modules/structs/issue.go @@ -2,12 +2,9 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( - "bytes" - "encoding/json" - "fmt" "time" ) @@ -64,30 +61,6 @@ type ListIssueOption struct { State string } -// ListIssues returns all issues assigned the authenticated user -func (c *Client) ListIssues(opt ListIssueOption) ([]*Issue, error) { - issues := make([]*Issue, 0, 10) - return issues, c.getParsedResponse("GET", fmt.Sprintf("/issues?page=%d", opt.Page), nil, nil, &issues) -} - -// ListUserIssues returns all issues assigned to the authenticated user -func (c *Client) ListUserIssues(opt ListIssueOption) ([]*Issue, error) { - issues := make([]*Issue, 0, 10) - return issues, c.getParsedResponse("GET", fmt.Sprintf("/user/issues?page=%d", opt.Page), nil, nil, &issues) -} - -// ListRepoIssues returns all issues for a given repository -func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Issue, error) { - issues := make([]*Issue, 0, 10) - return issues, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues?page=%d", owner, repo, opt.Page), nil, nil, &issues) -} - -// GetIssue returns a single issue for a given repository -func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, error) { - issue := new(Issue) - return issue, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue) -} - // CreateIssueOption options to create one issue type CreateIssueOption struct { // required:true @@ -105,17 +78,6 @@ type CreateIssueOption struct { Closed bool `json:"closed"` } -// CreateIssue create a new issue for a given repository -func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - issue := new(Issue) - return issue, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues", owner, repo), - jsonHeader, bytes.NewReader(body), issue) -} - // EditIssueOption options for editing an issue type EditIssueOption struct { Title string `json:"title"` @@ -128,33 +90,6 @@ type EditIssueOption struct { Deadline *time.Time `json:"due_date"` } -// EditIssue modify an existing issue for a given repository -func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption) (*Issue, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - issue := new(Issue) - return issue, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), - jsonHeader, bytes.NewReader(body), issue) -} - -// StartIssueStopWatch starts a stopwatch for an existing issue for a given -// repository -func (c *Client) StartIssueStopWatch(owner, repo string, index int64) error { - _, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index), - jsonHeader, nil) - return err -} - -// StopIssueStopWatch stops an existing stopwatch for an issue in a given -// repository -func (c *Client) StopIssueStopWatch(owner, repo string, index int64) error { - _, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index), - jsonHeader, nil) - return err -} - // EditDeadlineOption options for creating a deadline type EditDeadlineOption struct { // required:true diff --git a/modules/structs/issue_comment.go b/modules/structs/issue_comment.go new file mode 100644 index 0000000000..185f3910ed --- /dev/null +++ b/modules/structs/issue_comment.go @@ -0,0 +1,35 @@ +// Copyright 2016 The Gogs 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 structs + +import ( + "time" +) + +// Comment represents a comment on a commit or issue +type Comment struct { + ID int64 `json:"id"` + HTMLURL string `json:"html_url"` + PRURL string `json:"pull_request_url"` + IssueURL string `json:"issue_url"` + Poster *User `json:"user"` + Body string `json:"body"` + // swagger:strfmt date-time + Created time.Time `json:"created_at"` + // swagger:strfmt date-time + Updated time.Time `json:"updated_at"` +} + +// CreateIssueCommentOption options for creating a comment on an issue +type CreateIssueCommentOption struct { + // required:true + Body string `json:"body" binding:"Required"` +} + +// EditIssueCommentOption options for editing a comment +type EditIssueCommentOption struct { + // required: true + Body string `json:"body" binding:"Required"` +} diff --git a/modules/structs/issue_label.go b/modules/structs/issue_label.go new file mode 100644 index 0000000000..f0821fbaf5 --- /dev/null +++ b/modules/structs/issue_label.go @@ -0,0 +1,36 @@ +// Copyright 2016 The Gogs 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 structs + +// Label a label to an issue or a pr +// swagger:model +type Label struct { + ID int64 `json:"id"` + Name string `json:"name"` + // example: 00aabb + Color string `json:"color"` + URL string `json:"url"` +} + +// CreateLabelOption options for creating a label +type CreateLabelOption struct { + // required:true + Name string `json:"name" binding:"Required"` + // required:true + // example: #00aabb + Color string `json:"color" binding:"Required;Size(7)"` +} + +// EditLabelOption options for editing a label +type EditLabelOption struct { + Name *string `json:"name"` + Color *string `json:"color"` +} + +// IssueLabelsOption a collection of labels +type IssueLabelsOption struct { + // list of label IDs + Labels []int64 `json:"labels"` +} diff --git a/modules/structs/issue_milestone.go b/modules/structs/issue_milestone.go new file mode 100644 index 0000000000..2bfdcd6bff --- /dev/null +++ b/modules/structs/issue_milestone.go @@ -0,0 +1,39 @@ +// Copyright 2016 The Gogs 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 structs + +import ( + "time" +) + +// Milestone milestone is a collection of issues on one repository +type Milestone struct { + ID int64 `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + State StateType `json:"state"` + OpenIssues int `json:"open_issues"` + ClosedIssues int `json:"closed_issues"` + // swagger:strfmt date-time + Closed *time.Time `json:"closed_at"` + // swagger:strfmt date-time + Deadline *time.Time `json:"due_on"` +} + +// CreateMilestoneOption options for creating a milestone +type CreateMilestoneOption struct { + Title string `json:"title"` + Description string `json:"description"` + // swagger:strfmt date-time + Deadline *time.Time `json:"due_on"` +} + +// EditMilestoneOption options for editing a milestone +type EditMilestoneOption struct { + Title string `json:"title"` + Description *string `json:"description"` + State *string `json:"state"` + Deadline *time.Time `json:"due_on"` +} diff --git a/modules/structs/issue_tracked_time.go b/modules/structs/issue_tracked_time.go new file mode 100644 index 0000000000..be90b36267 --- /dev/null +++ b/modules/structs/issue_tracked_time.go @@ -0,0 +1,30 @@ +// Copyright 2017 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 structs + +import ( + "time" +) + +// TrackedTime worked time for an issue / pr +type TrackedTime struct { + ID int64 `json:"id"` + // swagger:strfmt date-time + Created time.Time `json:"created"` + // Time in seconds + Time int64 `json:"time"` + UserID int64 `json:"user_id"` + IssueID int64 `json:"issue_id"` +} + +// TrackedTimes represent a list of tracked times +type TrackedTimes []*TrackedTime + +// AddTimeOption options for adding time to an issue +type AddTimeOption struct { + // time in seconds + // required: true + Time int64 `json:"time" binding:"Required"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/lfs_lock.go b/modules/structs/lfs_lock.go similarity index 99% rename from vendor/code.gitea.io/sdk/gitea/lfs_lock.go rename to modules/structs/lfs_lock.go index 356636a3a2..3709106315 100644 --- a/vendor/code.gitea.io/sdk/gitea/lfs_lock.go +++ b/modules/structs/lfs_lock.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( "time" diff --git a/vendor/code.gitea.io/sdk/gitea/miscellaneous.go b/modules/structs/miscellaneous.go similarity index 81% rename from vendor/code.gitea.io/sdk/gitea/miscellaneous.go rename to modules/structs/miscellaneous.go index 3735047b7f..8eca90330e 100644 --- a/vendor/code.gitea.io/sdk/gitea/miscellaneous.go +++ b/modules/structs/miscellaneous.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs // SearchResults results of a successful search type SearchResults struct { @@ -44,9 +44,3 @@ type MarkdownRender string type ServerVersion struct { Version string `json:"version"` } - -// ServerVersion returns the version of the server -func (c *Client) ServerVersion() (string, error) { - v := ServerVersion{} - return v.Version, c.getParsedResponse("GET", "/api/v1/version", nil, nil, &v) -} diff --git a/vendor/code.gitea.io/sdk/gitea/org.go b/modules/structs/org.go similarity index 53% rename from vendor/code.gitea.io/sdk/gitea/org.go rename to modules/structs/org.go index 0897da3ef6..fd15da1ce9 100644 --- a/vendor/code.gitea.io/sdk/gitea/org.go +++ b/modules/structs/org.go @@ -2,13 +2,7 @@ // 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" -) +package structs // Organization represents an organization type Organization struct { @@ -22,24 +16,6 @@ type Organization struct { Visibility VisibleType `json:"visibility"` } -// ListMyOrgs list all of current user's organizations -func (c *Client) ListMyOrgs() ([]*Organization, error) { - orgs := make([]*Organization, 0, 5) - return orgs, c.getParsedResponse("GET", "/user/orgs", nil, nil, &orgs) -} - -// ListUserOrgs list all of some user's organizations -func (c *Client) ListUserOrgs(user string) ([]*Organization, error) { - orgs := make([]*Organization, 0, 5) - return orgs, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs", user), nil, nil, &orgs) -} - -// GetOrg get one organization by name -func (c *Client) GetOrg(orgname string) (*Organization, error) { - org := new(Organization) - return org, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s", orgname), nil, nil, org) -} - // CreateOrgOption options for creating an organization type CreateOrgOption struct { // required: true @@ -58,13 +34,3 @@ type EditOrgOption struct { Website string `json:"website"` Location string `json:"location"` } - -// EditOrg modify one organization via options -func (c *Client) EditOrg(orgname string, opt EditOrgOption) error { - body, err := json.Marshal(&opt) - if err != nil { - return err - } - _, err = c.getResponse("PATCH", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, bytes.NewReader(body)) - return err -} diff --git a/modules/structs/org_member.go b/modules/structs/org_member.go new file mode 100644 index 0000000000..3b2a8b599a --- /dev/null +++ b/modules/structs/org_member.go @@ -0,0 +1,10 @@ +// Copyright 2016 The Gogs 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 structs + +// AddOrgMembershipOption add user to organization options +type AddOrgMembershipOption struct { + Role string `json:"role" binding:"Required"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/org_team.go b/modules/structs/org_team.go similarity index 98% rename from vendor/code.gitea.io/sdk/gitea/org_team.go rename to modules/structs/org_team.go index 9de0a8d000..b8b5090225 100644 --- a/vendor/code.gitea.io/sdk/gitea/org_team.go +++ b/modules/structs/org_team.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs // Team represents a team in an organization type Team struct { diff --git a/vendor/code.gitea.io/sdk/gitea/pull.go b/modules/structs/pull.go similarity index 57% rename from vendor/code.gitea.io/sdk/gitea/pull.go rename to modules/structs/pull.go index 6fcdd1d41b..722d245afc 100644 --- a/vendor/code.gitea.io/sdk/gitea/pull.go +++ b/modules/structs/pull.go @@ -2,12 +2,9 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( - "bytes" - "encoding/json" - "fmt" "time" ) @@ -67,22 +64,6 @@ type ListPullRequestsOptions struct { State string `json:"state"` } -// ListRepoPullRequests list PRs of one repository -func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - prs := make([]*PullRequest, 0, 10) - return prs, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls", owner, repo), jsonHeader, bytes.NewReader(body), &prs) -} - -// GetPullRequest get information of one PR -func (c *Client) GetPullRequest(owner, repo string, index int64) (*PullRequest, error) { - pr := new(PullRequest) - return pr, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index), nil, nil, pr) -} - // CreatePullRequestOption options when creating a pull request type CreatePullRequestOption struct { Head string `json:"head" binding:"Required"` @@ -97,17 +78,6 @@ type CreatePullRequestOption struct { Deadline *time.Time `json:"due_date"` } -// CreatePullRequest create pull request with options -func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOption) (*PullRequest, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - pr := new(PullRequest) - return pr, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/pulls", owner, repo), - jsonHeader, bytes.NewReader(body), pr) -} - // EditPullRequestOption options when modify pull request type EditPullRequestOption struct { Title string `json:"title"` @@ -120,32 +90,3 @@ type EditPullRequestOption struct { // swagger:strfmt date-time Deadline *time.Time `json:"due_date"` } - -// EditPullRequest modify pull request with PR id and options -func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRequestOption) (*PullRequest, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - pr := new(PullRequest) - return pr, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), - jsonHeader, bytes.NewReader(body), pr) -} - -// MergePullRequest merge a PR to repository by PR id -func (c *Client) MergePullRequest(owner, repo string, index int64) error { - _, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil) - return err -} - -// IsPullRequestMerged test if one PR is merged to one repository -func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, error) { - statusCode, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil) - - if err != nil { - return false, err - } - - return statusCode == 204, nil - -} diff --git a/modules/structs/release.go b/modules/structs/release.go new file mode 100644 index 0000000000..b7575af39a --- /dev/null +++ b/modules/structs/release.go @@ -0,0 +1,50 @@ +// Copyright 2016 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 structs + +import ( + "time" +) + +// Release represents a repository release +type Release struct { + ID int64 `json:"id"` + TagName string `json:"tag_name"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + URL string `json:"url"` + TarURL string `json:"tarball_url"` + ZipURL string `json:"zipball_url"` + IsDraft bool `json:"draft"` + IsPrerelease bool `json:"prerelease"` + // swagger:strfmt date-time + CreatedAt time.Time `json:"created_at"` + // swagger:strfmt date-time + PublishedAt time.Time `json:"published_at"` + Publisher *User `json:"author"` + Attachments []*Attachment `json:"assets"` +} + +// CreateReleaseOption options when creating a release +type CreateReleaseOption struct { + // required: true + TagName string `json:"tag_name" binding:"Required"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + IsDraft bool `json:"draft"` + IsPrerelease bool `json:"prerelease"` +} + +// EditReleaseOption options when editing a release +type EditReleaseOption struct { + TagName string `json:"tag_name"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + IsDraft *bool `json:"draft"` + IsPrerelease *bool `json:"prerelease"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/repo.go b/modules/structs/repo.go similarity index 50% rename from vendor/code.gitea.io/sdk/gitea/repo.go rename to modules/structs/repo.go index 8b7c0b1e68..b5283beeaa 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo.go +++ b/modules/structs/repo.go @@ -2,12 +2,9 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( - "bytes" - "encoding/json" - "fmt" "time" ) @@ -48,24 +45,6 @@ type Repository struct { Permissions *Permission `json:"permissions,omitempty"` } -// ListMyRepos lists all repositories for the authenticated user that has access to. -func (c *Client) ListMyRepos() ([]*Repository, error) { - repos := make([]*Repository, 0, 10) - return repos, c.getParsedResponse("GET", "/user/repos", nil, nil, &repos) -} - -// ListUserRepos list all repositories of one user by user's name -func (c *Client) ListUserRepos(user string) ([]*Repository, error) { - repos := make([]*Repository, 0, 10) - return repos, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos", user), nil, nil, &repos) -} - -// ListOrgRepos list all repositories of one organization by organization's name -func (c *Client) ListOrgRepos(org string) ([]*Repository, error) { - repos := make([]*Repository, 0, 10) - return repos, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos", org), nil, nil, &repos) -} - // CreateRepoOption options when creating repository // swagger:model type CreateRepoOption struct { @@ -88,36 +67,42 @@ type CreateRepoOption struct { Readme string `json:"readme"` } -// CreateRepo creates a repository for authenticated user. -func (c *Client) CreateRepo(opt CreateRepoOption) (*Repository, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - repo := new(Repository) - return repo, c.getParsedResponse("POST", "/user/repos", jsonHeader, bytes.NewReader(body), repo) -} - -// CreateOrgRepo creates an organization repository for authenticated user. -func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - repo := new(Repository) - return repo, c.getParsedResponse("POST", fmt.Sprintf("/org/%s/repos", org), jsonHeader, bytes.NewReader(body), repo) -} - -// GetRepo returns information of a repository of given owner. -func (c *Client) GetRepo(owner, reponame string) (*Repository, error) { - repo := new(Repository) - return repo, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo) -} - -// DeleteRepo deletes a repository of user or organization. -func (c *Client) DeleteRepo(owner, repo string) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil) - return err +// EditRepoOption options when editing a repository's properties +// swagger:model +type EditRepoOption struct { + // Name of the repository + // + // required: true + // unique: true + Name *string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"` + // A short description of the repository. + Description *string `json:"description,omitempty" binding:"MaxSize(255)"` + // A URL with more information about the repository. + Website *string `json:"website,omitempty" binding:"MaxSize(255)"` + // 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 enable issues for this repository or `false` to disable them. + EnableIssues *bool `json:"enable_issues,omitempty"` + // Either `true` to enable the wiki for this repository or `false` to disable it. + EnableWiki *bool `json:"enable_wiki,omitempty"` + // Updates the default branch for this repository. + DefaultBranch *string `json:"default_branch,omitempty"` + // Either `true` to allow pull requests, or `false` to prevent pull request. + EnablePullRequests *bool `json:"enable_pull_requests,omitempty"` + // Either `true` to ignore whitepace for conflicts, or `false` to not ignore whitespace. `enabled_pull_requests` must be `true`. + IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace,omitempty"` + // Either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `enabled_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. `enabled_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. `enabled_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. `enabled_pull_requests` must be `true`. + AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` + // `true` to archive this repository. Note: You cannot unarchive repositories through the API. + Archived *bool `json:"archived,omitempty"` } // MigrateRepoOption options for migrating a repository from an external service @@ -134,23 +119,3 @@ type MigrateRepoOption struct { Private bool `json:"private"` Description string `json:"description"` } - -// MigrateRepo migrates a repository from other Git hosting sources for the -// authenticated user. -// -// To migrate a repository for a organization, the authenticated user must be a -// owner of the specified organization. -func (c *Client) MigrateRepo(opt MigrateRepoOption) (*Repository, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - repo := new(Repository) - return repo, c.getParsedResponse("POST", "/repos/migrate", jsonHeader, bytes.NewReader(body), repo) -} - -// MirrorSync adds a mirrored repository to the mirror sync queue. -func (c *Client) MirrorSync(owner, repo string) error { - _, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), nil, nil) - return err -} diff --git a/modules/structs/repo_branch.go b/modules/structs/repo_branch.go new file mode 100644 index 0000000000..a6ae6c1663 --- /dev/null +++ b/modules/structs/repo_branch.go @@ -0,0 +1,11 @@ +// Copyright 2016 The Gogs 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 structs + +// Branch represents a repository branch +type Branch struct { + Name string `json:"name"` + Commit *PayloadCommit `json:"commit"` +} diff --git a/modules/structs/repo_collaborator.go b/modules/structs/repo_collaborator.go new file mode 100644 index 0000000000..2b4fa390d2 --- /dev/null +++ b/modules/structs/repo_collaborator.go @@ -0,0 +1,10 @@ +// Copyright 2016 The Gogs 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 structs + +// AddCollaboratorOption options when adding a user as a collaborator of a repository +type AddCollaboratorOption struct { + Permission *string `json:"permission"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_commit.go b/modules/structs/repo_commit.go similarity index 81% rename from vendor/code.gitea.io/sdk/gitea/repo_commit.go rename to modules/structs/repo_commit.go index 1df66428aa..9cde2873d4 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo_commit.go +++ b/modules/structs/repo_commit.go @@ -3,11 +3,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea - -import ( - "fmt" -) +package structs // Identity for a person's identity like an author or committer type Identity struct { @@ -46,9 +42,3 @@ type Commit struct { Committer *User `json:"committer"` Parents []*CommitMeta `json:"parents"` } - -// GetSingleCommit returns a single commit -func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, error) { - commit := new(Commit) - return commit, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit) -} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_file.go b/modules/structs/repo_file.go similarity index 88% rename from vendor/code.gitea.io/sdk/gitea/repo_file.go rename to modules/structs/repo_file.go index 1e9e45ec24..ac8b9333fe 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo_file.go +++ b/modules/structs/repo_file.go @@ -3,17 +3,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea - -import ( - "fmt" -) - -// GetFile downloads a file of repository, ref can be branch/tag/commit. -// e.g.: ref -> master, tree -> macaron.go(no leading slash) -func (c *Client) GetFile(user, repo, ref, tree string) ([]byte, error) { - return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s/%s", user, repo, ref, tree), nil, nil) -} +package structs // FileOptions options for all file APIs type FileOptions struct { diff --git a/modules/structs/repo_key.go b/modules/structs/repo_key.go new file mode 100644 index 0000000000..29928bd97e --- /dev/null +++ b/modules/structs/repo_key.go @@ -0,0 +1,41 @@ +// Copyright 2015 The Gogs 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 structs + +import ( + "time" +) + +// DeployKey a deploy key +type DeployKey struct { + ID int64 `json:"id"` + KeyID int64 `json:"key_id"` + Key string `json:"key"` + URL string `json:"url"` + Title string `json:"title"` + Fingerprint string `json:"fingerprint"` + // swagger:strfmt date-time + Created time.Time `json:"created_at"` + ReadOnly bool `json:"read_only"` + Repository *Repository `json:"repository,omitempty"` +} + +// CreateKeyOption options when creating a key +type CreateKeyOption struct { + // Title of the key to add + // + // required: true + // unique: true + Title string `json:"title" binding:"Required"` + // An armored SSH key to add + // + // required: true + // unique: true + Key string `json:"key" binding:"Required"` + // Describe if the key has only read access or read/write + // + // required: false + ReadOnly bool `json:"read_only"` +} diff --git a/modules/structs/repo_refs.go b/modules/structs/repo_refs.go new file mode 100644 index 0000000000..0bf4b94ae1 --- /dev/null +++ b/modules/structs/repo_refs.go @@ -0,0 +1,19 @@ +// Copyright 2018 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 structs + +// Reference represents a Git reference. +type Reference struct { + Ref string `json:"ref"` + URL string `json:"url"` + Object *GitObject `json:"object"` +} + +// GitObject represents a Git object. +type GitObject struct { + Type string `json:"type"` + SHA string `json:"sha"` + URL string `json:"url"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_tag.go b/modules/structs/repo_tag.go similarity index 58% rename from vendor/code.gitea.io/sdk/gitea/repo_tag.go rename to modules/structs/repo_tag.go index 27efe8da2e..6294a8099d 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo_tag.go +++ b/modules/structs/repo_tag.go @@ -2,11 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea - -import ( - "fmt" -) +package structs // Tag represents a repository tag type Tag struct { @@ -18,9 +14,3 @@ type Tag struct { ZipballURL string `json:"zipball_url"` TarballURL string `json:"tarball_url"` } - -// ListRepoTags list all the branches of one repository -func (c *Client) ListRepoTags(user, repo string) ([]*Tag, error) { - tags := make([]*Tag, 0, 10) - return tags, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags", user, repo), nil, nil, &tags) -} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_tree.go b/modules/structs/repo_tree.go similarity index 58% rename from vendor/code.gitea.io/sdk/gitea/repo_tree.go rename to modules/structs/repo_tree.go index 842ab9b438..58aa3b8339 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo_tree.go +++ b/modules/structs/repo_tree.go @@ -2,11 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea - -import ( - "fmt" -) +package structs // GitEntry represents a git tree type GitEntry struct { @@ -27,15 +23,3 @@ type GitTreeResponse struct { Page int `json:"page"` TotalCount int `json:"total_count"` } - -// GetTrees downloads a file of repository, ref can be branch/tag/commit. -// e.g.: ref -> master, tree -> macaron.go(no leading slash) -func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, error) { - var trees GitTreeResponse - var path = fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref) - if recursive { - path += "?recursive=1" - } - err := c.getParsedResponse("GET", path, nil, nil, &trees) - return &trees, err -} diff --git a/modules/structs/repo_watch.go b/modules/structs/repo_watch.go new file mode 100644 index 0000000000..606785a4fd --- /dev/null +++ b/modules/structs/repo_watch.go @@ -0,0 +1,19 @@ +// Copyright 2017 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 structs + +import ( + "time" +) + +// WatchInfo represents an API watch status of one repository +type WatchInfo struct { + Subscribed bool `json:"subscribed"` + Ignored bool `json:"ignored"` + Reason interface{} `json:"reason"` + CreatedAt time.Time `json:"created_at"` + URL string `json:"url"` + RepositoryURL string `json:"repository_url"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/status.go b/modules/structs/status.go similarity index 62% rename from vendor/code.gitea.io/sdk/gitea/status.go rename to modules/structs/status.go index 3060ab1b2b..e833bd69e5 100644 --- a/vendor/code.gitea.io/sdk/gitea/status.go +++ b/modules/structs/status.go @@ -2,12 +2,9 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( - "bytes" - "encoding/json" - "fmt" "time" ) @@ -66,32 +63,3 @@ type CreateStatusOption struct { type ListStatusesOption struct { Page int } - -// CreateStatus creates a new Status for a given Commit -// -// POST /repos/:owner/:repo/statuses/:sha -func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, error) { - body, err := json.Marshal(&opts) - if err != nil { - return nil, err - } - status := &Status{} - return status, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, sha), - jsonHeader, bytes.NewReader(body), status) -} - -// ListStatuses returns all statuses for a given Commit -// -// GET /repos/:owner/:repo/commits/:ref/statuses -func (c *Client) ListStatuses(owner, repo, sha string, opts ListStatusesOption) ([]*Status, error) { - statuses := make([]*Status, 0, 10) - return statuses, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?page=%d", owner, repo, sha, opts.Page), nil, nil, &statuses) -} - -// GetCombinedStatus returns the CombinedStatus for a given Commit -// -// GET /repos/:owner/:repo/commits/:ref/status -func (c *Client) GetCombinedStatus(owner, repo, sha string) (*CombinedStatus, error) { - status := &CombinedStatus{} - return status, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, sha), nil, nil, status) -} diff --git a/vendor/code.gitea.io/sdk/gitea/user.go b/modules/structs/user.go similarity index 69% rename from vendor/code.gitea.io/sdk/gitea/user.go rename to modules/structs/user.go index 42d9b983e3..251c803129 100644 --- a/vendor/code.gitea.io/sdk/gitea/user.go +++ b/modules/structs/user.go @@ -2,11 +2,10 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( "encoding/json" - "fmt" ) // User represents a user @@ -37,17 +36,3 @@ func (u User) MarshalJSON() ([]byte, error) { CompatUserName string `json:"username"` }{shadow(u), u.UserName}) } - -// GetUserInfo get user info by user's name -func (c *Client) GetUserInfo(user string) (*User, error) { - u := new(User) - err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s", user), nil, nil, u) - return u, err -} - -// GetMyUserInfo get user info of current user -func (c *Client) GetMyUserInfo() (*User, error) { - u := new(User) - err := c.getParsedResponse("GET", "/user", nil, nil, u) - return u, err -} diff --git a/modules/structs/user_app.go b/modules/structs/user_app.go new file mode 100644 index 0000000000..9340486685 --- /dev/null +++ b/modules/structs/user_app.go @@ -0,0 +1,34 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2019 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 structs + +import ( + "encoding/base64" +) + +// BasicAuthEncode generate base64 of basic auth head +func BasicAuthEncode(user, pass string) string { + return base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)) +} + +// AccessToken represents an API access token. +// swagger:response AccessToken +type AccessToken struct { + ID int64 `json:"id"` + Name string `json:"name"` + Token string `json:"sha1"` + TokenLastEight string `json:"token_last_eight"` +} + +// AccessTokenList represents a list of API access token. +// swagger:response AccessTokenList +type AccessTokenList []*AccessToken + +// CreateAccessTokenOption options when create access token +// swagger:parameters userCreateToken +type CreateAccessTokenOption struct { + Name string `json:"name" binding:"Required"` +} diff --git a/modules/structs/user_email.go b/modules/structs/user_email.go new file mode 100644 index 0000000000..a72b04119a --- /dev/null +++ b/modules/structs/user_email.go @@ -0,0 +1,25 @@ +// Copyright 2015 The Gogs 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 structs + +// Email an email address belonging to a user +type Email struct { + // swagger:strfmt email + Email string `json:"email"` + Verified bool `json:"verified"` + Primary bool `json:"primary"` +} + +// CreateEmailOption options when creating email addresses +type CreateEmailOption struct { + // email addresses to add + Emails []string `json:"emails"` +} + +// DeleteEmailOption options when deleting email addresses +type DeleteEmailOption struct { + // email addresses to delete + Emails []string `json:"emails"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go b/modules/structs/user_gpgkey.go similarity index 52% rename from vendor/code.gitea.io/sdk/gitea/user_gpgkey.go rename to modules/structs/user_gpgkey.go index 0817d89461..f501a09cb9 100644 --- a/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go +++ b/modules/structs/user_gpgkey.go @@ -2,12 +2,9 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( - "bytes" - "encoding/json" - "fmt" "time" ) @@ -44,37 +41,3 @@ type CreateGPGKeyOption struct { // unique: true ArmoredKey string `json:"armored_public_key" binding:"Required"` } - -// ListGPGKeys list all the GPG keys of the user -func (c *Client) ListGPGKeys(user string) ([]*GPGKey, error) { - keys := make([]*GPGKey, 0, 10) - return keys, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys", user), nil, nil, &keys) -} - -// ListMyGPGKeys list all the GPG keys of current user -func (c *Client) ListMyGPGKeys() ([]*GPGKey, error) { - keys := make([]*GPGKey, 0, 10) - return keys, c.getParsedResponse("GET", "/user/gpg_keys", nil, nil, &keys) -} - -// GetGPGKey get current user's GPG key by key id -func (c *Client) GetGPGKey(keyID int64) (*GPGKey, error) { - key := new(GPGKey) - return key, c.getParsedResponse("GET", fmt.Sprintf("/user/gpg_keys/%d", keyID), nil, nil, &key) -} - -// CreateGPGKey create GPG key with options -func (c *Client) CreateGPGKey(opt CreateGPGKeyOption) (*GPGKey, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - key := new(GPGKey) - return key, c.getParsedResponse("POST", "/user/gpg_keys", jsonHeader, bytes.NewReader(body), key) -} - -// DeleteGPGKey delete GPG key with key id -func (c *Client) DeleteGPGKey(keyID int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/user/gpg_keys/%d", keyID), nil, nil) - return err -} diff --git a/modules/structs/user_key.go b/modules/structs/user_key.go new file mode 100644 index 0000000000..ee487607c3 --- /dev/null +++ b/modules/structs/user_key.go @@ -0,0 +1,23 @@ +// Copyright 2015 The Gogs 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 structs + +import ( + "time" +) + +// PublicKey publickey is a user key to push code to repository +type PublicKey struct { + ID int64 `json:"id"` + Key string `json:"key"` + URL string `json:"url,omitempty"` + Title string `json:"title,omitempty"` + Fingerprint string `json:"fingerprint,omitempty"` + // swagger:strfmt date-time + Created time.Time `json:"created_at,omitempty"` + Owner *User `json:"user,omitempty"` + ReadOnly bool `json:"read_only,omitempty"` + KeyType string `json:"key_type,omitempty"` +} diff --git a/modules/structs/user_search.go b/modules/structs/user_search.go new file mode 100644 index 0000000000..1650cf736a --- /dev/null +++ b/modules/structs/user_search.go @@ -0,0 +1,5 @@ +package structs + +type searchUsersResponse struct { + Users []*User `json:"data"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/utils.go b/modules/structs/utils.go similarity index 96% rename from vendor/code.gitea.io/sdk/gitea/utils.go rename to modules/structs/utils.go index 80892a1e7a..1b9d689562 100644 --- a/vendor/code.gitea.io/sdk/gitea/utils.go +++ b/modules/structs/utils.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gitea +package structs import ( "net/http" diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index 03263a86dd..fba41a8cfe 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -6,7 +6,7 @@ package admin import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/admin/repo.go b/routers/api/v1/admin/repo.go index d883e2cbd0..aaa7957a50 100644 --- a/routers/api/v1/admin/repo.go +++ b/routers/api/v1/admin/repo.go @@ -5,7 +5,7 @@ package admin import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/routers/api/v1/repo" diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 0c7088151f..6dc3b0325b 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -10,9 +10,9 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/convert" "code.gitea.io/gitea/routers/api/v1/user" - api "code.gitea.io/sdk/gitea" ) func parseLoginSource(ctx *context.APIContext, u *models.User, sourceID int64, loginName string) { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index b68bf4b0b8..dfe705f7a8 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -65,13 +65,13 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/admin" "code.gitea.io/gitea/routers/api/v1/misc" "code.gitea.io/gitea/routers/api/v1/org" "code.gitea.io/gitea/routers/api/v1/repo" _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation "code.gitea.io/gitea/routers/api/v1/user" - api "code.gitea.io/sdk/gitea" "github.com/go-macaron/binding" "gopkg.in/macaron.v1" diff --git a/routers/api/v1/convert/convert.go b/routers/api/v1/convert/convert.go index f132dedb55..74fd9b3afd 100644 --- a/routers/api/v1/convert/convert.go +++ b/routers/api/v1/convert/convert.go @@ -11,8 +11,8 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" ) diff --git a/routers/api/v1/misc/markdown.go b/routers/api/v1/misc/markdown.go index 9ae7a6c58c..06e344a15b 100644 --- a/routers/api/v1/misc/markdown.go +++ b/routers/api/v1/misc/markdown.go @@ -7,7 +7,7 @@ package misc import ( "strings" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/markup/markdown" diff --git a/routers/api/v1/misc/markdown_test.go b/routers/api/v1/misc/markdown_test.go index 9de1f77f60..6cb6b92f9f 100644 --- a/routers/api/v1/misc/markdown_test.go +++ b/routers/api/v1/misc/markdown_test.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" "github.com/go-macaron/inject" "github.com/stretchr/testify/assert" diff --git a/routers/api/v1/misc/version.go b/routers/api/v1/misc/version.go index 20e0a60c72..803f5ac716 100644 --- a/routers/api/v1/misc/version.go +++ b/routers/api/v1/misc/version.go @@ -7,7 +7,7 @@ package misc import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/sdk/gitea" + "code.gitea.io/gitea/modules/structs" ) // Version shows the version of the Gitea server @@ -20,5 +20,5 @@ func Version(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/ServerVersion" - ctx.JSON(200, &gitea.ServerVersion{Version: setting.AppVer}) + ctx.JSON(200, &structs.ServerVersion{Version: setting.AppVer}) } diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index 5825de2c1c..128cfec328 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -5,7 +5,7 @@ package org import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 33c906ec6a..ad60dfbda5 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -7,7 +7,7 @@ package org import ( "fmt" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index e3916046f0..e1d0663f05 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -6,7 +6,7 @@ package org import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 340dd96c7e..40b6e008b9 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -6,7 +6,7 @@ package org import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index b9a23d3b51..1aaae8723b 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/routers/api/v1/convert" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetBranch get a branch of a repository diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index be4a65f9c8..98daf2c9d5 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListCollaborators list a repository's collaborators diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index d29f188535..3f3bd13268 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetSingleCommit get a commit via diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 10108e11c3..db952263e2 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -13,8 +13,8 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/repofiles" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/repo" - api "code.gitea.io/sdk/gitea" ) // GetRawFile get a file by path on a repository diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index d10f668712..c004544f58 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListForks list a repository's forks diff --git a/routers/api/v1/repo/git_hook.go b/routers/api/v1/repo/git_hook.go index b7a8691cd3..80610356dd 100644 --- a/routers/api/v1/repo/git_hook.go +++ b/routers/api/v1/repo/git_hook.go @@ -7,8 +7,8 @@ package repo import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/convert" - api "code.gitea.io/sdk/gitea" ) // ListGitHooks list all Git hooks of a repository diff --git a/routers/api/v1/repo/git_ref.go b/routers/api/v1/repo/git_ref.go index 6a6ed9ff36..2ec8749058 100644 --- a/routers/api/v1/repo/git_ref.go +++ b/routers/api/v1/repo/git_ref.go @@ -7,7 +7,7 @@ package repo import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetGitAllRefs get ref or an list all the refs of a repository diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index ef9d5ac1d3..b35a10ac3d 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -8,9 +8,9 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/convert" "code.gitea.io/gitea/routers/api/v1/utils" - api "code.gitea.io/sdk/gitea" ) // ListHooks list all hooks of a repository diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index c14fc78ee4..daaa3d5985 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -18,7 +18,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListIssues list the issues of a repository diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 7a720deab2..14e81222e3 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/notification" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListIssueComments list all the comments of an issue diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 4c8ca8d523..7c22e6885c 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListIssueLabels list all the labels of an issue diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index d2ddb0e0c8..194f7d32ce 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) func trackedTimesToAPIFormat(trackedTimes []*models.TrackedTime) []*api.TrackedTime { diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index e26ce8cf50..59782d05f6 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -12,7 +12,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/api/v1/convert" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // appendPrivateInformation appends the owner and key type information to api.PublicKey diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index f61751f3ab..1cd8890ca9 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListLabels list all the labels of a repository diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index a76f601a21..1d01093306 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListMilestones list all the opened milestones for a repository diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index ab8a85b64d..dda8387913 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -15,8 +15,8 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - api "code.gitea.io/sdk/gitea" ) // ListPullRequests returns a list of all PRs diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 1b5b19666c..0fdcfa355c 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetRelease get a single release of a repository diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index 5efdd69817..f85787bc59 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -13,7 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // GetReleaseAttachment gets a single attachment of the release diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 35fea20d49..56aac8014c 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -19,7 +19,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/convert" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) var searchOrderByMap = map[string]map[string]models.SearchOrderBy{ diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index df4241dde2..046142252b 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -7,7 +7,7 @@ package repo import ( "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListStargazers list a repository's stargazers diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index da2b459c21..487e62c91d 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // NewCommitStatus creates a new CommitStatus diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go index a3d5e2a270..b7d329dc78 100644 --- a/routers/api/v1/repo/subscriber.go +++ b/routers/api/v1/repo/subscriber.go @@ -7,7 +7,7 @@ package repo import ( "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListSubscribers list a repo's subscribers (i.e. watchers) diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 505f5b40c9..dd1b5aa7c1 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/routers/api/v1/convert" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ListTags list all the tags of a repository diff --git a/routers/api/v1/swagger/issue.go b/routers/api/v1/swagger/issue.go index 16711e9b7e..c06186bf6c 100644 --- a/routers/api/v1/swagger/issue.go +++ b/routers/api/v1/swagger/issue.go @@ -5,7 +5,7 @@ package swagger import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // Issue diff --git a/routers/api/v1/swagger/key.go b/routers/api/v1/swagger/key.go index b247832387..c3da37af63 100644 --- a/routers/api/v1/swagger/key.go +++ b/routers/api/v1/swagger/key.go @@ -5,7 +5,7 @@ package swagger import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // PublicKey diff --git a/routers/api/v1/swagger/misc.go b/routers/api/v1/swagger/misc.go index c28ba02dbb..9fd0ab5b00 100644 --- a/routers/api/v1/swagger/misc.go +++ b/routers/api/v1/swagger/misc.go @@ -5,7 +5,7 @@ package swagger import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // ServerVersion diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 83f7cfec8d..2df97304aa 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -6,7 +6,7 @@ package swagger import ( "code.gitea.io/gitea/modules/auth" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // not actually a response, just a hack to get go-swagger to include definitions diff --git a/routers/api/v1/swagger/org.go b/routers/api/v1/swagger/org.go index eb4a1084e2..c962e7b188 100644 --- a/routers/api/v1/swagger/org.go +++ b/routers/api/v1/swagger/org.go @@ -5,7 +5,7 @@ package swagger import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // Organization diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index a23e670dac..e7df0b8f71 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -5,7 +5,7 @@ package swagger import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // Repository diff --git a/routers/api/v1/swagger/user.go b/routers/api/v1/swagger/user.go index caaf7df705..a2df40e4cf 100644 --- a/routers/api/v1/swagger/user.go +++ b/routers/api/v1/swagger/user.go @@ -6,7 +6,7 @@ package swagger import ( "code.gitea.io/gitea/models" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // User diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index d9716f419a..c090a724b7 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -6,7 +6,7 @@ package user import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index 1d071e697f..67659cfb0f 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -5,7 +5,7 @@ package user import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index c2f0c6e651..453f73137d 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -5,7 +5,7 @@ package user import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 9d6579e413..c2c55e9b92 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -5,7 +5,7 @@ package user import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 8e0cde0535..286f9ae4c3 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -5,7 +5,7 @@ package user import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 1ddb3bd57b..5848169088 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -7,7 +7,7 @@ package user import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" ) // listUserRepos - List the repositories owned by the given user. diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index 62c820f8f4..e5eec71b60 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -5,7 +5,7 @@ package user import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 76b4fc8dcc..8d05a67185 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/convert" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" ) diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index 2ce0be2ff0..f0eb95ac1e 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -5,7 +5,7 @@ package user import ( - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index f0c6b91a18..da15850ec5 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -11,9 +11,9 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/convert" "code.gitea.io/gitea/routers/utils" - api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" ) diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go index 2e76cbfe02..7695f208ce 100644 --- a/routers/repo/webhook.go +++ b/routers/repo/webhook.go @@ -18,7 +18,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/sdk/gitea" + api "code.gitea.io/gitea/modules/structs" "github.com/Unknwon/com" ) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 198c18919e..0752e7be2b 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -6673,7 +6673,7 @@ "x-go-name": "Permission" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "AddTimeOption": { "description": "AddTimeOption options for adding time to an issue", @@ -6689,7 +6689,7 @@ "x-go-name": "Time" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Attachment": { "description": "Attachment a generic attachment", @@ -6728,7 +6728,7 @@ "x-go-name": "UUID" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Branch": { "description": "Branch represents a repository branch", @@ -6742,7 +6742,7 @@ "x-go-name": "Name" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Comment": { "description": "Comment represents a comment on a commit or issue", @@ -6783,7 +6783,7 @@ "$ref": "#/definitions/User" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Commit": { "type": "object", @@ -6818,7 +6818,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CommitMeta": { "type": "object", @@ -6833,7 +6833,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CommitUser": { "type": "object", @@ -6853,7 +6853,7 @@ "x-go-name": "Name" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateEmailOption": { "description": "CreateEmailOption options when creating email addresses", @@ -6868,7 +6868,7 @@ "x-go-name": "Emails" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateFileOptions": { "description": "CreateFileOptions options for creating files", @@ -6897,7 +6897,7 @@ "x-go-name": "NewBranchName" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateForkOption": { "description": "CreateForkOption options for creating a fork", @@ -6909,7 +6909,7 @@ "x-go-name": "Organization" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateGPGKeyOption": { "description": "CreateGPGKeyOption options create user GPG key", @@ -6925,7 +6925,7 @@ "x-go-name": "ArmoredKey" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateHookOption": { "description": "CreateHookOption options when create a hook", @@ -6965,7 +6965,7 @@ "x-go-name": "Type" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateIssueCommentOption": { "description": "CreateIssueCommentOption options for creating a comment on an issue", @@ -6979,7 +6979,7 @@ "x-go-name": "Body" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateIssueOption": { "description": "CreateIssueOption options to create one issue", @@ -7033,7 +7033,7 @@ "x-go-name": "Title" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateKeyOption": { "description": "CreateKeyOption options when creating a key", @@ -7061,7 +7061,7 @@ "x-go-name": "Title" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateLabelOption": { "description": "CreateLabelOption options for creating a label", @@ -7081,7 +7081,7 @@ "x-go-name": "Name" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateMilestoneOption": { "description": "CreateMilestoneOption options for creating a milestone", @@ -7101,7 +7101,7 @@ "x-go-name": "Title" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateOrgOption": { "description": "CreateOrgOption options for creating an organization", @@ -7134,7 +7134,7 @@ "x-go-name": "Website" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreatePullRequestOption": { "description": "CreatePullRequestOption options when creating a pull request", @@ -7186,7 +7186,7 @@ "x-go-name": "Title" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateReleaseOption": { "description": "CreateReleaseOption options when creating a release", @@ -7220,7 +7220,7 @@ "x-go-name": "Target" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateRepoOption": { "description": "CreateRepoOption options when creating repository", @@ -7266,7 +7266,7 @@ "x-go-name": "Readme" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateStatusOption": { "description": "CreateStatusOption holds the information needed to create a new Status for a Commit", @@ -7288,7 +7288,7 @@ "x-go-name": "TargetURL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateTeamOption": { "description": "CreateTeamOption options for creating a team", @@ -7331,7 +7331,7 @@ "x-go-name": "Units" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "CreateUserOption": { "description": "CreateUserOption create user options", @@ -7377,7 +7377,7 @@ "x-go-name": "Username" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "DeleteEmailOption": { "description": "DeleteEmailOption options when deleting email addresses", @@ -7392,7 +7392,7 @@ "x-go-name": "Emails" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "DeleteFileOptions": { "description": "DeleteFileOptions options for deleting files (used for other File structs below)", @@ -7421,7 +7421,7 @@ "x-go-name": "SHA" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "DeployKey": { "description": "DeployKey a deploy key", @@ -7466,7 +7466,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditAttachmentOptions": { "description": "EditAttachmentOptions options for editing attachments", @@ -7477,7 +7477,7 @@ "x-go-name": "Name" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditDeadlineOption": { "description": "EditDeadlineOption options for creating a deadline", @@ -7492,7 +7492,7 @@ "x-go-name": "Deadline" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditGitHookOption": { "description": "EditGitHookOption options when modifying one Git hook", @@ -7503,7 +7503,7 @@ "x-go-name": "Content" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditHookOption": { "description": "EditHookOption options when modify one hook", @@ -7528,7 +7528,7 @@ "x-go-name": "Events" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditIssueCommentOption": { "description": "EditIssueCommentOption options for editing a comment", @@ -7542,7 +7542,7 @@ "x-go-name": "Body" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditIssueOption": { "description": "EditIssueOption options for editing an issue", @@ -7582,7 +7582,7 @@ "x-go-name": "Title" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditLabelOption": { "description": "EditLabelOption options for editing a label", @@ -7597,7 +7597,7 @@ "x-go-name": "Name" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditMilestoneOption": { "description": "EditMilestoneOption options for editing a milestone", @@ -7621,7 +7621,7 @@ "x-go-name": "Title" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditOrgOption": { "description": "EditOrgOption options for editing an organization", @@ -7644,7 +7644,7 @@ "x-go-name": "Website" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditPullRequestOption": { "description": "EditPullRequestOption options when modify pull request", @@ -7692,7 +7692,7 @@ "x-go-name": "Title" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditReleaseOption": { "description": "EditReleaseOption options when editing a release", @@ -7723,7 +7723,7 @@ "x-go-name": "Target" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditTeamOption": { "description": "EditTeamOption options for editing a team", @@ -7766,7 +7766,7 @@ "x-go-name": "Units" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "EditUserOption": { "description": "EditUserOption edit user options", @@ -7839,7 +7839,7 @@ "x-go-name": "Website" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Email": { "description": "Email an email address belonging to a user", @@ -7859,7 +7859,7 @@ "x-go-name": "Verified" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "FileCommitResponse": { "type": "object", @@ -7898,7 +7898,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "FileContentResponse": { "description": "FileContentResponse contains information about a repo's file stats and content", @@ -7945,7 +7945,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "FileDeleteResponse": { "description": "FileDeleteResponse contains information about a repo's file that was deleted", @@ -7962,7 +7962,7 @@ "$ref": "#/definitions/PayloadCommitVerification" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "FileLinksResponse": { "description": "FileLinksResponse contains the links for a repo's file", @@ -7981,7 +7981,7 @@ "x-go-name": "Self" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "FileResponse": { "description": "FileResponse contains information about a repo's file", @@ -7997,7 +7997,7 @@ "$ref": "#/definitions/PayloadCommitVerification" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "GPGKey": { "description": "GPGKey a user GPG key to sign commit and tag in repository", @@ -8061,7 +8061,7 @@ "x-go-name": "SubsKey" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "GPGKeyEmail": { "description": "GPGKeyEmail an email attached to a GPGKey", @@ -8076,7 +8076,7 @@ "x-go-name": "Verified" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "GitBlobResponse": { "description": "GitBlobResponse represents a git blob", @@ -8104,7 +8104,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "GitEntry": { "description": "GitEntry represents a git tree", @@ -8136,7 +8136,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "GitHook": { "description": "GitHook represents a Git repository hook", @@ -8155,7 +8155,7 @@ "x-go-name": "Name" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "GitObject": { "type": "object", @@ -8174,7 +8174,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "GitTreeResponse": { "description": "GitTreeResponse returns a git tree", @@ -8210,7 +8210,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Hook": { "description": "Hook a hook is a web hook when one repository changed", @@ -8254,7 +8254,7 @@ "x-go-name": "Updated" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Identity": { "description": "Identity for a person's identity like an author or committer", @@ -8270,7 +8270,7 @@ "x-go-name": "Name" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Issue": { "description": "Issue represents an issue in a repository", @@ -8353,7 +8353,7 @@ "$ref": "#/definitions/User" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "IssueDeadline": { "description": "IssueDeadline represents an issue deadline", @@ -8365,7 +8365,7 @@ "x-go-name": "Deadline" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "IssueLabelsOption": { "description": "IssueLabelsOption a collection of labels", @@ -8381,7 +8381,7 @@ "x-go-name": "Labels" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Label": { "description": "Label a label to an issue or a pr", @@ -8406,7 +8406,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "MarkdownOption": { "description": "MarkdownOption markdown options", @@ -8429,7 +8429,7 @@ "type": "boolean" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "MergePullRequestOption": { "description": "MergePullRequestForm form for merging Pull Request", @@ -8567,7 +8567,7 @@ "x-go-name": "Title" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Organization": { "description": "Organization represents an organization", @@ -8606,7 +8606,7 @@ "x-go-name": "Website" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "PRBranchInfo": { "description": "PRBranchInfo information about a branch", @@ -8633,7 +8633,7 @@ "x-go-name": "Sha" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "PayloadCommit": { "description": "PayloadCommit represents a commit", @@ -8688,7 +8688,7 @@ "$ref": "#/definitions/PayloadCommitVerification" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "PayloadCommitVerification": { "description": "PayloadCommitVerification represents the GPG verification of a commit", @@ -8711,7 +8711,7 @@ "x-go-name": "Verified" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "PayloadUser": { "description": "PayloadUser represents the author or committer of a commit", @@ -8732,7 +8732,7 @@ "x-go-name": "UserName" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Permission": { "description": "Permission represents a set of permissions", @@ -8751,7 +8751,7 @@ "x-go-name": "Push" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "PublicKey": { "description": "PublicKey publickey is a user key to push code to repository", @@ -8795,7 +8795,7 @@ "$ref": "#/definitions/User" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "PullRequest": { "description": "PullRequest represents a pull request", @@ -8917,7 +8917,7 @@ "$ref": "#/definitions/User" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "PullRequestMeta": { "description": "PullRequestMeta PR info if an issue is a PR", @@ -8933,7 +8933,7 @@ "x-go-name": "Merged" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Reference": { "type": "object", @@ -8951,7 +8951,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Release": { "description": "Release represents a repository release", @@ -9019,7 +9019,7 @@ "x-go-name": "ZipURL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "RepoCommit": { "type": "object", @@ -9043,7 +9043,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Repository": { "description": "Repository represents a repository", @@ -9151,7 +9151,7 @@ "x-go-name": "Website" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "SearchResults": { "description": "SearchResults results of a successful search", @@ -9169,7 +9169,7 @@ "x-go-name": "OK" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "ServerVersion": { "description": "ServerVersion wraps the version of the server", @@ -9180,12 +9180,12 @@ "x-go-name": "Version" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "StateType": { "description": "StateType issue state type", "type": "string", - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Status": { "description": "Status holds a single Status of a single Commit", @@ -9229,12 +9229,12 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "StatusState": { "description": "StatusState holds the state of a Status\nIt can be \"pending\", \"success\", \"error\", \"failure\", and \"warning\"", "type": "string", - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Tag": { "description": "Tag represents a repository tag", @@ -9267,7 +9267,7 @@ "x-go-name": "ZipballURL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "Team": { "description": "Team represents a team in an organization", @@ -9317,7 +9317,7 @@ "x-go-name": "Units" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "TimeStamp": { "description": "TimeStamp defines a timestamp", @@ -9356,7 +9356,7 @@ "x-go-name": "UserID" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "UpdateFileOptions": { "description": "UpdateFileOptions options for updating files", @@ -9393,7 +9393,7 @@ "x-go-name": "SHA" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "User": { "description": "User represents a user", @@ -9436,7 +9436,7 @@ "x-go-name": "UserName" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "UserHeatmapData": { "description": "UserHeatmapData represents the data needed to create a heatmap", @@ -9457,7 +9457,7 @@ "description": "VisibleType defines the visibility (Organization only)", "type": "integer", "format": "int64", - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" }, "WatchInfo": { "description": "WatchInfo represents an API watch status of one repository", @@ -9489,7 +9489,7 @@ "x-go-name": "URL" } }, - "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + "x-go-package": "code.gitea.io/gitea/modules/structs" } }, "responses": { diff --git a/vendor/code.gitea.io/sdk/LICENSE b/vendor/code.gitea.io/sdk/LICENSE deleted file mode 100644 index 10aeba46bc..0000000000 --- a/vendor/code.gitea.io/sdk/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2016 The Gitea Authors -Copyright (c) 2014 The Gogs Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/code.gitea.io/sdk/gitea/admin_org.go b/vendor/code.gitea.io/sdk/gitea/admin_org.go deleted file mode 100644 index 4071b6f187..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/admin_org.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 The Gogs 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" -) - -// AdminCreateOrg create an organization -func (c *Client) AdminCreateOrg(user string, opt CreateOrgOption) (*Organization, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - org := new(Organization) - return org, c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/orgs", user), - jsonHeader, bytes.NewReader(body), org) -} diff --git a/vendor/code.gitea.io/sdk/gitea/admin_repo.go b/vendor/code.gitea.io/sdk/gitea/admin_repo.go deleted file mode 100644 index cf565ffa38..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/admin_repo.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 The Gogs 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" -) - -// AdminCreateRepo create a repo -func (c *Client) AdminCreateRepo(user string, opt CreateRepoOption) (*Repository, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - repo := new(Repository) - return repo, c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/repos", user), - jsonHeader, bytes.NewReader(body), repo) -} diff --git a/vendor/code.gitea.io/sdk/gitea/attachment.go b/vendor/code.gitea.io/sdk/gitea/attachment.go deleted file mode 100644 index 8c527de735..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/attachment.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2017 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 "code.gitea.io/sdk/gitea" -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "mime/multipart" - "net/http" - "time" -) - -// Attachment a generic attachment -// swagger:model -type Attachment struct { - ID int64 `json:"id"` - Name string `json:"name"` - Size int64 `json:"size"` - DownloadCount int64 `json:"download_count"` - // swagger:strfmt date-time - Created time.Time `json:"created_at"` - UUID string `json:"uuid"` - DownloadURL string `json:"browser_download_url"` -} - -// ListReleaseAttachments list release's attachments -func (c *Client) ListReleaseAttachments(user, repo string, release int64) ([]*Attachment, error) { - attachments := make([]*Attachment, 0, 10) - err := c.getParsedResponse("GET", - fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release), - nil, nil, &attachments) - return attachments, err -} - -// GetReleaseAttachment returns the requested attachment -func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, error) { - a := new(Attachment) - err := c.getParsedResponse("GET", - fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), - nil, nil, &a) - return a, err -} - -// CreateReleaseAttachment creates an attachment for the given release -func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, error) { - // Write file to body - body := new(bytes.Buffer) - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - if err != nil { - return nil, err - } - - if _, err = io.Copy(part, file); err != nil { - return nil, err - } - if err = writer.Close(); err != nil { - return nil, err - } - - // Send request - attachment := new(Attachment) - err = c.getParsedResponse("POST", - fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release), - http.Header{"Content-Type": {writer.FormDataContentType()}}, body, &attachment) - return attachment, err -} - -// EditReleaseAttachment updates the given attachment with the given options -func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, error) { - body, err := json.Marshal(&form) - if err != nil { - return nil, err - } - attach := new(Attachment) - return attach, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, attachment), jsonHeader, bytes.NewReader(body), attach) -} - -// DeleteReleaseAttachment deletes the given attachment including the uploaded file -func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil) - return err -} - -// EditAttachmentOptions options for editing attachments -// swagger:model -type EditAttachmentOptions struct { - Name string `json:"name"` -} diff --git a/vendor/code.gitea.io/sdk/gitea/fork.go b/vendor/code.gitea.io/sdk/gitea/fork.go deleted file mode 100644 index 57222498ef..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/fork.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2016 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" -) - -// ListForks list a repository's forks -func (c *Client) ListForks(user, repo string) ([]*Repository, error) { - forks := make([]*Repository, 10) - err := c.getParsedResponse("GET", - fmt.Sprintf("/repos/%s/%s/forks", user, repo), - nil, nil, &forks) - return forks, err -} - -// CreateForkOption options for creating a fork -type CreateForkOption struct { - // organization name, if forking into an organization - Organization *string `json:"organization"` -} - -// CreateFork create a fork of a repository -func (c *Client) CreateFork(user, repo string, form CreateForkOption) (*Repository, error) { - body, err := json.Marshal(form) - if err != nil { - return nil, err - } - fork := new(Repository) - err = c.getParsedResponse("POST", - fmt.Sprintf("/repos/%s/%s/forks", user, repo), - jsonHeader, bytes.NewReader(body), &fork) - return fork, err -} diff --git a/vendor/code.gitea.io/sdk/gitea/git_hook.go b/vendor/code.gitea.io/sdk/gitea/git_hook.go deleted file mode 100644 index d47fe1d0ca..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/git_hook.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2019 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" -) - -// GitHook represents a Git repository hook -type GitHook struct { - Name string `json:"name"` - IsActive bool `json:"is_active"` - Content string `json:"content,omitempty"` -} - -// GitHookList represents a list of Git hooks -type GitHookList []*GitHook - -// ListRepoGitHooks list all the Git hooks of one repository -func (c *Client) ListRepoGitHooks(user, repo string) (GitHookList, error) { - hooks := make([]*GitHook, 0, 10) - return hooks, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git", user, repo), nil, nil, &hooks) -} - -// GetRepoGitHook get a Git hook of a repository -func (c *Client) GetRepoGitHook(user, repo, id string) (*GitHook, error) { - h := new(GitHook) - return h, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil, h) -} - -// EditGitHookOption options when modifying one Git hook -type EditGitHookOption struct { - Content string `json:"content"` -} - -// EditRepoGitHook modify one Git hook of a repository -func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) error { - body, err := json.Marshal(&opt) - if err != nil { - return err - } - _, err = c.getResponse("PATCH", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), jsonHeader, bytes.NewReader(body)) - return err -} - -// DeleteRepoGitHook delete one Git hook from a repository -func (c *Client) DeleteRepoGitHook(user, repo, id string) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/gitea.go b/vendor/code.gitea.io/sdk/gitea/gitea.go deleted file mode 100644 index fa35e09555..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/gitea.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2014 The Gogs 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 ( - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "strings" -) - -// Version return the library version -func Version() string { - return "0.12.3" -} - -// Client represents a Gogs API client. -type Client struct { - url string - accessToken string - sudo string - client *http.Client -} - -// NewClient initializes and returns a API client. -func NewClient(url, token string) *Client { - return &Client{ - url: strings.TrimSuffix(url, "/"), - accessToken: token, - client: &http.Client{}, - } -} - -// NewClientWithHTTP creates an API client with a custom http client -func NewClientWithHTTP(url string, httpClient *http.Client) { - client := NewClient(url, "") - client.client = httpClient -} - -// SetHTTPClient replaces default http.Client with user given one. -func (c *Client) SetHTTPClient(client *http.Client) { - c.client = client -} - -// SetSudo sets username to impersonate. -func (c *Client) SetSudo(sudo string) { - c.sudo = sudo -} - -func (c *Client) doRequest(method, path string, header http.Header, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest(method, c.url+"/api/v1"+path, body) - if err != nil { - return nil, err - } - if len(c.accessToken) != 0 { - req.Header.Set("Authorization", "token "+c.accessToken) - } - if c.sudo != "" { - req.Header.Set("Sudo", c.sudo) - } - for k, v := range header { - req.Header[k] = v - } - - return c.client.Do(req) -} - -func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, error) { - resp, err := c.doRequest(method, path, header, body) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - switch resp.StatusCode { - case 403: - return nil, errors.New("403 Forbidden") - case 404: - return nil, errors.New("404 Not Found") - case 409: - return nil, errors.New("409 Conflict") - case 422: - return nil, fmt.Errorf("422 Unprocessable Entity: %s", string(data)) - } - - if resp.StatusCode/100 != 2 { - errMap := make(map[string]interface{}) - if err = json.Unmarshal(data, &errMap); err != nil { - // when the JSON can't be parsed, data was probably empty or a plain string, - // so we try to return a helpful error anyway - return nil, fmt.Errorf("Unknown API Error: %d %s", resp.StatusCode, string(data)) - } - return nil, errors.New(errMap["message"].(string)) - } - - return data, nil -} - -func (c *Client) getParsedResponse(method, path string, header http.Header, body io.Reader, obj interface{}) error { - data, err := c.getResponse(method, path, header, body) - if err != nil { - return err - } - return json.Unmarshal(data, obj) -} - -func (c *Client) getStatusCode(method, path string, header http.Header, body io.Reader) (int, error) { - resp, err := c.doRequest(method, path, header, body) - if err != nil { - return -1, err - } - defer resp.Body.Close() - - return resp.StatusCode, nil -} diff --git a/vendor/code.gitea.io/sdk/gitea/issue_comment.go b/vendor/code.gitea.io/sdk/gitea/issue_comment.go deleted file mode 100644 index 2c8127c609..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/issue_comment.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2016 The Gogs 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" - "time" -) - -// Comment represents a comment on a commit or issue -type Comment struct { - ID int64 `json:"id"` - HTMLURL string `json:"html_url"` - PRURL string `json:"pull_request_url"` - IssueURL string `json:"issue_url"` - Poster *User `json:"user"` - Body string `json:"body"` - // swagger:strfmt date-time - Created time.Time `json:"created_at"` - // swagger:strfmt date-time - Updated time.Time `json:"updated_at"` -} - -// ListIssueComments list comments on an issue. -func (c *Client) ListIssueComments(owner, repo string, index int64) ([]*Comment, error) { - comments := make([]*Comment, 0, 10) - return comments, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index), nil, nil, &comments) -} - -// ListRepoIssueComments list comments for a given repo. -func (c *Client) ListRepoIssueComments(owner, repo string) ([]*Comment, error) { - comments := make([]*Comment, 0, 10) - return comments, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo), nil, nil, &comments) -} - -// CreateIssueCommentOption options for creating a comment on an issue -type CreateIssueCommentOption struct { - // required:true - Body string `json:"body" binding:"Required"` -} - -// CreateIssueComment create comment on an issue. -func (c *Client) CreateIssueComment(owner, repo string, index int64, opt CreateIssueCommentOption) (*Comment, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - comment := new(Comment) - return comment, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index), jsonHeader, bytes.NewReader(body), comment) -} - -// EditIssueCommentOption options for editing a comment -type EditIssueCommentOption struct { - // required: true - Body string `json:"body" binding:"Required"` -} - -// EditIssueComment edits an issue comment. -func (c *Client) EditIssueComment(owner, repo string, index, commentID int64, opt EditIssueCommentOption) (*Comment, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - comment := new(Comment) - return comment, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/:%s/:%s/issues/%d/comments/%d", owner, repo, index, commentID), jsonHeader, bytes.NewReader(body), comment) -} - -// DeleteIssueComment deletes an issue comment. -func (c *Client) DeleteIssueComment(owner, repo string, index, commentID int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/comments/%d", owner, repo, index, commentID), nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/issue_label.go b/vendor/code.gitea.io/sdk/gitea/issue_label.go deleted file mode 100644 index 47d1b8221e..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/issue_label.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2016 The Gogs 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" -) - -// Label a label to an issue or a pr -// swagger:model -type Label struct { - ID int64 `json:"id"` - Name string `json:"name"` - // example: 00aabb - Color string `json:"color"` - URL string `json:"url"` -} - -// ListRepoLabels list labels of one repository -func (c *Client) ListRepoLabels(owner, repo string) ([]*Label, error) { - labels := make([]*Label, 0, 10) - return labels, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels", owner, repo), nil, nil, &labels) -} - -// GetRepoLabel get one label of repository by repo it -// TODO: maybe we need get a label by name -func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, error) { - label := new(Label) - return label, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil, label) -} - -// CreateLabelOption options for creating a label -type CreateLabelOption struct { - // required:true - Name string `json:"name" binding:"Required"` - // required:true - // example: #00aabb - Color string `json:"color" binding:"Required;Size(7)"` -} - -// CreateLabel create one label of repository -func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - label := new(Label) - return label, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/labels", owner, repo), - jsonHeader, bytes.NewReader(body), label) -} - -// EditLabelOption options for editing a label -type EditLabelOption struct { - Name *string `json:"name"` - Color *string `json:"color"` -} - -// EditLabel modify one label with options -func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*Label, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - label := new(Label) - return label, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), jsonHeader, bytes.NewReader(body), label) -} - -// DeleteLabel delete one label of repository by id -// TODO: maybe we need delete by name -func (c *Client) DeleteLabel(owner, repo string, id int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil) - return err -} - -// IssueLabelsOption a collection of labels -type IssueLabelsOption struct { - // list of label IDs - Labels []int64 `json:"labels"` -} - -// GetIssueLabels get labels of one issue via issue id -func (c *Client) GetIssueLabels(owner, repo string, index int64) ([]*Label, error) { - labels := make([]*Label, 0, 5) - return labels, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil, &labels) -} - -// AddIssueLabels add one or more labels to one issue -func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - var labels []*Label - return labels, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), jsonHeader, bytes.NewReader(body), &labels) -} - -// ReplaceIssueLabels replace old labels of issue with new labels -func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - var labels []*Label - return labels, c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), jsonHeader, bytes.NewReader(body), &labels) -} - -// DeleteIssueLabel delete one label of one issue by issue id and label id -// TODO: maybe we need delete by label name and issue id -func (c *Client) DeleteIssueLabel(owner, repo string, index, label int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, label), nil, nil) - return err -} - -// ClearIssueLabels delete all the labels of one issue. -func (c *Client) ClearIssueLabels(owner, repo string, index int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/issue_milestone.go b/vendor/code.gitea.io/sdk/gitea/issue_milestone.go deleted file mode 100644 index 775a6a9117..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/issue_milestone.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2016 The Gogs 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" - "time" -) - -// Milestone milestone is a collection of issues on one repository -type Milestone struct { - ID int64 `json:"id"` - Title string `json:"title"` - Description string `json:"description"` - State StateType `json:"state"` - OpenIssues int `json:"open_issues"` - ClosedIssues int `json:"closed_issues"` - // swagger:strfmt date-time - Closed *time.Time `json:"closed_at"` - // swagger:strfmt date-time - Deadline *time.Time `json:"due_on"` -} - -// ListRepoMilestones list all the milestones of one repository -func (c *Client) ListRepoMilestones(owner, repo string) ([]*Milestone, error) { - milestones := make([]*Milestone, 0, 10) - return milestones, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones", owner, repo), nil, nil, &milestones) -} - -// GetMilestone get one milestone by repo name and milestone id -func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, error) { - milestone := new(Milestone) - return milestone, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil, milestone) -} - -// CreateMilestoneOption options for creating a milestone -type CreateMilestoneOption struct { - Title string `json:"title"` - Description string `json:"description"` - // swagger:strfmt date-time - Deadline *time.Time `json:"due_on"` -} - -// CreateMilestone create one milestone with options -func (c *Client) CreateMilestone(owner, repo string, opt CreateMilestoneOption) (*Milestone, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - milestone := new(Milestone) - return milestone, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/milestones", owner, repo), jsonHeader, bytes.NewReader(body), milestone) -} - -// EditMilestoneOption options for editing a milestone -type EditMilestoneOption struct { - Title string `json:"title"` - Description *string `json:"description"` - State *string `json:"state"` - Deadline *time.Time `json:"due_on"` -} - -// EditMilestone modify milestone with options -func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOption) (*Milestone, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - milestone := new(Milestone) - return milestone, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), jsonHeader, bytes.NewReader(body), milestone) -} - -// DeleteMilestone delete one milestone by milestone id -func (c *Client) DeleteMilestone(owner, repo string, id int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go b/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go deleted file mode 100644 index 7f4b64cbb3..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 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" - "time" -) - -// TrackedTime worked time for an issue / pr -type TrackedTime struct { - ID int64 `json:"id"` - // swagger:strfmt date-time - Created time.Time `json:"created"` - // Time in seconds - Time int64 `json:"time"` - UserID int64 `json:"user_id"` - IssueID int64 `json:"issue_id"` -} - -// TrackedTimes represent a list of tracked times -type TrackedTimes []*TrackedTime - -// GetUserTrackedTimes list tracked times of a user -func (c *Client) GetUserTrackedTimes(owner, repo, user string) (TrackedTimes, error) { - times := make(TrackedTimes, 0, 10) - return times, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/times/%s", owner, repo, user), nil, nil, ×) -} - -// GetRepoTrackedTimes list tracked times of a repository -func (c *Client) GetRepoTrackedTimes(owner, repo string) (TrackedTimes, error) { - times := make(TrackedTimes, 0, 10) - return times, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/times", owner, repo), nil, nil, ×) -} - -// GetMyTrackedTimes list tracked times of the current user -func (c *Client) GetMyTrackedTimes() (TrackedTimes, error) { - times := make(TrackedTimes, 0, 10) - return times, c.getParsedResponse("GET", "/user/times", nil, nil, ×) -} - -// AddTimeOption options for adding time to an issue -type AddTimeOption struct { - // time in seconds - // required: true - Time int64 `json:"time" binding:"Required"` -} - -// AddTime adds time to issue with the given index -func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*TrackedTime, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - t := new(TrackedTime) - return t, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), - jsonHeader, bytes.NewReader(body), t) -} - -// ListTrackedTimes get tracked times of one issue via issue id -func (c *Client) ListTrackedTimes(owner, repo string, index int64) (TrackedTimes, error) { - times := make(TrackedTimes, 0, 5) - return times, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), nil, nil, ×) -} diff --git a/vendor/code.gitea.io/sdk/gitea/org_member.go b/vendor/code.gitea.io/sdk/gitea/org_member.go deleted file mode 100644 index 9bb95af3e2..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/org_member.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 The Gogs 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" -) - -// AddOrgMembershipOption add user to organization options -type AddOrgMembershipOption struct { - Role string `json:"role" binding:"Required"` -} - -// AddOrgMembership add some one to an organization's member -func (c *Client) AddOrgMembership(org, user string, opt AddOrgMembershipOption) error { - body, err := json.Marshal(&opt) - if err != nil { - return err - } - _, err = c.getResponse("PUT", fmt.Sprintf("/orgs/%s/membership/%s", org, user), jsonHeader, bytes.NewReader(body)) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/org_type.go b/vendor/code.gitea.io/sdk/gitea/org_type.go deleted file mode 100644 index 778ec3b0a7..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/org_type.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019 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 - -// VisibleType defines the visibility (Organization only) -type VisibleType int - -const ( - // VisibleTypePublic Visible for everyone - VisibleTypePublic VisibleType = iota - - // VisibleTypeLimited Visible for every connected user - VisibleTypeLimited - - // VisibleTypePrivate Visible only for organization's members - VisibleTypePrivate -) - -// VisibilityModes is a map of org Visibility types -var VisibilityModes = map[string]VisibleType{ - "public": VisibleTypePublic, - "limited": VisibleTypeLimited, - "private": VisibleTypePrivate, -} - -// IsPublic returns true if VisibleType is public -func (vt VisibleType) IsPublic() bool { - return vt == VisibleTypePublic -} - -// IsLimited returns true if VisibleType is limited -func (vt VisibleType) IsLimited() bool { - return vt == VisibleTypeLimited -} - -// IsPrivate returns true if VisibleType is private -func (vt VisibleType) IsPrivate() bool { - return vt == VisibleTypePrivate -} - -// ExtractKeysFromMapString provides a slice of keys from map -func ExtractKeysFromMapString(in map[string]VisibleType) (keys []string) { - for k := range in { - keys = append(keys, k) - } - return -} diff --git a/vendor/code.gitea.io/sdk/gitea/release.go b/vendor/code.gitea.io/sdk/gitea/release.go deleted file mode 100644 index 396251dcac..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/release.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2016 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" - "time" -) - -// Release represents a repository release -type Release struct { - ID int64 `json:"id"` - TagName string `json:"tag_name"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - URL string `json:"url"` - TarURL string `json:"tarball_url"` - ZipURL string `json:"zipball_url"` - IsDraft bool `json:"draft"` - IsPrerelease bool `json:"prerelease"` - // swagger:strfmt date-time - CreatedAt time.Time `json:"created_at"` - // swagger:strfmt date-time - PublishedAt time.Time `json:"published_at"` - Publisher *User `json:"author"` - Attachments []*Attachment `json:"assets"` -} - -// ListReleases list releases of a repository -func (c *Client) ListReleases(user, repo string) ([]*Release, error) { - releases := make([]*Release, 0, 10) - err := c.getParsedResponse("GET", - fmt.Sprintf("/repos/%s/%s/releases", user, repo), - nil, nil, &releases) - return releases, err -} - -// GetRelease get a release of a repository -func (c *Client) GetRelease(user, repo string, id int64) (*Release, error) { - r := new(Release) - err := c.getParsedResponse("GET", - fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), - nil, nil, &r) - return r, err -} - -// CreateReleaseOption options when creating a release -type CreateReleaseOption struct { - // required: true - TagName string `json:"tag_name" binding:"Required"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - IsDraft bool `json:"draft"` - IsPrerelease bool `json:"prerelease"` -} - -// CreateRelease create a release -func (c *Client) CreateRelease(user, repo string, form CreateReleaseOption) (*Release, error) { - body, err := json.Marshal(form) - if err != nil { - return nil, err - } - r := new(Release) - err = c.getParsedResponse("POST", - fmt.Sprintf("/repos/%s/%s/releases", user, repo), - jsonHeader, bytes.NewReader(body), r) - return r, err -} - -// EditReleaseOption options when editing a release -type EditReleaseOption struct { - TagName string `json:"tag_name"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - IsDraft *bool `json:"draft"` - IsPrerelease *bool `json:"prerelease"` -} - -// EditRelease edit a release -func (c *Client) EditRelease(user, repo string, id int64, form EditReleaseOption) (*Release, error) { - body, err := json.Marshal(form) - if err != nil { - return nil, err - } - r := new(Release) - err = c.getParsedResponse("PATCH", - fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), - jsonHeader, bytes.NewReader(body), r) - return r, err -} - -// DeleteRelease delete a release from a repository -func (c *Client) DeleteRelease(user, repo string, id int64) error { - _, err := c.getResponse("DELETE", - fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), - nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_branch.go b/vendor/code.gitea.io/sdk/gitea/repo_branch.go deleted file mode 100644 index 481fc3386e..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/repo_branch.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2016 The Gogs 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 ( - "fmt" -) - -// Branch represents a repository branch -type Branch struct { - Name string `json:"name"` - Commit *PayloadCommit `json:"commit"` -} - -// ListRepoBranches list all the branches of one repository -func (c *Client) ListRepoBranches(user, repo string) ([]*Branch, error) { - branches := make([]*Branch, 0, 10) - return branches, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches", user, repo), nil, nil, &branches) -} - -// GetRepoBranch get one branch's information of one repository -func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, error) { - b := new(Branch) - return b, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b) -} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_collaborator.go b/vendor/code.gitea.io/sdk/gitea/repo_collaborator.go deleted file mode 100644 index bd61a22cec..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/repo_collaborator.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2016 The Gogs 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" -) - -// ListCollaborators list a repository's collaborators -func (c *Client) ListCollaborators(user, repo string) ([]*User, error) { - collaborators := make([]*User, 0, 10) - err := c.getParsedResponse("GET", - fmt.Sprintf("/repos/%s/%s/collaborators", user, repo), - nil, nil, &collaborators) - return collaborators, err -} - -// IsCollaborator check if a user is a collaborator of a repository -func (c *Client) IsCollaborator(user, repo, collaborator string) (bool, error) { - status, err := c.getStatusCode("GET", - fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), - nil, nil) - if err != nil { - return false, err - } - if status == 204 { - return true, nil - } - return false, nil -} - -// AddCollaboratorOption options when adding a user as a collaborator of a repository -type AddCollaboratorOption struct { - Permission *string `json:"permission"` -} - -// AddCollaborator add some user as a collaborator of a repository -func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollaboratorOption) error { - body, err := json.Marshal(&opt) - if err != nil { - return err - } - _, err = c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, bytes.NewReader(body)) - return err -} - -// DeleteCollaborator remove a collaborator from a repository -func (c *Client) DeleteCollaborator(user, repo, collaborator string) error { - _, err := c.getResponse("DELETE", - fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), - nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_key.go b/vendor/code.gitea.io/sdk/gitea/repo_key.go deleted file mode 100644 index a1ae4584e0..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/repo_key.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2015 The Gogs 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" - "time" -) - -// DeployKey a deploy key -type DeployKey struct { - ID int64 `json:"id"` - KeyID int64 `json:"key_id"` - Key string `json:"key"` - URL string `json:"url"` - Title string `json:"title"` - Fingerprint string `json:"fingerprint"` - // swagger:strfmt date-time - Created time.Time `json:"created_at"` - ReadOnly bool `json:"read_only"` - Repository *Repository `json:"repository,omitempty"` -} - -// ListDeployKeys list all the deploy keys of one repository -func (c *Client) ListDeployKeys(user, repo string) ([]*DeployKey, error) { - keys := make([]*DeployKey, 0, 10) - return keys, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys", user, repo), nil, nil, &keys) -} - -// GetDeployKey get one deploy key with key id -func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, error) { - key := new(DeployKey) - return key, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys/%d", user, repo, keyID), nil, nil, &key) -} - -// CreateKeyOption options when creating a key -type CreateKeyOption struct { - // Title of the key to add - // - // required: true - // unique: true - Title string `json:"title" binding:"Required"` - // An armored SSH key to add - // - // required: true - // unique: true - Key string `json:"key" binding:"Required"` - // Describe if the key has only read access or read/write - // - // required: false - ReadOnly bool `json:"read_only"` -} - -// CreateDeployKey options when create one deploy key -func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*DeployKey, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - key := new(DeployKey) - return key, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/keys", user, repo), jsonHeader, bytes.NewReader(body), key) -} - -// DeleteDeployKey delete deploy key with key id -func (c *Client) DeleteDeployKey(owner, repo string, keyID int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/keys/%d", owner, repo, keyID), nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_refs.go b/vendor/code.gitea.io/sdk/gitea/repo_refs.go deleted file mode 100644 index b946a10026..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/repo_refs.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 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 ( - "encoding/json" - "errors" - "fmt" - "strings" -) - -// Reference represents a Git reference. -type Reference struct { - Ref string `json:"ref"` - URL string `json:"url"` - Object *GitObject `json:"object"` -} - -// GitObject represents a Git object. -type GitObject struct { - Type string `json:"type"` - SHA string `json:"sha"` - URL string `json:"url"` -} - -// GetRepoRef get one ref's information of one repository -func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, error) { - ref = strings.TrimPrefix(ref, "refs/") - r := new(Reference) - err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil, &r) - if _, ok := err.(*json.UnmarshalTypeError); ok { - // Multiple refs - return nil, errors.New("no exact match found for this ref") - } else if err != nil { - return nil, err - } - - return r, nil -} - -// GetRepoRefs get list of ref's information of one repository -func (c *Client) GetRepoRefs(user, repo, ref string) ([]*Reference, error) { - ref = strings.TrimPrefix(ref, "refs/") - resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil) - if err != nil { - return nil, err - } - - // Attempt to unmarshal single returned ref. - r := new(Reference) - refErr := json.Unmarshal(resp, r) - if refErr == nil { - return []*Reference{r}, nil - } - - // Attempt to unmarshal multiple refs. - var rs []*Reference - refsErr := json.Unmarshal(resp, &rs) - if refsErr == nil { - if len(rs) == 0 { - return nil, errors.New("unexpected response: an array of refs with length 0") - } - return rs, nil - } - - return nil, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", refErr, refsErr) -} diff --git a/vendor/code.gitea.io/sdk/gitea/repo_watch.go b/vendor/code.gitea.io/sdk/gitea/repo_watch.go deleted file mode 100644 index 1005f9fbd8..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/repo_watch.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2017 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 ( - "fmt" - "net/http" - "time" -) - -// WatchInfo represents an API watch status of one repository -type WatchInfo struct { - Subscribed bool `json:"subscribed"` - Ignored bool `json:"ignored"` - Reason interface{} `json:"reason"` - CreatedAt time.Time `json:"created_at"` - URL string `json:"url"` - RepositoryURL string `json:"repository_url"` -} - -// GetWatchedRepos list all the watched repos of user -func (c *Client) GetWatchedRepos(user, pass string) ([]*Repository, error) { - repos := make([]*Repository, 0, 10) - return repos, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), - http.Header{"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, nil, &repos) -} - -// WatchRepo start to watch a repository -func (c *Client) WatchRepo(user, pass, repoUser, repoName string) (*WatchInfo, error) { - i := new(WatchInfo) - return i, c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), - http.Header{"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, nil, i) -} - -// UnWatchRepo start to watch a repository -func (c *Client) UnWatchRepo(user, pass, repoUser, repoName string) (int, error) { - return c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), - http.Header{"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, nil) -} diff --git a/vendor/code.gitea.io/sdk/gitea/user_app.go b/vendor/code.gitea.io/sdk/gitea/user_app.go deleted file mode 100644 index c5367be5cd..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/user_app.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Copyright 2019 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/base64" - "encoding/json" - "fmt" - "net/http" -) - -// BasicAuthEncode generate base64 of basic auth head -func BasicAuthEncode(user, pass string) string { - return base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)) -} - -// AccessToken represents an API access token. -// swagger:response AccessToken -type AccessToken struct { - ID int64 `json:"id"` - Name string `json:"name"` - Token string `json:"sha1"` - TokenLastEight string `json:"token_last_eight"` -} - -// AccessTokenList represents a list of API access token. -// swagger:response AccessTokenList -type AccessTokenList []*AccessToken - -// ListAccessTokens lista all the access tokens of user -func (c *Client) ListAccessTokens(user, pass string) ([]*AccessToken, error) { - tokens := make([]*AccessToken, 0, 10) - return tokens, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens", user), - http.Header{"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, nil, &tokens) -} - -// CreateAccessTokenOption options when create access token -// swagger:parameters userCreateToken -type CreateAccessTokenOption struct { - Name string `json:"name" binding:"Required"` -} - -// CreateAccessToken create one access token with options -func (c *Client) CreateAccessToken(user, pass string, opt CreateAccessTokenOption) (*AccessToken, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - t := new(AccessToken) - return t, c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", user), - http.Header{ - "content-type": []string{"application/json"}, - "Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, - bytes.NewReader(body), t) -} - -// DeleteAccessToken delete token with key id -func (c *Client) DeleteAccessToken(user string, keyID int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/user/%s/tokens/%d", user, keyID), nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/user_email.go b/vendor/code.gitea.io/sdk/gitea/user_email.go deleted file mode 100644 index 721f52144b..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/user_email.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Gogs 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" -) - -// Email an email address belonging to a user -type Email struct { - // swagger:strfmt email - Email string `json:"email"` - Verified bool `json:"verified"` - Primary bool `json:"primary"` -} - -// ListEmails all the email addresses of user -func (c *Client) ListEmails() ([]*Email, error) { - emails := make([]*Email, 0, 3) - return emails, c.getParsedResponse("GET", "/user/emails", nil, nil, &emails) -} - -// CreateEmailOption options when creating email addresses -type CreateEmailOption struct { - // email addresses to add - Emails []string `json:"emails"` -} - -// AddEmail add one email to current user with options -func (c *Client) AddEmail(opt CreateEmailOption) ([]*Email, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - emails := make([]*Email, 0, 3) - return emails, c.getParsedResponse("POST", "/user/emails", jsonHeader, bytes.NewReader(body), emails) -} - -// DeleteEmailOption options when deleting email addresses -type DeleteEmailOption struct { - // email addresses to delete - Emails []string `json:"emails"` -} - -// DeleteEmail delete one email of current users' -func (c *Client) DeleteEmail(opt DeleteEmailOption) error { - body, err := json.Marshal(&opt) - if err != nil { - return err - } - _, err = c.getResponse("DELETE", "/user/emails", jsonHeader, bytes.NewReader(body)) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/user_follow.go b/vendor/code.gitea.io/sdk/gitea/user_follow.go deleted file mode 100644 index a197a7f188..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/user_follow.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015 The Gogs 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 "fmt" - -// ListMyFollowers list all the followers of current user -func (c *Client) ListMyFollowers(page int) ([]*User, error) { - users := make([]*User, 0, 10) - return users, c.getParsedResponse("GET", fmt.Sprintf("/user/followers?page=%d", page), nil, nil, &users) -} - -// ListFollowers list all the followers of one user -func (c *Client) ListFollowers(user string, page int) ([]*User, error) { - users := make([]*User, 0, 10) - return users, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?page=%d", user, page), nil, nil, &users) -} - -// ListMyFollowing list all the users current user followed -func (c *Client) ListMyFollowing(page int) ([]*User, error) { - users := make([]*User, 0, 10) - return users, c.getParsedResponse("GET", fmt.Sprintf("/user/following?page=%d", page), nil, nil, &users) -} - -// ListFollowing list all the users the user followed -func (c *Client) ListFollowing(user string, page int) ([]*User, error) { - users := make([]*User, 0, 10) - return users, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?page=%d", user, page), nil, nil, &users) -} - -// IsFollowing if current user followed the target -func (c *Client) IsFollowing(target string) bool { - _, err := c.getResponse("GET", fmt.Sprintf("/user/following/%s", target), nil, nil) - return err == nil -} - -// IsUserFollowing if the user followed the target -func (c *Client) IsUserFollowing(user, target string) bool { - _, err := c.getResponse("GET", fmt.Sprintf("/users/%s/following/%s", user, target), nil, nil) - return err == nil -} - -// Follow set current user follow the target -func (c *Client) Follow(target string) error { - _, err := c.getResponse("PUT", fmt.Sprintf("/user/following/%s", target), nil, nil) - return err -} - -// Unfollow set current user unfollow the target -func (c *Client) Unfollow(target string) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/user/following/%s", target), nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/user_key.go b/vendor/code.gitea.io/sdk/gitea/user_key.go deleted file mode 100644 index cccaa65db9..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/user_key.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015 The Gogs 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" - "time" -) - -// PublicKey publickey is a user key to push code to repository -type PublicKey struct { - ID int64 `json:"id"` - Key string `json:"key"` - URL string `json:"url,omitempty"` - Title string `json:"title,omitempty"` - Fingerprint string `json:"fingerprint,omitempty"` - // swagger:strfmt date-time - Created time.Time `json:"created_at,omitempty"` - Owner *User `json:"user,omitempty"` - ReadOnly bool `json:"read_only,omitempty"` - KeyType string `json:"key_type,omitempty"` -} - -// ListPublicKeys list all the public keys of the user -func (c *Client) ListPublicKeys(user string) ([]*PublicKey, error) { - keys := make([]*PublicKey, 0, 10) - return keys, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys", user), nil, nil, &keys) -} - -// ListMyPublicKeys list all the public keys of current user -func (c *Client) ListMyPublicKeys() ([]*PublicKey, error) { - keys := make([]*PublicKey, 0, 10) - return keys, c.getParsedResponse("GET", "/user/keys", nil, nil, &keys) -} - -// GetPublicKey get current user's public key by key id -func (c *Client) GetPublicKey(keyID int64) (*PublicKey, error) { - key := new(PublicKey) - return key, c.getParsedResponse("GET", fmt.Sprintf("/user/keys/%d", keyID), nil, nil, &key) -} - -// CreatePublicKey create public key with options -func (c *Client) CreatePublicKey(opt CreateKeyOption) (*PublicKey, error) { - body, err := json.Marshal(&opt) - if err != nil { - return nil, err - } - key := new(PublicKey) - return key, c.getParsedResponse("POST", "/user/keys", jsonHeader, bytes.NewReader(body), key) -} - -// DeletePublicKey delete public key with key id -func (c *Client) DeletePublicKey(keyID int64) error { - _, err := c.getResponse("DELETE", fmt.Sprintf("/user/keys/%d", keyID), nil, nil) - return err -} diff --git a/vendor/code.gitea.io/sdk/gitea/user_search.go b/vendor/code.gitea.io/sdk/gitea/user_search.go deleted file mode 100644 index 65ab980d66..0000000000 --- a/vendor/code.gitea.io/sdk/gitea/user_search.go +++ /dev/null @@ -1,14 +0,0 @@ -package gitea - -import "fmt" - -type searchUsersResponse struct { - Users []*User `json:"data"` -} - -// SearchUsers finds users by query -func (c *Client) SearchUsers(query string, limit int) ([]*User, error) { - resp := new(searchUsersResponse) - err := c.getParsedResponse("GET", fmt.Sprintf("/users/search?q=%s&limit=%d", query, limit), nil, nil, &resp) - return resp.Users, err -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 715f20a130..4d427e5a74 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,3 @@ -# code.gitea.io/sdk v0.0.0-20190510041517-9251befd3702 -code.gitea.io/sdk/gitea # github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml # github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 From ce8de3533485eed0c56059d6334a5031a73eed67 Mon Sep 17 00:00:00 2001 From: zeripath Date: Sat, 11 May 2019 16:29:17 +0100 Subject: [PATCH 09/12] Remove local clones & make hooks run on merge/edit/upload (#6672) * Add options to git.Clone to make it more capable * Begin the process of removing the local copy and tidy up * Remove Wiki LocalCopy Checkouts * Remove the last LocalRepo helpers * Remove WithTemporaryFile * Enable push-hooks for these routes * Ensure tests cope with hooks Signed-off-by: Andrew Thornton * Remove Repository.LocalCopyPath() * Move temporary repo to use the standard temporary path * Fix the tests Signed-off-by: Andrew Thornton * Remove LocalWikiPath * Fix missing remove Signed-off-by: Andrew Thornton * Use AppURL for Oauth user link (#6894) * Use AppURL for Oauth user link Fix #6843 * Update oauth.go * Update oauth.go * internal/ssh: ignore env command totally (#6825) * ssh: ignore env command totally * Remove commented code Needed fix described in issue #6889 * Escape the commit message on issues update and title in telegram hook (#6901) * update sdk to latest (#6903) * improve description of branch protection (fix #6886) (#6906) The branch protection description text were not quite accurate. * Fix logging documentation (#6904) * ENABLE_MACARON_REDIRECT should be REDIRECT_MACARON_LOG * Allow DISABLE_ROUTER_LOG to be set in the [log] section * [skip ci] Updated translations via Crowdin * Move sdk structs to modules/structs (#6905) * move sdk structs to moduels/structs * fix tests * fix fmt * fix swagger * fix vendor --- contrib/pr/checkout.go | 1 - integrations/api_repo_file_content_test.go | 6 +- integrations/api_repo_file_create_test.go | 210 +++++----- integrations/api_repo_file_delete_test.go | 208 +++++----- integrations/api_repo_file_update_test.go | 270 ++++++------- integrations/editor_test.go | 142 +++---- integrations/integration_test.go | 1 - integrations/pull_create_test.go | 101 ++--- integrations/pull_merge_test.go | 151 ++++---- integrations/pull_status_test.go | 130 ++++--- integrations/repo_activity_test.go | 73 ++-- integrations/repo_branch_test.go | 5 + .../repofiles_delete_test.go | 44 ++- integrations/repofiles_update_test.go | 365 ++++++++++++++++++ models/helper_directory.go | 45 +++ models/helper_environment.go | 36 ++ models/pull.go | 67 ++-- models/repo.go | 63 +-- models/repo_branch.go | 181 +++------ models/repo_test.go | 21 - models/user.go | 11 - models/wiki.go | 231 +++++++---- models/wiki_test.go | 49 ++- modules/git/command.go | 48 ++- modules/git/repo.go | 24 +- modules/git/repo_branch.go | 6 +- modules/git/repo_index.go | 98 +++++ modules/git/repo_object.go | 27 ++ modules/git/repo_tree.go | 50 +++ modules/repofiles/temp_repo.go | 39 +- modules/repofiles/update_test.go | 357 ----------------- modules/setting/repository.go | 3 - routers/repo/branch.go | 6 - 33 files changed, 1652 insertions(+), 1417 deletions(-) rename modules/repofiles/delete_test.go => integrations/repofiles_delete_test.go (76%) create mode 100644 integrations/repofiles_update_test.go create mode 100644 models/helper_directory.go create mode 100644 models/helper_environment.go create mode 100644 modules/git/repo_index.go delete mode 100644 modules/repofiles/update_test.go diff --git a/contrib/pr/checkout.go b/contrib/pr/checkout.go index bc393da135..607a503189 100644 --- a/contrib/pr/checkout.go +++ b/contrib/pr/checkout.go @@ -108,7 +108,6 @@ func runPR() { models.LoadFixtures() os.RemoveAll(setting.RepoRootPath) os.RemoveAll(models.LocalCopyPath()) - os.RemoveAll(models.LocalWikiPath()) com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath) log.Printf("[PR] Setting up router\n") diff --git a/integrations/api_repo_file_content_test.go b/integrations/api_repo_file_content_test.go index 1f535ef3a0..896d811083 100644 --- a/integrations/api_repo_file_content_test.go +++ b/integrations/api_repo_file_content_test.go @@ -6,6 +6,7 @@ package integrations import ( "net/http" + "net/url" "path/filepath" "testing" @@ -40,7 +41,10 @@ func getExpectedFileContentResponseForFileContents(branch string) *api.FileConte } func TestAPIGetFileContents(t *testing.T) { - prepareTestEnv(t) + onGiteaRun(t, testAPIGetFileContents) +} + +func testAPIGetFileContents(t *testing.T, u *url.URL) { user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos diff --git a/integrations/api_repo_file_create_test.go b/integrations/api_repo_file_create_test.go index a43855b06c..28097179a0 100644 --- a/integrations/api_repo_file_create_test.go +++ b/integrations/api_repo_file_create_test.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "fmt" "net/http" + "net/url" "path/filepath" "testing" @@ -91,125 +92,126 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon } func TestAPICreateFile(t *testing.T) { - prepareTestEnv(t) - user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 - user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org - user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos - repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo - repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo - repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo - fileID := 0 + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 + user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org + user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos + repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo + repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo + repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo + fileID := 0 - // Get user2's token - session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) - session = emptyTestSession(t) - // Get user4's token - session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) - session = emptyTestSession(t) + // Get user2's token + session := loginUser(t, user2.Name) + token2 := getTokenForLoggedInUser(t, session) + session = emptyTestSession(t) + // Get user4's token + session = loginUser(t, user4.Name) + token4 := getTokenForLoggedInUser(t, session) + session = emptyTestSession(t) - // Test creating a file in repo1 which user2 owns, try both with branch and empty branch - for _, branch := range [...]string{ - "master", // Branch - "", // Empty branch - } { + // Test creating a file in repo1 which user2 owns, try both with branch and empty branch + for _, branch := range [...]string{ + "master", // Branch + "", // Empty branch + } { + createFileOptions := getCreateFileOptions() + createFileOptions.BranchName = branch + fileID++ + treePath := fmt.Sprintf("new/file%d.txt", fileID) + url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) + req := NewRequestWithJSON(t, "POST", url, &createFileOptions) + resp := session.MakeRequest(t, req, http.StatusCreated) + gitRepo, _ := git.OpenRepository(repo1.RepoPath()) + commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) + expectedFileResponse := getExpectedFileResponseForCreate(commitID, treePath) + var fileResponse api.FileResponse + DecodeJSON(t, resp, &fileResponse) + assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) + assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) + assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) + } + + // Test creating a file in a new branch createFileOptions := getCreateFileOptions() - createFileOptions.BranchName = branch + createFileOptions.BranchName = repo1.DefaultBranch + createFileOptions.NewBranchName = "new_branch" fileID++ treePath := fmt.Sprintf("new/file%d.txt", fileID) url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) req := NewRequestWithJSON(t, "POST", url, &createFileOptions) resp := session.MakeRequest(t, req, http.StatusCreated) - gitRepo, _ := git.OpenRepository(repo1.RepoPath()) - commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) - expectedFileResponse := getExpectedFileResponseForCreate(commitID, treePath) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) - assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) - } + expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf" + expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/new/file%d.txt", fileID) + expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID) + assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) + assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) + assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) - // Test creating a file in a new branch - createFileOptions := getCreateFileOptions() - createFileOptions.BranchName = repo1.DefaultBranch - createFileOptions.NewBranchName = "new_branch" - fileID++ - treePath := fmt.Sprintf("new/file%d.txt", fileID) - url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) - req := NewRequestWithJSON(t, "POST", url, &createFileOptions) - resp := session.MakeRequest(t, req, http.StatusCreated) - var fileResponse api.FileResponse - DecodeJSON(t, resp, &fileResponse) - expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf" - expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/new/file%d.txt", fileID) - expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID) - assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) - assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) - assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) + // Test trying to create a file that already exists, should fail + createFileOptions = getCreateFileOptions() + treePath = "README.md" + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) + req = NewRequestWithJSON(t, "POST", url, &createFileOptions) + resp = session.MakeRequest(t, req, http.StatusInternalServerError) + expectedAPIError := context.APIError{ + Message: "repository file already exists [path: " + treePath + "]", + URL: base.DocURL, + } + var apiError context.APIError + DecodeJSON(t, resp, &apiError) + assert.Equal(t, expectedAPIError, apiError) - // Test trying to create a file that already exists, should fail - createFileOptions = getCreateFileOptions() - treePath = "README.md" - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) - req = NewRequestWithJSON(t, "POST", url, &createFileOptions) - resp = session.MakeRequest(t, req, http.StatusInternalServerError) - expectedAPIError := context.APIError{ - Message: "repository file already exists [path: " + treePath + "]", - URL: base.DocURL, - } - var apiError context.APIError - DecodeJSON(t, resp, &apiError) - assert.Equal(t, expectedAPIError, apiError) + // Test creating a file in repo1 by user4 who does not have write access + createFileOptions = getCreateFileOptions() + fileID++ + treePath = fmt.Sprintf("new/file%d.txt", fileID) + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) + req = NewRequestWithJSON(t, "POST", url, &createFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Test creating a file in repo1 by user4 who does not have write access - createFileOptions = getCreateFileOptions() - fileID++ - treePath = fmt.Sprintf("new/file%d.txt", fileID) - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) - req = NewRequestWithJSON(t, "POST", url, &createFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) + // Tests a repo with no token given so will fail + createFileOptions = getCreateFileOptions() + fileID++ + treePath = fmt.Sprintf("new/file%d.txt", fileID) + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) + req = NewRequestWithJSON(t, "POST", url, &createFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Tests a repo with no token given so will fail - createFileOptions = getCreateFileOptions() - fileID++ - treePath = fmt.Sprintf("new/file%d.txt", fileID) - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) - req = NewRequestWithJSON(t, "POST", url, &createFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) + // Test using access token for a private repo that the user of the token owns + createFileOptions = getCreateFileOptions() + fileID++ + treePath = fmt.Sprintf("new/file%d.txt", fileID) + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) + req = NewRequestWithJSON(t, "POST", url, &createFileOptions) + session.MakeRequest(t, req, http.StatusCreated) - // Test using access token for a private repo that the user of the token owns - createFileOptions = getCreateFileOptions() - fileID++ - treePath = fmt.Sprintf("new/file%d.txt", fileID) - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) - req = NewRequestWithJSON(t, "POST", url, &createFileOptions) - session.MakeRequest(t, req, http.StatusCreated) + // Test using org repo "user3/repo3" where user2 is a collaborator + createFileOptions = getCreateFileOptions() + fileID++ + treePath = fmt.Sprintf("new/file%d.txt", fileID) + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) + req = NewRequestWithJSON(t, "POST", url, &createFileOptions) + session.MakeRequest(t, req, http.StatusCreated) - // Test using org repo "user3/repo3" where user2 is a collaborator - createFileOptions = getCreateFileOptions() - fileID++ - treePath = fmt.Sprintf("new/file%d.txt", fileID) - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) - req = NewRequestWithJSON(t, "POST", url, &createFileOptions) - session.MakeRequest(t, req, http.StatusCreated) + // Test using org repo "user3/repo3" with no user token + createFileOptions = getCreateFileOptions() + fileID++ + treePath = fmt.Sprintf("new/file%d.txt", fileID) + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) + req = NewRequestWithJSON(t, "POST", url, &createFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Test using org repo "user3/repo3" with no user token - createFileOptions = getCreateFileOptions() - fileID++ - treePath = fmt.Sprintf("new/file%d.txt", fileID) - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) - req = NewRequestWithJSON(t, "POST", url, &createFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) - - // Test using repo "user2/repo1" where user4 is a NOT collaborator - createFileOptions = getCreateFileOptions() - fileID++ - treePath = fmt.Sprintf("new/file%d.txt", fileID) - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) - req = NewRequestWithJSON(t, "POST", url, &createFileOptions) - session.MakeRequest(t, req, http.StatusForbidden) + // Test using repo "user2/repo1" where user4 is a NOT collaborator + createFileOptions = getCreateFileOptions() + fileID++ + treePath = fmt.Sprintf("new/file%d.txt", fileID) + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) + req = NewRequestWithJSON(t, "POST", url, &createFileOptions) + session.MakeRequest(t, req, http.StatusForbidden) + }) } diff --git a/integrations/api_repo_file_delete_test.go b/integrations/api_repo_file_delete_test.go index b619f9c43c..57e2539e19 100644 --- a/integrations/api_repo_file_delete_test.go +++ b/integrations/api_repo_file_delete_test.go @@ -7,6 +7,7 @@ package integrations import ( "fmt" "net/http" + "net/url" "testing" "code.gitea.io/gitea/models" @@ -37,34 +38,50 @@ func getDeleteFileOptions() *api.DeleteFileOptions { } func TestAPIDeleteFile(t *testing.T) { - prepareTestEnv(t) - user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 - user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org - user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos - repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo - repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo - repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo - fileID := 0 + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 + user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org + user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos + repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo + repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo + repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo + fileID := 0 - // Get user2's token - session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) - session = emptyTestSession(t) - // Get user4's token - session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) - session = emptyTestSession(t) + // Get user2's token + session := loginUser(t, user2.Name) + token2 := getTokenForLoggedInUser(t, session) + session = emptyTestSession(t) + // Get user4's token + session = loginUser(t, user4.Name) + token4 := getTokenForLoggedInUser(t, session) + session = emptyTestSession(t) - // Test deleting a file in repo1 which user2 owns, try both with branch and empty branch - for _, branch := range [...]string{ - "master", // Branch - "", // Empty branch - } { + // Test deleting a file in repo1 which user2 owns, try both with branch and empty branch + for _, branch := range [...]string{ + "master", // Branch + "", // Empty branch + } { + fileID++ + treePath := fmt.Sprintf("delete/file%d.txt", fileID) + createFile(user2, repo1, treePath) + deleteFileOptions := getDeleteFileOptions() + deleteFileOptions.BranchName = branch + url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) + req := NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) + resp := session.MakeRequest(t, req, http.StatusOK) + var fileResponse api.FileResponse + DecodeJSON(t, resp, &fileResponse) + assert.NotNil(t, fileResponse) + assert.Nil(t, fileResponse.Content) + } + + // Test deleting file and making the delete in a new branch fileID++ treePath := fmt.Sprintf("delete/file%d.txt", fileID) createFile(user2, repo1, treePath) deleteFileOptions := getDeleteFileOptions() - deleteFileOptions.BranchName = branch + deleteFileOptions.BranchName = repo1.DefaultBranch + deleteFileOptions.NewBranchName = "new_branch" url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) req := NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) resp := session.MakeRequest(t, req, http.StatusOK) @@ -72,92 +89,77 @@ func TestAPIDeleteFile(t *testing.T) { DecodeJSON(t, resp, &fileResponse) assert.NotNil(t, fileResponse) assert.Nil(t, fileResponse.Content) - } - // Test deleting file and making the delete in a new branch - fileID++ - treePath := fmt.Sprintf("delete/file%d.txt", fileID) - createFile(user2, repo1, treePath) - deleteFileOptions := getDeleteFileOptions() - deleteFileOptions.BranchName = repo1.DefaultBranch - deleteFileOptions.NewBranchName = "new_branch" - url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) - req := NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) - resp := session.MakeRequest(t, req, http.StatusOK) - var fileResponse api.FileResponse - DecodeJSON(t, resp, &fileResponse) - assert.NotNil(t, fileResponse) - assert.Nil(t, fileResponse.Content) + // Test deleting a file with the wrong SHA + fileID++ + treePath = fmt.Sprintf("delete/file%d.txt", fileID) + createFile(user2, repo1, treePath) + deleteFileOptions = getDeleteFileOptions() + correctSHA := deleteFileOptions.SHA + deleteFileOptions.SHA = "badsha" + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) + req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) + resp = session.MakeRequest(t, req, http.StatusInternalServerError) + expectedAPIError := context.APIError{ + Message: "sha does not match [given: " + deleteFileOptions.SHA + ", expected: " + correctSHA + "]", + URL: base.DocURL, + } + var apiError context.APIError + DecodeJSON(t, resp, &apiError) + assert.Equal(t, expectedAPIError, apiError) - // Test deleting a file with the wrong SHA - fileID++ - treePath = fmt.Sprintf("delete/file%d.txt", fileID) - createFile(user2, repo1, treePath) - deleteFileOptions = getDeleteFileOptions() - correctSHA := deleteFileOptions.SHA - deleteFileOptions.SHA = "badsha" - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) - req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) - resp = session.MakeRequest(t, req, http.StatusInternalServerError) - expectedAPIError := context.APIError{ - Message: "sha does not match [given: " + deleteFileOptions.SHA + ", expected: " + correctSHA + "]", - URL: base.DocURL, - } - var apiError context.APIError - DecodeJSON(t, resp, &apiError) - assert.Equal(t, expectedAPIError, apiError) + // Test creating a file in repo1 by user4 who does not have write access + fileID++ + treePath = fmt.Sprintf("delete/file%d.txt", fileID) + createFile(user2, repo16, treePath) + deleteFileOptions = getDeleteFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) + req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Test creating a file in repo1 by user4 who does not have write access - fileID++ - treePath = fmt.Sprintf("delete/file%d.txt", fileID) - createFile(user2, repo16, treePath) - deleteFileOptions = getDeleteFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) - req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) + // Tests a repo with no token given so will fail + fileID++ + treePath = fmt.Sprintf("delete/file%d.txt", fileID) + createFile(user2, repo16, treePath) + deleteFileOptions = getDeleteFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) + req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Tests a repo with no token given so will fail - fileID++ - treePath = fmt.Sprintf("delete/file%d.txt", fileID) - createFile(user2, repo16, treePath) - deleteFileOptions = getDeleteFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) - req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) + // Test using access token for a private repo that the user of the token owns + fileID++ + treePath = fmt.Sprintf("delete/file%d.txt", fileID) + createFile(user2, repo16, treePath) + deleteFileOptions = getDeleteFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) + req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) + session.MakeRequest(t, req, http.StatusOK) - // Test using access token for a private repo that the user of the token owns - fileID++ - treePath = fmt.Sprintf("delete/file%d.txt", fileID) - createFile(user2, repo16, treePath) - deleteFileOptions = getDeleteFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) - req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) - session.MakeRequest(t, req, http.StatusOK) + // Test using org repo "user3/repo3" where user2 is a collaborator + fileID++ + treePath = fmt.Sprintf("delete/file%d.txt", fileID) + createFile(user3, repo3, treePath) + deleteFileOptions = getDeleteFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) + req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) + session.MakeRequest(t, req, http.StatusOK) - // Test using org repo "user3/repo3" where user2 is a collaborator - fileID++ - treePath = fmt.Sprintf("delete/file%d.txt", fileID) - createFile(user3, repo3, treePath) - deleteFileOptions = getDeleteFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) - req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) - session.MakeRequest(t, req, http.StatusOK) + // Test using org repo "user3/repo3" with no user token + fileID++ + treePath = fmt.Sprintf("delete/file%d.txt", fileID) + createFile(user3, repo3, treePath) + deleteFileOptions = getDeleteFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) + req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Test using org repo "user3/repo3" with no user token - fileID++ - treePath = fmt.Sprintf("delete/file%d.txt", fileID) - createFile(user3, repo3, treePath) - deleteFileOptions = getDeleteFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) - req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) - - // Test using repo "user2/repo1" where user4 is a NOT collaborator - fileID++ - treePath = fmt.Sprintf("delete/file%d.txt", fileID) - createFile(user2, repo1, treePath) - deleteFileOptions = getDeleteFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) - req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) - session.MakeRequest(t, req, http.StatusForbidden) + // Test using repo "user2/repo1" where user4 is a NOT collaborator + fileID++ + treePath = fmt.Sprintf("delete/file%d.txt", fileID) + createFile(user2, repo1, treePath) + deleteFileOptions = getDeleteFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) + req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions) + session.MakeRequest(t, req, http.StatusForbidden) + }) } diff --git a/integrations/api_repo_file_update_test.go b/integrations/api_repo_file_update_test.go index 71994564ff..37438339bb 100644 --- a/integrations/api_repo_file_update_test.go +++ b/integrations/api_repo_file_update_test.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "fmt" "net/http" + "net/url" "path/filepath" "testing" @@ -79,156 +80,157 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon } func TestAPIUpdateFile(t *testing.T) { - prepareTestEnv(t) - user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 - user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org - user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos - repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo - repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo - repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo - fileID := 0 + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16 + user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org + user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos + repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo + repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo + repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo + fileID := 0 - // Get user2's token - session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) - session = emptyTestSession(t) - // Get user4's token - session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) - session = emptyTestSession(t) + // Get user2's token + session := loginUser(t, user2.Name) + token2 := getTokenForLoggedInUser(t, session) + session = emptyTestSession(t) + // Get user4's token + session = loginUser(t, user4.Name) + token4 := getTokenForLoggedInUser(t, session) + session = emptyTestSession(t) - // Test updating a file in repo1 which user2 owns, try both with branch and empty branch - for _, branch := range [...]string{ - "master", // Branch - "", // Empty branch - } { + // Test updating a file in repo1 which user2 owns, try both with branch and empty branch + for _, branch := range [...]string{ + "master", // Branch + "", // Empty branch + } { + fileID++ + treePath := fmt.Sprintf("update/file%d.txt", fileID) + createFile(user2, repo1, treePath) + updateFileOptions := getUpdateFileOptions() + updateFileOptions.BranchName = branch + url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) + req := NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + resp := session.MakeRequest(t, req, http.StatusOK) + gitRepo, _ := git.OpenRepository(repo1.RepoPath()) + commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName) + expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath) + var fileResponse api.FileResponse + DecodeJSON(t, resp, &fileResponse) + assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) + assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) + assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) + } + + // Test updating a file in a new branch + updateFileOptions := getUpdateFileOptions() + updateFileOptions.BranchName = repo1.DefaultBranch + updateFileOptions.NewBranchName = "new_branch" fileID++ treePath := fmt.Sprintf("update/file%d.txt", fileID) createFile(user2, repo1, treePath) - updateFileOptions := getUpdateFileOptions() - updateFileOptions.BranchName = branch url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) req := NewRequestWithJSON(t, "PUT", url, &updateFileOptions) resp := session.MakeRequest(t, req, http.StatusOK) - gitRepo, _ := git.OpenRepository(repo1.RepoPath()) - commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName) - expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) - assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) - } + expectedSHA := "08bd14b2e2852529157324de9c226b3364e76136" + expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/update/file%d.txt", fileID) + expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID) + assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) + assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) + assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) - // Test updating a file in a new branch - updateFileOptions := getUpdateFileOptions() - updateFileOptions.BranchName = repo1.DefaultBranch - updateFileOptions.NewBranchName = "new_branch" - fileID++ - treePath := fmt.Sprintf("update/file%d.txt", fileID) - createFile(user2, repo1, treePath) - url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) - req := NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - resp := session.MakeRequest(t, req, http.StatusOK) - var fileResponse api.FileResponse - DecodeJSON(t, resp, &fileResponse) - expectedSHA := "08bd14b2e2852529157324de9c226b3364e76136" - expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/update/file%d.txt", fileID) - expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID) - assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) - assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) - assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) + // Test updating a file and renaming it + updateFileOptions = getUpdateFileOptions() + updateFileOptions.BranchName = repo1.DefaultBranch + fileID++ + treePath = fmt.Sprintf("update/file%d.txt", fileID) + createFile(user2, repo1, treePath) + updateFileOptions.FromPath = treePath + treePath = "rename/" + treePath + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) + req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &fileResponse) + expectedSHA = "08bd14b2e2852529157324de9c226b3364e76136" + expectedHTMLURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/master/rename/update/file%d.txt", fileID) + expectedDownloadURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID) + assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) + assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) + assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) - // Test updating a file and renaming it - updateFileOptions = getUpdateFileOptions() - updateFileOptions.BranchName = repo1.DefaultBranch - fileID++ - treePath = fmt.Sprintf("update/file%d.txt", fileID) - createFile(user2, repo1, treePath) - updateFileOptions.FromPath = treePath - treePath = "rename/" + treePath - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) - req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - resp = session.MakeRequest(t, req, http.StatusOK) - DecodeJSON(t, resp, &fileResponse) - expectedSHA = "08bd14b2e2852529157324de9c226b3364e76136" - expectedHTMLURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/master/rename/update/file%d.txt", fileID) - expectedDownloadURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID) - assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) - assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL) - assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) + // Test updating a file with the wrong SHA + fileID++ + treePath = fmt.Sprintf("update/file%d.txt", fileID) + createFile(user2, repo1, treePath) + updateFileOptions = getUpdateFileOptions() + correctSHA := updateFileOptions.SHA + updateFileOptions.SHA = "badsha" + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) + req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + resp = session.MakeRequest(t, req, http.StatusInternalServerError) + expectedAPIError := context.APIError{ + Message: "sha does not match [given: " + updateFileOptions.SHA + ", expected: " + correctSHA + "]", + URL: base.DocURL, + } + var apiError context.APIError + DecodeJSON(t, resp, &apiError) + assert.Equal(t, expectedAPIError, apiError) - // Test updating a file with the wrong SHA - fileID++ - treePath = fmt.Sprintf("update/file%d.txt", fileID) - createFile(user2, repo1, treePath) - updateFileOptions = getUpdateFileOptions() - correctSHA := updateFileOptions.SHA - updateFileOptions.SHA = "badsha" - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2) - req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - resp = session.MakeRequest(t, req, http.StatusInternalServerError) - expectedAPIError := context.APIError{ - Message: "sha does not match [given: " + updateFileOptions.SHA + ", expected: " + correctSHA + "]", - URL: base.DocURL, - } - var apiError context.APIError - DecodeJSON(t, resp, &apiError) - assert.Equal(t, expectedAPIError, apiError) + // Test creating a file in repo1 by user4 who does not have write access + fileID++ + treePath = fmt.Sprintf("update/file%d.txt", fileID) + createFile(user2, repo16, treePath) + updateFileOptions = getUpdateFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) + req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Test creating a file in repo1 by user4 who does not have write access - fileID++ - treePath = fmt.Sprintf("update/file%d.txt", fileID) - createFile(user2, repo16, treePath) - updateFileOptions = getUpdateFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4) - req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) + // Tests a repo with no token given so will fail + fileID++ + treePath = fmt.Sprintf("update/file%d.txt", fileID) + createFile(user2, repo16, treePath) + updateFileOptions = getUpdateFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) + req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Tests a repo with no token given so will fail - fileID++ - treePath = fmt.Sprintf("update/file%d.txt", fileID) - createFile(user2, repo16, treePath) - updateFileOptions = getUpdateFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath) - req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) + // Test using access token for a private repo that the user of the token owns + fileID++ + treePath = fmt.Sprintf("update/file%d.txt", fileID) + createFile(user2, repo16, treePath) + updateFileOptions = getUpdateFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) + req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + session.MakeRequest(t, req, http.StatusOK) - // Test using access token for a private repo that the user of the token owns - fileID++ - treePath = fmt.Sprintf("update/file%d.txt", fileID) - createFile(user2, repo16, treePath) - updateFileOptions = getUpdateFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2) - req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - session.MakeRequest(t, req, http.StatusOK) + // Test using org repo "user3/repo3" where user2 is a collaborator + fileID++ + treePath = fmt.Sprintf("update/file%d.txt", fileID) + createFile(user3, repo3, treePath) + updateFileOptions = getUpdateFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) + req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + session.MakeRequest(t, req, http.StatusOK) - // Test using org repo "user3/repo3" where user2 is a collaborator - fileID++ - treePath = fmt.Sprintf("update/file%d.txt", fileID) - createFile(user3, repo3, treePath) - updateFileOptions = getUpdateFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2) - req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - session.MakeRequest(t, req, http.StatusOK) + // Test using org repo "user3/repo3" with no user token + fileID++ + treePath = fmt.Sprintf("update/file%d.txt", fileID) + createFile(user3, repo3, treePath) + updateFileOptions = getUpdateFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) + req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + session.MakeRequest(t, req, http.StatusNotFound) - // Test using org repo "user3/repo3" with no user token - fileID++ - treePath = fmt.Sprintf("update/file%d.txt", fileID) - createFile(user3, repo3, treePath) - updateFileOptions = getUpdateFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath) - req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - session.MakeRequest(t, req, http.StatusNotFound) - - // Test using repo "user2/repo1" where user4 is a NOT collaborator - fileID++ - treePath = fmt.Sprintf("update/file%d.txt", fileID) - createFile(user2, repo1, treePath) - updateFileOptions = getUpdateFileOptions() - url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) - req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) - session.MakeRequest(t, req, http.StatusForbidden) + // Test using repo "user2/repo1" where user4 is a NOT collaborator + fileID++ + treePath = fmt.Sprintf("update/file%d.txt", fileID) + createFile(user2, repo1, treePath) + updateFileOptions = getUpdateFileOptions() + url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4) + req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions) + session.MakeRequest(t, req, http.StatusForbidden) + }) } diff --git a/integrations/editor_test.go b/integrations/editor_test.go index e2dd2e1dc4..8e6effe7eb 100644 --- a/integrations/editor_test.go +++ b/integrations/editor_test.go @@ -7,6 +7,7 @@ package integrations import ( "net/http" "net/http/httptest" + "net/url" "path" "testing" @@ -14,80 +15,79 @@ import ( ) func TestCreateFile(t *testing.T) { - prepareTestEnv(t) + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user2") - session := loginUser(t, "user2") + // Request editor page + req := NewRequest(t, "GET", "/user2/repo1/_new/master/") + resp := session.MakeRequest(t, req, http.StatusOK) - // Request editor page - req := NewRequest(t, "GET", "/user2/repo1/_new/master/") - resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + lastCommit := doc.GetInputValueByName("last_commit") + assert.NotEmpty(t, lastCommit) - doc := NewHTMLParser(t, resp.Body) - lastCommit := doc.GetInputValueByName("last_commit") - assert.NotEmpty(t, lastCommit) - - // Save new file to master branch - req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ - "_csrf": doc.GetCSRF(), - "last_commit": lastCommit, - "tree_path": "test.txt", - "content": "Content", - "commit_choice": "direct", + // Save new file to master branch + req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ + "_csrf": doc.GetCSRF(), + "last_commit": lastCommit, + "tree_path": "test.txt", + "content": "Content", + "commit_choice": "direct", + }) + resp = session.MakeRequest(t, req, http.StatusFound) }) - resp = session.MakeRequest(t, req, http.StatusFound) } func TestCreateFileOnProtectedBranch(t *testing.T) { - prepareTestEnv(t) + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user2") - session := loginUser(t, "user2") + csrf := GetCSRF(t, session, "/user2/repo1/settings/branches") + // Change master branch to protected + req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{ + "_csrf": csrf, + "protected": "on", + }) + resp := session.MakeRequest(t, req, http.StatusFound) + // Check if master branch has been locked successfully + flashCookie := session.GetCookie("macaron_flash") + assert.NotNil(t, flashCookie) + assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) - csrf := GetCSRF(t, session, "/user2/repo1/settings/branches") - // Change master branch to protected - req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{ - "_csrf": csrf, - "protected": "on", + // Request editor page + req = NewRequest(t, "GET", "/user2/repo1/_new/master/") + resp = session.MakeRequest(t, req, http.StatusOK) + + doc := NewHTMLParser(t, resp.Body) + lastCommit := doc.GetInputValueByName("last_commit") + assert.NotEmpty(t, lastCommit) + + // Save new file to master branch + req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ + "_csrf": doc.GetCSRF(), + "last_commit": lastCommit, + "tree_path": "test.txt", + "content": "Content", + "commit_choice": "direct", + }) + + resp = session.MakeRequest(t, req, http.StatusOK) + // Check body for error message + assert.Contains(t, resp.Body.String(), "Cannot commit to protected branch 'master'.") + + // remove the protected branch + csrf = GetCSRF(t, session, "/user2/repo1/settings/branches") + // Change master branch to protected + req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{ + "_csrf": csrf, + "protected": "off", + }) + resp = session.MakeRequest(t, req, http.StatusFound) + // Check if master branch has been locked successfully + flashCookie = session.GetCookie("macaron_flash") + assert.NotNil(t, flashCookie) + assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bdisabled.", flashCookie.Value) }) - resp := session.MakeRequest(t, req, http.StatusFound) - // Check if master branch has been locked successfully - flashCookie := session.GetCookie("macaron_flash") - assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) - - // Request editor page - req = NewRequest(t, "GET", "/user2/repo1/_new/master/") - resp = session.MakeRequest(t, req, http.StatusOK) - - doc := NewHTMLParser(t, resp.Body) - lastCommit := doc.GetInputValueByName("last_commit") - assert.NotEmpty(t, lastCommit) - - // Save new file to master branch - req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ - "_csrf": doc.GetCSRF(), - "last_commit": lastCommit, - "tree_path": "test.txt", - "content": "Content", - "commit_choice": "direct", - }) - - resp = session.MakeRequest(t, req, http.StatusOK) - // Check body for error message - assert.Contains(t, resp.Body.String(), "Cannot commit to protected branch 'master'.") - - // remove the protected branch - csrf = GetCSRF(t, session, "/user2/repo1/settings/branches") - // Change master branch to protected - req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/master", map[string]string{ - "_csrf": csrf, - "protected": "off", - }) - resp = session.MakeRequest(t, req, http.StatusFound) - // Check if master branch has been locked successfully - flashCookie = session.GetCookie("macaron_flash") - assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527master%2527%2Bhas%2Bbeen%2Bdisabled.", flashCookie.Value) - } func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) *httptest.ResponseRecorder { @@ -151,13 +151,15 @@ func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, bra } func TestEditFile(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user2") - testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n") + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user2") + testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n") + }) } func TestEditFileToNewBranch(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user2") - testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n") + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user2") + testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n") + }) } diff --git a/integrations/integration_test.go b/integrations/integration_test.go index 566859518a..93dacaf78a 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -178,7 +178,6 @@ func prepareTestEnv(t testing.TB, skip ...int) { assert.NoError(t, models.LoadFixtures()) assert.NoError(t, os.RemoveAll(setting.RepoRootPath)) assert.NoError(t, os.RemoveAll(models.LocalCopyPath())) - assert.NoError(t, os.RemoveAll(models.LocalWikiPath())) assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath)) diff --git a/integrations/pull_create_test.go b/integrations/pull_create_test.go index ed553bf162..8f39e8b028 100644 --- a/integrations/pull_create_test.go +++ b/integrations/pull_create_test.go @@ -7,6 +7,7 @@ package integrations import ( "net/http" "net/http/httptest" + "net/url" "path" "strings" "testing" @@ -43,63 +44,65 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch, titl } func TestPullCreate(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") - // check the redirected URL - url := resp.HeaderMap.Get("Location") - assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url) + // check the redirected URL + url := resp.HeaderMap.Get("Location") + assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url) - // check .diff can be accessed and matches performed change - req := NewRequest(t, "GET", url+".diff") - resp = session.MakeRequest(t, req, http.StatusOK) - assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body) - assert.Regexp(t, "^diff", resp.Body) - assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one + // check .diff can be accessed and matches performed change + req := NewRequest(t, "GET", url+".diff") + resp = session.MakeRequest(t, req, http.StatusOK) + assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body) + assert.Regexp(t, "^diff", resp.Body) + assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one - // check .patch can be accessed and matches performed change - req = NewRequest(t, "GET", url+".patch") - resp = session.MakeRequest(t, req, http.StatusOK) - assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body) - assert.Regexp(t, "diff", resp.Body) - assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body) - assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one + // check .patch can be accessed and matches performed change + req = NewRequest(t, "GET", url+".patch") + resp = session.MakeRequest(t, req, http.StatusOK) + assert.Regexp(t, `\+Hello, World \(Edited\)`, resp.Body) + assert.Regexp(t, "diff", resp.Body) + assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body) + assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one + }) } func TestPullCreate_TitleEscape(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", "master", "XSS PR") + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + resp := testPullCreate(t, session, "user1", "repo1", "master", "XSS PR") - // check the redirected URL - url := resp.HeaderMap.Get("Location") - assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url) + // check the redirected URL + url := resp.HeaderMap.Get("Location") + assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url) - // Edit title - req := NewRequest(t, "GET", url) - resp = session.MakeRequest(t, req, http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url") - assert.True(t, exists, "The template has changed") + // Edit title + req := NewRequest(t, "GET", url) + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url") + assert.True(t, exists, "The template has changed") - req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{ - "_csrf": htmlDoc.GetCSRF(), - "title": "XSS PR", + req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{ + "_csrf": htmlDoc.GetCSRF(), + "title": "XSS PR", + }) + session.MakeRequest(t, req, http.StatusOK) + + req = NewRequest(t, "GET", url) + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc = NewHTMLParser(t, resp.Body) + titleHTML, err := htmlDoc.doc.Find(".comments .event .text b").First().Html() + assert.NoError(t, err) + assert.Equal(t, "<i>XSS PR</i>", titleHTML) + titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html() + assert.NoError(t, err) + assert.Equal(t, "<u>XSS PR</u>", titleHTML) }) - session.MakeRequest(t, req, http.StatusOK) - - req = NewRequest(t, "GET", url) - resp = session.MakeRequest(t, req, http.StatusOK) - htmlDoc = NewHTMLParser(t, resp.Body) - titleHTML, err := htmlDoc.doc.Find(".comments .event .text b").First().Html() - assert.NoError(t, err) - assert.Equal(t, "<i>XSS PR</i>", titleHTML) - titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html() - assert.NoError(t, err) - assert.Equal(t, "<u>XSS PR</u>", titleHTML) } diff --git a/integrations/pull_merge_test.go b/integrations/pull_merge_test.go index f466515eb4..1f0ad27a0b 100644 --- a/integrations/pull_merge_test.go +++ b/integrations/pull_merge_test.go @@ -7,6 +7,7 @@ package integrations import ( "net/http" "net/http/httptest" + "net/url" "path" "strings" "testing" @@ -52,108 +53,118 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str } func TestPullMerge(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") + resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") - elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) - testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) + elem := strings.Split(test.RedirectURL(resp), "/") + assert.EqualValues(t, "pulls", elem[3]) + testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) + }) } func TestPullRebase(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") + resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") - elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) - testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase) + elem := strings.Split(test.RedirectURL(resp), "/") + assert.EqualValues(t, "pulls", elem[3]) + testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase) + }) } func TestPullRebaseMerge(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + prepareTestEnv(t) + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") + resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") - elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) - testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebaseMerge) + elem := strings.Split(test.RedirectURL(resp), "/") + assert.EqualValues(t, "pulls", elem[3]) + testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebaseMerge) + }) } func TestPullSquash(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n") + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + prepareTestEnv(t) + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n") - resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") + resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") - elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) - testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash) + elem := strings.Split(test.RedirectURL(resp), "/") + assert.EqualValues(t, "pulls", elem[3]) + testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash) + }) } func TestPullCleanUpAfterMerge(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n") + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + prepareTestEnv(t) + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title") + resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title") - elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) - testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) + elem := strings.Split(test.RedirectURL(resp), "/") + assert.EqualValues(t, "pulls", elem[3]) + testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) - // Check PR branch deletion - resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4]) - respJSON := struct { - Redirect string - }{} - DecodeJSON(t, resp, &respJSON) + // Check PR branch deletion + resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4]) + respJSON := struct { + Redirect string + }{} + DecodeJSON(t, resp, &respJSON) - assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found") + assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found") - elem = strings.Split(respJSON.Redirect, "/") - assert.EqualValues(t, "pulls", elem[3]) + elem = strings.Split(respJSON.Redirect, "/") + assert.EqualValues(t, "pulls", elem[3]) - // Check branch deletion result - req := NewRequest(t, "GET", respJSON.Redirect) - resp = session.MakeRequest(t, req, http.StatusOK) + // Check branch deletion result + req := NewRequest(t, "GET", respJSON.Redirect) + resp = session.MakeRequest(t, req, http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - resultMsg := htmlDoc.doc.Find(".ui.message>p").Text() + htmlDoc := NewHTMLParser(t, resp.Body) + resultMsg := htmlDoc.doc.Find(".ui.message>p").Text() - assert.EqualValues(t, "Branch 'user1/feature/test' has been deleted.", resultMsg) + assert.EqualValues(t, "Branch 'user1/feature/test' has been deleted.", resultMsg) + }) } func TestCantMergeWorkInProgress(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + prepareTestEnv(t) + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", "master", "[wip] This is a pull title") + resp := testPullCreate(t, session, "user1", "repo1", "master", "[wip] This is a pull title") - req := NewRequest(t, "GET", resp.Header().Get("Location")) - resp = session.MakeRequest(t, req, http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - text := strings.TrimSpace(htmlDoc.doc.Find(".merge.segment > .text.grey").Text()) - assert.NotEmpty(t, text, "Can't find WIP text") + req := NewRequest(t, "GET", resp.Header().Get("Location")) + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + text := strings.TrimSpace(htmlDoc.doc.Find(".merge.segment > .text.grey").Text()) + assert.NotEmpty(t, text, "Can't find WIP text") - // remove from lang - expected := i18n.Tr("en", "repo.pulls.cannot_merge_work_in_progress", "[wip]") - replacer := strings.NewReplacer("", "", "", "") - assert.Equal(t, replacer.Replace(expected), text, "Unable to find WIP text") + // remove from lang + expected := i18n.Tr("en", "repo.pulls.cannot_merge_work_in_progress", "[wip]") + replacer := strings.NewReplacer("", "", "", "") + assert.Equal(t, replacer.Replace(expected), text, "Unable to find WIP text") + }) } diff --git a/integrations/pull_status_test.go b/integrations/pull_status_test.go index 2381444676..2a4d8e0b68 100644 --- a/integrations/pull_status_test.go +++ b/integrations/pull_status_test.go @@ -6,6 +6,7 @@ package integrations import ( "fmt" "net/http" + "net/url" "path" "testing" @@ -16,78 +17,79 @@ import ( ) func TestPullCreate_CommitStatus(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1") + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1") - url := path.Join("user1", "repo1", "compare", "master...status1") - req := NewRequestWithValues(t, "POST", url, - map[string]string{ - "_csrf": GetCSRF(t, session, url), - "title": "pull request from status1", - }, - ) - session.MakeRequest(t, req, http.StatusFound) - - req = NewRequest(t, "GET", "/user1/repo1/pulls") - resp := session.MakeRequest(t, req, http.StatusOK) - doc := NewHTMLParser(t, resp.Body) - - // Request repository commits page - req = NewRequest(t, "GET", "/user1/repo1/pulls/1/commits") - resp = session.MakeRequest(t, req, http.StatusOK) - doc = NewHTMLParser(t, resp.Body) - - // Get first commit URL - commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") - assert.True(t, exists) - assert.NotEmpty(t, commitURL) - - commitID := path.Base(commitURL) - - statusList := []models.CommitStatusState{ - models.CommitStatusPending, - models.CommitStatusError, - models.CommitStatusFailure, - models.CommitStatusWarning, - models.CommitStatusSuccess, - } - - statesIcons := map[models.CommitStatusState]string{ - models.CommitStatusPending: "circle icon yellow", - models.CommitStatusSuccess: "check icon green", - models.CommitStatusError: "warning icon red", - models.CommitStatusFailure: "remove icon red", - models.CommitStatusWarning: "warning sign icon yellow", - } - - // Update commit status, and check if icon is updated as well - for _, status := range statusList { - - // Call API to add status for commit - token := getTokenForLoggedInUser(t, session) - req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/user1/repo1/statuses/%s?token=%s", commitID, token), - api.CreateStatusOption{ - State: api.StatusState(status), - TargetURL: "http://test.ci/", - Description: "", - Context: "testci", + url := path.Join("user1", "repo1", "compare", "master...status1") + req := NewRequestWithValues(t, "POST", url, + map[string]string{ + "_csrf": GetCSRF(t, session, url), + "title": "pull request from status1", }, ) - session.MakeRequest(t, req, http.StatusCreated) + session.MakeRequest(t, req, http.StatusFound) - req = NewRequestf(t, "GET", "/user1/repo1/pulls/1/commits") + req = NewRequest(t, "GET", "/user1/repo1/pulls") + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + + // Request repository commits page + req = NewRequest(t, "GET", "/user1/repo1/pulls/1/commits") resp = session.MakeRequest(t, req, http.StatusOK) doc = NewHTMLParser(t, resp.Body) - commitURL, exists = doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") + // Get first commit URL + commitURL, exists := doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") assert.True(t, exists) assert.NotEmpty(t, commitURL) - assert.EqualValues(t, commitID, path.Base(commitURL)) - cls, ok := doc.doc.Find("#commits-table tbody tr td.message i.commit-status").Last().Attr("class") - assert.True(t, ok) - assert.EqualValues(t, "commit-status "+statesIcons[status], cls) - } + commitID := path.Base(commitURL) + + statusList := []models.CommitStatusState{ + models.CommitStatusPending, + models.CommitStatusError, + models.CommitStatusFailure, + models.CommitStatusWarning, + models.CommitStatusSuccess, + } + + statesIcons := map[models.CommitStatusState]string{ + models.CommitStatusPending: "circle icon yellow", + models.CommitStatusSuccess: "check icon green", + models.CommitStatusError: "warning icon red", + models.CommitStatusFailure: "remove icon red", + models.CommitStatusWarning: "warning sign icon yellow", + } + + // Update commit status, and check if icon is updated as well + for _, status := range statusList { + + // Call API to add status for commit + token := getTokenForLoggedInUser(t, session) + req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/user1/repo1/statuses/%s?token=%s", commitID, token), + api.CreateStatusOption{ + State: api.StatusState(status), + TargetURL: "http://test.ci/", + Description: "", + Context: "testci", + }, + ) + session.MakeRequest(t, req, http.StatusCreated) + + req = NewRequestf(t, "GET", "/user1/repo1/pulls/1/commits") + resp = session.MakeRequest(t, req, http.StatusOK) + doc = NewHTMLParser(t, resp.Body) + + commitURL, exists = doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") + assert.True(t, exists) + assert.NotEmpty(t, commitURL) + assert.EqualValues(t, commitID, path.Base(commitURL)) + + cls, ok := doc.doc.Find("#commits-table tbody tr td.message i.commit-status").Last().Attr("class") + assert.True(t, ok) + assert.EqualValues(t, "commit-status "+statesIcons[status], cls) + } + }) } diff --git a/integrations/repo_activity_test.go b/integrations/repo_activity_test.go index fcdbda2c89..cec5c79c4d 100644 --- a/integrations/repo_activity_test.go +++ b/integrations/repo_activity_test.go @@ -6,6 +6,7 @@ package integrations import ( "net/http" + "net/url" "strings" "testing" @@ -16,49 +17,51 @@ import ( ) func TestRepoActivity(t *testing.T) { - prepareTestEnv(t) - session := loginUser(t, "user1") + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - // Create PRs (1 merged & 2 proposed) - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") - elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) - testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) + session := loginUser(t, "user1") - testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n") - testPullCreate(t, session, "user1", "repo1", "feat/better_readme", "This is a pull title") + // Create PRs (1 merged & 2 proposed) + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title") + elem := strings.Split(test.RedirectURL(resp), "/") + assert.EqualValues(t, "pulls", elem[3]) + testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge) - testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n") - testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme", "This is a pull title") + testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n") + testPullCreate(t, session, "user1", "repo1", "feat/better_readme", "This is a pull title") - // Create issues (3 new issues) - testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1") - testNewIssue(t, session, "user2", "repo1", "Issue 2", "Description 2") - testNewIssue(t, session, "user2", "repo1", "Issue 3", "Description 3") + testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n") + testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme", "This is a pull title") - // Create releases (1 new release) - createNewRelease(t, session, "/user2/repo1", "v1.0.0", "v1.0.0", false, false) + // Create issues (3 new issues) + testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1") + testNewIssue(t, session, "user2", "repo1", "Issue 2", "Description 2") + testNewIssue(t, session, "user2", "repo1", "Issue 3", "Description 3") - // Open Activity page and check stats - req := NewRequest(t, "GET", "/user2/repo1/activity") - resp = session.MakeRequest(t, req, http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) + // Create releases (1 new release) + createNewRelease(t, session, "/user2/repo1", "v1.0.0", "v1.0.0", false, false) - // Should be 1 published release - list := htmlDoc.doc.Find("#published-releases").Next().Find("p.desc") - assert.Len(t, list.Nodes, 1) + // Open Activity page and check stats + req := NewRequest(t, "GET", "/user2/repo1/activity") + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) - // Should be 1 merged pull request - list = htmlDoc.doc.Find("#merged-pull-requests").Next().Find("p.desc") - assert.Len(t, list.Nodes, 1) + // Should be 1 published release + list := htmlDoc.doc.Find("#published-releases").Next().Find("p.desc") + assert.Len(t, list.Nodes, 1) - // Should be 2 merged proposed pull requests - list = htmlDoc.doc.Find("#proposed-pull-requests").Next().Find("p.desc") - assert.Len(t, list.Nodes, 2) + // Should be 1 merged pull request + list = htmlDoc.doc.Find("#merged-pull-requests").Next().Find("p.desc") + assert.Len(t, list.Nodes, 1) - // Should be 3 new issues - list = htmlDoc.doc.Find("#new-issues").Next().Find("p.desc") - assert.Len(t, list.Nodes, 3) + // Should be 2 merged proposed pull requests + list = htmlDoc.doc.Find("#proposed-pull-requests").Next().Find("p.desc") + assert.Len(t, list.Nodes, 2) + + // Should be 3 new issues + list = htmlDoc.doc.Find("#new-issues").Next().Find("p.desc") + assert.Len(t, list.Nodes, 3) + }) } diff --git a/integrations/repo_branch_test.go b/integrations/repo_branch_test.go index 3101dc4c0f..a5447cfb66 100644 --- a/integrations/repo_branch_test.go +++ b/integrations/repo_branch_test.go @@ -6,6 +6,7 @@ package integrations import ( "net/http" + "net/url" "path" "strings" "testing" @@ -35,6 +36,10 @@ func testCreateBranch(t testing.TB, session *TestSession, user, repo, oldRefSubU } func TestCreateBranch(t *testing.T) { + onGiteaRun(t, testCreateBranches) +} + +func testCreateBranches(t *testing.T, giteaURL *url.URL) { tests := []struct { OldRefSubURL string NewBranch string diff --git a/modules/repofiles/delete_test.go b/integrations/repofiles_delete_test.go similarity index 76% rename from modules/repofiles/delete_test.go rename to integrations/repofiles_delete_test.go index 9d034066f5..c3bb18ec9c 100644 --- a/modules/repofiles/delete_test.go +++ b/integrations/repofiles_delete_test.go @@ -2,20 +2,22 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package repofiles +package integrations import ( + "net/url" "testing" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/repofiles" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" ) -func getDeleteRepoFileOptions(repo *models.Repository) *DeleteRepoFileOptions { - return &DeleteRepoFileOptions{ +func getDeleteRepoFileOptions(repo *models.Repository) *repofiles.DeleteRepoFileOptions { + return &repofiles.DeleteRepoFileOptions{ LastCommitID: "", OldBranch: repo.DefaultBranch, NewBranch: repo.DefaultBranch, @@ -27,15 +29,15 @@ func getDeleteRepoFileOptions(repo *models.Repository) *DeleteRepoFileOptions { } } -func getExpectedDeleteFileResponse() *api.FileResponse { +func getExpectedDeleteFileResponse(u *url.URL) *api.FileResponse { return &api.FileResponse{ Content: nil, Commit: &api.FileCommitResponse{ CommitMeta: api.CommitMeta{ - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", + URL: u.String() + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", }, - HTMLURL: "https://try.gitea.io/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d", + HTMLURL: u.String() + "user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d", Author: &api.CommitUser{ Identity: api.Identity{ Name: "user1", @@ -53,7 +55,7 @@ func getExpectedDeleteFileResponse() *api.FileResponse { Parents: []*api.CommitMeta{}, Message: "Initial commit\n", Tree: &api.CommitMeta{ - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6", + URL: u.String() + "api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6", SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6", }, }, @@ -67,6 +69,10 @@ func getExpectedDeleteFileResponse() *api.FileResponse { } func TestDeleteRepoFile(t *testing.T) { + onGiteaRun(t, testDeleteRepoFile) +} + +func testDeleteRepoFile(t *testing.T, u *url.URL) { // setup models.PrepareTestEnv(t) ctx := test.MockContext(t, "user2/repo1") @@ -80,14 +86,14 @@ func TestDeleteRepoFile(t *testing.T) { opts := getDeleteRepoFileOptions(repo) t.Run("Delete README.md file", func(t *testing.T) { - fileResponse, err := DeleteRepoFile(repo, doer, opts) + fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) assert.Nil(t, err) - expectedFileResponse := getExpectedDeleteFileResponse() + expectedFileResponse := getExpectedDeleteFileResponse(u) assert.EqualValues(t, expectedFileResponse, fileResponse) }) t.Run("Verify README.md has been deleted", func(t *testing.T) { - fileResponse, err := DeleteRepoFile(repo, doer, opts) + fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) assert.Nil(t, fileResponse) expectedError := "repository file does not exist [path: " + opts.TreePath + "]" assert.EqualError(t, err, expectedError) @@ -96,6 +102,10 @@ func TestDeleteRepoFile(t *testing.T) { // Test opts with branch names removed, same results func TestDeleteRepoFileWithoutBranchNames(t *testing.T) { + onGiteaRun(t, testDeleteRepoFileWithoutBranchNames) +} + +func testDeleteRepoFileWithoutBranchNames(t *testing.T, u *url.URL) { // setup models.PrepareTestEnv(t) ctx := test.MockContext(t, "user2/repo1") @@ -111,9 +121,9 @@ func TestDeleteRepoFileWithoutBranchNames(t *testing.T) { opts.NewBranch = "" t.Run("Delete README.md without Branch Name", func(t *testing.T) { - fileResponse, err := DeleteRepoFile(repo, doer, opts) + fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) assert.Nil(t, err) - expectedFileResponse := getExpectedDeleteFileResponse() + expectedFileResponse := getExpectedDeleteFileResponse(u) assert.EqualValues(t, expectedFileResponse, fileResponse) }) } @@ -133,7 +143,7 @@ func TestDeleteRepoFileErrors(t *testing.T) { t.Run("Bad branch", func(t *testing.T) { opts := getDeleteRepoFileOptions(repo) opts.OldBranch = "bad_branch" - fileResponse, err := DeleteRepoFile(repo, doer, opts) + fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) assert.Error(t, err) assert.Nil(t, fileResponse) expectedError := "branch does not exist [name: " + opts.OldBranch + "]" @@ -144,7 +154,7 @@ func TestDeleteRepoFileErrors(t *testing.T) { opts := getDeleteRepoFileOptions(repo) origSHA := opts.SHA opts.SHA = "bad_sha" - fileResponse, err := DeleteRepoFile(repo, doer, opts) + fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) assert.Nil(t, fileResponse) assert.Error(t, err) expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]" @@ -154,7 +164,7 @@ func TestDeleteRepoFileErrors(t *testing.T) { t.Run("New branch already exists", func(t *testing.T) { opts := getDeleteRepoFileOptions(repo) opts.NewBranch = "develop" - fileResponse, err := DeleteRepoFile(repo, doer, opts) + fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) assert.Nil(t, fileResponse) assert.Error(t, err) expectedError := "branch already exists [name: " + opts.NewBranch + "]" @@ -164,7 +174,7 @@ func TestDeleteRepoFileErrors(t *testing.T) { t.Run("TreePath is empty:", func(t *testing.T) { opts := getDeleteRepoFileOptions(repo) opts.TreePath = "" - fileResponse, err := DeleteRepoFile(repo, doer, opts) + fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) assert.Nil(t, fileResponse) assert.Error(t, err) expectedError := "path contains a malformed path component [path: ]" @@ -174,7 +184,7 @@ func TestDeleteRepoFileErrors(t *testing.T) { t.Run("TreePath is a git directory:", func(t *testing.T) { opts := getDeleteRepoFileOptions(repo) opts.TreePath = ".git" - fileResponse, err := DeleteRepoFile(repo, doer, opts) + fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts) assert.Nil(t, fileResponse) assert.Error(t, err) expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]" diff --git a/integrations/repofiles_update_test.go b/integrations/repofiles_update_test.go new file mode 100644 index 0000000000..02a9bbeb16 --- /dev/null +++ b/integrations/repofiles_update_test.go @@ -0,0 +1,365 @@ +// Copyright 2019 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 integrations + +import ( + "net/url" + "testing" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/repofiles" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" + + "github.com/stretchr/testify/assert" +) + +func getCreateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFileOptions { + return &repofiles.UpdateRepoFileOptions{ + OldBranch: repo.DefaultBranch, + NewBranch: repo.DefaultBranch, + TreePath: "new/file.txt", + Message: "Creates new/file.txt", + Content: "This is a NEW file", + IsNewFile: true, + Author: nil, + Committer: nil, + } +} + +func getUpdateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFileOptions { + return &repofiles.UpdateRepoFileOptions{ + OldBranch: repo.DefaultBranch, + NewBranch: repo.DefaultBranch, + TreePath: "README.md", + Message: "Updates README.md", + SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", + Content: "This is UPDATED content for the README file", + IsNewFile: false, + Author: nil, + Committer: nil, + } +} + +func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileResponse { + return &api.FileResponse{ + Content: &api.FileContentResponse{ + Name: "file.txt", + Path: "new/file.txt", + SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", + Size: 18, + URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt", + HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt", + GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", + DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/new/file.txt", + Type: "blob", + Links: &api.FileLinksResponse{ + Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt", + GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", + HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt", + }, + }, + Commit: &api.FileCommitResponse{ + CommitMeta: api.CommitMeta{ + URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID, + SHA: commitID, + }, + HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID, + Author: &api.CommitUser{ + Identity: api.Identity{ + Name: "User Two", + Email: "user2@noreply.example.org", + }, + Date: time.Now().UTC().Format(time.RFC3339), + }, + Committer: &api.CommitUser{ + Identity: api.Identity{ + Name: "User Two", + Email: "user2@noreply.example.org", + }, + Date: time.Now().UTC().Format(time.RFC3339), + }, + Parents: []*api.CommitMeta{ + { + URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", + SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", + }, + }, + Message: "Updates README.md\n", + Tree: &api.CommitMeta{ + URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", + SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc", + }, + }, + Verification: &api.PayloadCommitVerification{ + Verified: false, + Reason: "unsigned", + Signature: "", + Payload: "", + }, + } +} + +func getExpectedFileResponseForRepofilesUpdate(commitID string) *api.FileResponse { + return &api.FileResponse{ + Content: &api.FileContentResponse{ + Name: "README.md", + Path: "README.md", + SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", + Size: 43, + URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md", + HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md", + GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", + DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/README.md", + Type: "blob", + Links: &api.FileLinksResponse{ + Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md", + GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", + HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md", + }, + }, + Commit: &api.FileCommitResponse{ + CommitMeta: api.CommitMeta{ + URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID, + SHA: commitID, + }, + HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID, + Author: &api.CommitUser{ + Identity: api.Identity{ + Name: "User Two", + Email: "user2@noreply.example.org", + }, + Date: time.Now().UTC().Format(time.RFC3339), + }, + Committer: &api.CommitUser{ + Identity: api.Identity{ + Name: "User Two", + Email: "user2@noreply.example.org", + }, + Date: time.Now().UTC().Format(time.RFC3339), + }, + Parents: []*api.CommitMeta{ + { + URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", + SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", + }, + }, + Message: "Updates README.md\n", + Tree: &api.CommitMeta{ + URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", + SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc", + }, + }, + Verification: &api.PayloadCommitVerification{ + Verified: false, + Reason: "unsigned", + Signature: "", + Payload: "", + }, + } +} + +func TestCreateOrUpdateRepoFileForCreate(t *testing.T) { + // setup + onGiteaRun(t, func(t *testing.T, u *url.URL) { + ctx := test.MockContext(t, "user2/repo1") + ctx.SetParams(":id", "1") + test.LoadRepo(t, ctx, 1) + test.LoadRepoCommit(t, ctx) + test.LoadUser(t, ctx, 2) + test.LoadGitRepo(t, ctx) + repo := ctx.Repo.Repository + doer := ctx.User + opts := getCreateRepoFileOptions(repo) + + // test + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + + // asserts + assert.Nil(t, err) + gitRepo, _ := git.OpenRepository(repo.RepoPath()) + commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) + expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID) + assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) + assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) + assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) + }) +} + +func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) { + // setup + onGiteaRun(t, func(t *testing.T, u *url.URL) { + ctx := test.MockContext(t, "user2/repo1") + ctx.SetParams(":id", "1") + test.LoadRepo(t, ctx, 1) + test.LoadRepoCommit(t, ctx) + test.LoadUser(t, ctx, 2) + test.LoadGitRepo(t, ctx) + repo := ctx.Repo.Repository + doer := ctx.User + opts := getUpdateRepoFileOptions(repo) + + // test + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + + // asserts + assert.Nil(t, err) + gitRepo, _ := git.OpenRepository(repo.RepoPath()) + commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID) + assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) + assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) + assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) + }) +} + +func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { + // setup + onGiteaRun(t, func(t *testing.T, u *url.URL) { + ctx := test.MockContext(t, "user2/repo1") + ctx.SetParams(":id", "1") + test.LoadRepo(t, ctx, 1) + test.LoadRepoCommit(t, ctx) + test.LoadUser(t, ctx, 2) + test.LoadGitRepo(t, ctx) + repo := ctx.Repo.Repository + doer := ctx.User + opts := getUpdateRepoFileOptions(repo) + suffix := "_new" + opts.FromTreePath = "README.md" + opts.TreePath = "README.md" + suffix // new file name, README.md_new + + // test + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + + // asserts + assert.Nil(t, err) + gitRepo, _ := git.OpenRepository(repo.RepoPath()) + commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String()) + // assert that the old file no longer exists in the last commit of the branch + fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath) + toEntry, err := commit.GetTreeEntryByPath(opts.TreePath) + assert.Nil(t, fromEntry) // Should no longer exist here + assert.NotNil(t, toEntry) // Should exist here + // assert SHA has remained the same but paths use the new file name + assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA) + assert.EqualValues(t, expectedFileResponse.Content.Name+suffix, fileResponse.Content.Name) + assert.EqualValues(t, expectedFileResponse.Content.Path+suffix, fileResponse.Content.Path) + assert.EqualValues(t, expectedFileResponse.Content.URL+suffix, fileResponse.Content.URL) + assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) + assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) + }) +} + +// Test opts with branch names removed, should get same results as above test +func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) { + // setup + onGiteaRun(t, func(t *testing.T, u *url.URL) { + ctx := test.MockContext(t, "user2/repo1") + ctx.SetParams(":id", "1") + test.LoadRepo(t, ctx, 1) + test.LoadRepoCommit(t, ctx) + test.LoadUser(t, ctx, 2) + test.LoadGitRepo(t, ctx) + repo := ctx.Repo.Repository + doer := ctx.User + opts := getUpdateRepoFileOptions(repo) + opts.OldBranch = "" + opts.NewBranch = "" + + // test + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + + // asserts + assert.Nil(t, err) + gitRepo, _ := git.OpenRepository(repo.RepoPath()) + commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID) + assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) + }) +} + +func TestCreateOrUpdateRepoFileErrors(t *testing.T) { + // setup + onGiteaRun(t, func(t *testing.T, u *url.URL) { + ctx := test.MockContext(t, "user2/repo1") + ctx.SetParams(":id", "1") + test.LoadRepo(t, ctx, 1) + test.LoadRepoCommit(t, ctx) + test.LoadUser(t, ctx, 2) + test.LoadGitRepo(t, ctx) + repo := ctx.Repo.Repository + doer := ctx.User + + t.Run("bad branch", func(t *testing.T) { + opts := getUpdateRepoFileOptions(repo) + opts.OldBranch = "bad_branch" + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + assert.Error(t, err) + assert.Nil(t, fileResponse) + expectedError := "branch does not exist [name: " + opts.OldBranch + "]" + assert.EqualError(t, err, expectedError) + }) + + t.Run("bad SHA", func(t *testing.T) { + opts := getUpdateRepoFileOptions(repo) + origSHA := opts.SHA + opts.SHA = "bad_sha" + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + assert.Nil(t, fileResponse) + assert.Error(t, err) + expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]" + assert.EqualError(t, err, expectedError) + }) + + t.Run("new branch already exists", func(t *testing.T) { + opts := getUpdateRepoFileOptions(repo) + opts.NewBranch = "develop" + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + assert.Nil(t, fileResponse) + assert.Error(t, err) + expectedError := "branch already exists [name: " + opts.NewBranch + "]" + assert.EqualError(t, err, expectedError) + }) + + t.Run("treePath is empty:", func(t *testing.T) { + opts := getUpdateRepoFileOptions(repo) + opts.TreePath = "" + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + assert.Nil(t, fileResponse) + assert.Error(t, err) + expectedError := "path contains a malformed path component [path: ]" + assert.EqualError(t, err, expectedError) + }) + + t.Run("treePath is a git directory:", func(t *testing.T) { + opts := getUpdateRepoFileOptions(repo) + opts.TreePath = ".git" + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + assert.Nil(t, fileResponse) + assert.Error(t, err) + expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]" + assert.EqualError(t, err, expectedError) + }) + + t.Run("create file that already exists", func(t *testing.T) { + opts := getCreateRepoFileOptions(repo) + opts.TreePath = "README.md" //already exists + fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) + assert.Nil(t, fileResponse) + assert.Error(t, err) + expectedError := "repository file already exists [path: " + opts.TreePath + "]" + assert.EqualError(t, err, expectedError) + }) + }) +} diff --git a/models/helper_directory.go b/models/helper_directory.go new file mode 100644 index 0000000000..417402b41c --- /dev/null +++ b/models/helper_directory.go @@ -0,0 +1,45 @@ +// Copyright 2019 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 models + +import ( + "fmt" + "os" + "path" + "path/filepath" + "time" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + "github.com/Unknwon/com" +) + +// LocalCopyPath returns the local repository temporary copy path. +func LocalCopyPath() string { + if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) { + return setting.Repository.Local.LocalCopyPath + } + return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath) +} + +// CreateTemporaryPath creates a temporary path +func CreateTemporaryPath(prefix string) (string, error) { + timeStr := com.ToStr(time.Now().Nanosecond()) // SHOULD USE SOMETHING UNIQUE + basePath := path.Join(LocalCopyPath(), prefix+"-"+timeStr+".git") + if err := os.MkdirAll(filepath.Dir(basePath), os.ModePerm); err != nil { + log.Error("Unable to create temporary directory: %s (%v)", basePath, err) + return "", fmt.Errorf("Failed to create dir %s: %v", basePath, err) + } + return basePath, nil +} + +// RemoveTemporaryPath removes the temporary path +func RemoveTemporaryPath(basePath string) error { + if _, err := os.Stat(basePath); !os.IsNotExist(err) { + return os.RemoveAll(basePath) + } + return nil +} diff --git a/models/helper_environment.go b/models/helper_environment.go new file mode 100644 index 0000000000..283584cc52 --- /dev/null +++ b/models/helper_environment.go @@ -0,0 +1,36 @@ +// Copyright 2019 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 models + +import ( + "fmt" + "os" + "strings" +) + +// PushingEnvironment returns an os environment to allow hooks to work on push +func PushingEnvironment(doer *User, repo *Repository) []string { + isWiki := "false" + if strings.HasSuffix(repo.Name, ".wiki") { + isWiki = "true" + } + + sig := doer.NewGitSig() + + return append(os.Environ(), + "GIT_AUTHOR_NAME="+sig.Name, + "GIT_AUTHOR_EMAIL="+sig.Email, + "GIT_COMMITTER_NAME="+sig.Name, + "GIT_COMMITTER_EMAIL="+sig.Email, + EnvRepoName+"="+repo.Name, + EnvRepoUsername+"="+repo.OwnerName, + EnvRepoIsWiki+"="+isWiki, + EnvPusherName+"="+doer.Name, + EnvPusherID+"="+fmt.Sprintf("%d", doer.ID), + ProtectedBranchRepoID+"="+fmt.Sprintf("%d", repo.ID), + "SSH_ORIGINAL_COMMAND=gitea-internal", + ) + +} diff --git a/models/pull.go b/models/pull.go index 7382cbd126..6f37145d9b 100644 --- a/models/pull.go +++ b/models/pull.go @@ -418,22 +418,21 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false) }() + // Clone base repo. + tmpBasePath, err := CreateTemporaryPath("merge") + if err != nil { + return err + } + defer RemoveTemporaryPath(tmpBasePath) + headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name) - // Clone base repo. - tmpBasePath := path.Join(LocalCopyPath(), "merge-"+com.ToStr(time.Now().Nanosecond())+".git") - - if err := os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm); err != nil { - return fmt.Errorf("Failed to create dir %s: %v", tmpBasePath, err) - } - - defer os.RemoveAll(tmpBasePath) - - var stderr string - if _, stderr, err = process.GetManager().ExecTimeout(5*time.Minute, - fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath), - "git", "clone", "-s", "--no-checkout", "-b", pr.BaseBranch, baseGitRepo.Path, tmpBasePath); err != nil { - return fmt.Errorf("git clone: %s", stderr) + if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{ + Shared: true, + NoCheckout: true, + Branch: pr.BaseBranch, + }); err != nil { + return fmt.Errorf("git clone: %v", err) } remoteRepoName := "head_repo" @@ -456,14 +455,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil { return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err) } - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath), "git", "remote", "add", remoteRepoName, headRepoPath); err != nil { return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } // Fetch head branch - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath), "git", "fetch", remoteRepoName); err != nil { return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) @@ -487,14 +486,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err) } - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git config): %s", tmpBasePath), "git", "config", "--local", "core.sparseCheckout", "true"); err != nil { return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", stderr) } // Read base branch index - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git read-tree): %s", tmpBasePath), "git", "read-tree", "HEAD"); err != nil { return fmt.Errorf("git read-tree HEAD: %s", stderr) @@ -503,14 +502,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle // Merge commits. switch mergeStyle { case MergeStyleMerge: - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath), "git", "merge", "--no-ff", "--no-commit", trackingBranch); err != nil { return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr) } sig := doer.NewGitSig() - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message); err != nil { @@ -518,50 +517,50 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle } case MergeStyleRebase: // Checkout head branch - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), "git", "checkout", "-b", stagingBranch, trackingBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Rebase before merging - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), "git", "rebase", "-q", pr.BaseBranch); err != nil { return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } // Checkout base branch again - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), "git", "checkout", pr.BaseBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Merge fast forward - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), "git", "merge", "--ff-only", "-q", stagingBranch); err != nil { return fmt.Errorf("git merge --ff-only [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } case MergeStyleRebaseMerge: // Checkout head branch - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), "git", "checkout", "-b", stagingBranch, trackingBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Rebase before merging - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath), "git", "rebase", "-q", pr.BaseBranch); err != nil { return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } // Checkout base branch again - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), "git", "checkout", pr.BaseBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Prepare merge with commit - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), "git", "merge", "--no-ff", "--no-commit", "-q", stagingBranch); err != nil { return fmt.Errorf("git merge --no-ff [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) @@ -569,7 +568,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle // Set custom message and author and create merge commit sig := doer.NewGitSig() - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git commit): %s", tmpBasePath), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message); err != nil { @@ -578,13 +577,13 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle case MergeStyleSquash: // Merge with squash - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath), "git", "merge", "-q", "--squash", trackingBranch); err != nil { return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } sig := pr.Issue.Poster.NewGitSig() - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message); err != nil { @@ -594,10 +593,12 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle} } + env := PushingEnvironment(doer, pr.BaseRepo) + // Push back to upstream. - if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath, + if _, stderr, err := process.GetManager().ExecDirEnv(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath), - "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { + env, "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { return fmt.Errorf("git push: %s", stderr) } diff --git a/models/repo.go b/models/repo.go index 66c1bdbab1..2f87e2f514 100644 --- a/models/repo.go +++ b/models/repo.go @@ -518,7 +518,7 @@ func (repo *Repository) DeleteWiki() error { } func (repo *Repository) deleteWiki(e Engine) error { - wikiPaths := []string{repo.WikiPath(), repo.LocalWikiPath()} + wikiPaths := []string{repo.WikiPath()} for _, wikiPath := range wikiPaths { removeAllWithNotice(e, "Delete repository wiki", wikiPath) } @@ -749,56 +749,6 @@ func (repo *Repository) DescriptionHTML() template.HTML { return template.HTML(markup.Sanitize(string(desc))) } -// LocalCopyPath returns the local repository copy path. -func LocalCopyPath() string { - if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) { - return setting.Repository.Local.LocalCopyPath - } - return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath) -} - -// LocalCopyPath returns the local repository copy path for the given repo. -func (repo *Repository) LocalCopyPath() string { - return path.Join(LocalCopyPath(), com.ToStr(repo.ID)) -} - -// UpdateLocalCopyBranch pulls latest changes of given branch from repoPath to localPath. -// It creates a new clone if local copy does not exist. -// This function checks out target branch by default, it is safe to assume subsequent -// operations are operating against target branch when caller has confidence for no race condition. -func UpdateLocalCopyBranch(repoPath, localPath, branch string) error { - if !com.IsExist(localPath) { - if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{ - Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second, - Branch: branch, - }); err != nil { - return fmt.Errorf("git clone %s: %v", branch, err) - } - } else { - _, err := git.NewCommand("fetch", "origin").RunInDir(localPath) - if err != nil { - return fmt.Errorf("git fetch origin: %v", err) - } - if len(branch) > 0 { - if err := git.Checkout(localPath, git.CheckoutOptions{ - Branch: branch, - }); err != nil { - return fmt.Errorf("git checkout %s: %v", branch, err) - } - - if err := git.ResetHEAD(localPath, true, "origin/"+branch); err != nil { - return fmt.Errorf("git reset --hard origin/%s: %v", branch, err) - } - } - } - return nil -} - -// UpdateLocalCopyBranch makes sure local copy of repository in given branch is up-to-date. -func (repo *Repository) UpdateLocalCopyBranch(branch string) error { - return UpdateLocalCopyBranch(repo.RepoPath(), repo.LocalCopyPath(), branch) -} - // PatchPath returns corresponding patch file path of repository by given issue ID. func (repo *Repository) PatchPath(index int64) (string, error) { return repo.patchPath(x, index) @@ -1583,12 +1533,10 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { return fmt.Errorf("rename repository directory: %v", err) } - removeAllWithNotice(sess, "Delete repository local copy", repo.LocalCopyPath()) // Rename remote wiki repository to new path and delete local copy. wikiPath := WikiPath(owner.Name, repo.Name) if com.IsExist(wikiPath) { - removeAllWithNotice(sess, "Delete repository wiki local copy", repo.LocalWikiPath()) if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { return fmt.Errorf("rename repository wiki: %v", err) } @@ -1633,20 +1581,11 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error) return fmt.Errorf("rename repository directory: %v", err) } - localPath := repo.LocalCopyPath() - if com.IsExist(localPath) { - _, err := git.NewCommand("remote", "set-url", "origin", newRepoPath).RunInDir(localPath) - if err != nil { - return fmt.Errorf("git remote set-url origin %s: %v", newRepoPath, err) - } - } - wikiPath := repo.WikiPath() if com.IsExist(wikiPath) { if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil { return fmt.Errorf("rename repository wiki: %v", err) } - RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath()) } sess := x.NewSession() diff --git a/models/repo_branch.go b/models/repo_branch.go index 0958e23974..08c881fc24 100644 --- a/models/repo_branch.go +++ b/models/repo_branch.go @@ -7,86 +7,11 @@ package models import ( "fmt" - "time" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" - - "github.com/Unknwon/com" + "code.gitea.io/gitea/modules/log" ) -// discardLocalRepoBranchChanges discards local commits/changes of -// given branch to make sure it is even to remote branch. -func discardLocalRepoBranchChanges(localPath, branch string) error { - if !com.IsExist(localPath) { - return nil - } - // No need to check if nothing in the repository. - if !git.IsBranchExist(localPath, branch) { - return nil - } - - refName := "origin/" + branch - if err := git.ResetHEAD(localPath, true, refName); err != nil { - return fmt.Errorf("git reset --hard %s: %v", refName, err) - } - return nil -} - -// DiscardLocalRepoBranchChanges discards the local repository branch changes -func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error { - return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch) -} - -// checkoutNewBranch checks out to a new branch from the a branch name. -func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error { - if err := git.Checkout(localPath, git.CheckoutOptions{ - Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, - Branch: newBranch, - OldBranch: oldBranch, - }); err != nil { - return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err) - } - return nil -} - -// CheckoutNewBranch checks out a new branch -func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error { - return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch) -} - -// deleteLocalBranch deletes a branch from a local repo cache -// First checks out default branch to avoid trying to delete the currently checked out branch -func deleteLocalBranch(localPath, defaultBranch, deleteBranch string) error { - if !com.IsExist(localPath) { - return nil - } - - if !git.IsBranchExist(localPath, deleteBranch) { - return nil - } - - // Must NOT have branch currently checked out - // Checkout default branch first - if err := git.Checkout(localPath, git.CheckoutOptions{ - Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, - Branch: defaultBranch, - }); err != nil { - return fmt.Errorf("git checkout %s: %v", defaultBranch, err) - } - - cmd := git.NewCommand("branch") - cmd.AddArguments("-D") - cmd.AddArguments(deleteBranch) - _, err := cmd.RunInDir(localPath) - return err -} - -// DeleteLocalBranch deletes a branch from the local repo -func (repo *Repository) DeleteLocalBranch(branchName string) error { - return deleteLocalBranch(repo.LocalCopyPath(), repo.DefaultBranch, branchName) -} - // CanCreateBranch returns true if repository meets the requirements for creating new branches. func (repo *Repository) CanCreateBranch() bool { return !repo.IsMirror @@ -137,29 +62,44 @@ func (repo *Repository) CheckBranchName(name string) error { // CreateNewBranch creates a new repository branch func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) { - repoWorkingPool.CheckIn(com.ToStr(repo.ID)) - defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) - // Check if branch name can be used if err := repo.CheckBranchName(branchName); err != nil { return err } - localPath := repo.LocalCopyPath() - - if err = discardLocalRepoBranchChanges(localPath, oldBranchName); err != nil { - return fmt.Errorf("discardLocalRepoChanges: %v", err) - } else if err = repo.UpdateLocalCopyBranch(oldBranchName); err != nil { - return fmt.Errorf("UpdateLocalCopyBranch: %v", err) + if !git.IsBranchExist(repo.RepoPath(), oldBranchName) { + return fmt.Errorf("OldBranch: %s does not exist. Cannot create new branch from this", oldBranchName) } - if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil { - return fmt.Errorf("CreateNewBranch: %v", err) + basePath, err := CreateTemporaryPath("branch-maker") + if err != nil { + return err + } + defer RemoveTemporaryPath(basePath) + + if err := git.Clone(repo.RepoPath(), basePath, git.CloneRepoOptions{ + Bare: true, + Shared: true, + }); err != nil { + log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) + return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) } - if err = git.Push(localPath, git.PushOptions{ + gitRepo, err := git.OpenRepository(basePath) + if err != nil { + log.Error("Unable to open temporary repository: %s (%v)", basePath, err) + return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) + } + + if err = gitRepo.CreateBranch(branchName, oldBranchName); err != nil { + log.Error("Unable to create branch: %s from %s. (%v)", branchName, oldBranchName, err) + return fmt.Errorf("Unable to create branch: %s from %s. (%v)", branchName, oldBranchName, err) + } + + if err = git.Push(basePath, git.PushOptions{ Remote: "origin", Branch: branchName, + Env: PushingEnvironment(doer, repo), }); err != nil { return fmt.Errorf("Push: %v", err) } @@ -167,62 +107,41 @@ func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName st return nil } -// updateLocalCopyToCommit pulls latest changes of given commit from repoPath to localPath. -// It creates a new clone if local copy does not exist. -// This function checks out target commit by default, it is safe to assume subsequent -// operations are operating against target commit when caller has confidence for no race condition. -func updateLocalCopyToCommit(repoPath, localPath, commit string) error { - if !com.IsExist(localPath) { - if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{ - Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second, - }); err != nil { - return fmt.Errorf("git clone: %v", err) - } - } else { - _, err := git.NewCommand("fetch", "origin").RunInDir(localPath) - if err != nil { - return fmt.Errorf("git fetch origin: %v", err) - } - if err := git.ResetHEAD(localPath, true, "HEAD"); err != nil { - return fmt.Errorf("git reset --hard HEAD: %v", err) - } - } - if err := git.Checkout(localPath, git.CheckoutOptions{ - Branch: commit, - }); err != nil { - return fmt.Errorf("git checkout %s: %v", commit, err) - } - return nil -} - -// updateLocalCopyToCommit makes sure local copy of repository is at given commit. -func (repo *Repository) updateLocalCopyToCommit(commit string) error { - return updateLocalCopyToCommit(repo.RepoPath(), repo.LocalCopyPath(), commit) -} - // CreateNewBranchFromCommit creates a new repository branch func (repo *Repository) CreateNewBranchFromCommit(doer *User, commit, branchName string) (err error) { - repoWorkingPool.CheckIn(com.ToStr(repo.ID)) - defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) - // Check if branch name can be used if err := repo.CheckBranchName(branchName); err != nil { return err } + basePath, err := CreateTemporaryPath("branch-maker") + if err != nil { + return err + } + defer RemoveTemporaryPath(basePath) - localPath := repo.LocalCopyPath() - - if err = repo.updateLocalCopyToCommit(commit); err != nil { - return fmt.Errorf("UpdateLocalCopyBranch: %v", err) + if err := git.Clone(repo.RepoPath(), basePath, git.CloneRepoOptions{ + Bare: true, + Shared: true, + }); err != nil { + log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) + return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) } - if err = repo.CheckoutNewBranch(commit, branchName); err != nil { - return fmt.Errorf("CheckoutNewBranch: %v", err) + gitRepo, err := git.OpenRepository(basePath) + if err != nil { + log.Error("Unable to open temporary repository: %s (%v)", basePath, err) + return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) } - if err = git.Push(localPath, git.PushOptions{ + if err = gitRepo.CreateBranch(branchName, commit); err != nil { + log.Error("Unable to create branch: %s from %s. (%v)", branchName, commit, err) + return fmt.Errorf("Unable to create branch: %s from %s. (%v)", branchName, commit, err) + } + + if err = git.Push(basePath, git.PushOptions{ Remote: "origin", Branch: branchName, + Env: PushingEnvironment(doer, repo), }); err != nil { return fmt.Errorf("Push: %v", err) } diff --git a/models/repo_test.go b/models/repo_test.go index a5b8cce9b9..eee3997868 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -5,11 +5,9 @@ package models import ( - "path" "testing" "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/setting" "github.com/Unknwon/com" "github.com/stretchr/testify/assert" @@ -138,25 +136,6 @@ func TestRepoAPIURL(t *testing.T) { assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL()) } -func TestRepoLocalCopyPath(t *testing.T) { - assert.NoError(t, PrepareTestDatabase()) - - repo, err := GetRepositoryByID(10) - assert.NoError(t, err) - assert.NotNil(t, repo) - - // test default - repoID := com.ToStr(repo.ID) - expected := path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath, repoID) - assert.Equal(t, expected, repo.LocalCopyPath()) - - // test absolute setting - tempPath := "/tmp/gitea/local-copy-path" - expected = path.Join(tempPath, repoID) - setting.Repository.Local.LocalCopyPath = tempPath - assert.Equal(t, expected, repo.LocalCopyPath()) -} - func TestTransferOwnership(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) diff --git a/models/user.go b/models/user.go index 0c445b5afd..90ca189ef0 100644 --- a/models/user.go +++ b/models/user.go @@ -943,17 +943,6 @@ func ChangeUserName(u *User, newUserName string) (err error) { return fmt.Errorf("ChangeUsernameInPullRequests: %v", err) } - // Delete all local copies of repository wiki that user owns. - if err = x.BufferSize(setting.IterateBufferSize). - Where("owner_id=?", u.ID). - Iterate(new(Repository), func(idx int, bean interface{}) error { - repo := bean.(*Repository) - RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath()) - return nil - }); err != nil { - return fmt.Errorf("Delete repository wiki local copy: %v", err) - } - // Do not fail if directory does not exist if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { return fmt.Errorf("Rename user directory: %v", err) diff --git a/models/wiki.go b/models/wiki.go index 0f5cdc20bd..bcf97c0765 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -6,15 +6,13 @@ package models import ( "fmt" - "io/ioutil" "net/url" "os" - "path" "path/filepath" "strings" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/sync" "github.com/Unknwon/com" @@ -89,34 +87,6 @@ func (repo *Repository) InitWiki() error { return nil } -// LocalWikiPath returns the local wiki repository copy path. -func LocalWikiPath() string { - if filepath.IsAbs(setting.Repository.Local.LocalWikiPath) { - return setting.Repository.Local.LocalWikiPath - } - return path.Join(setting.AppDataPath, setting.Repository.Local.LocalWikiPath) -} - -// LocalWikiPath returns the path to the local wiki repository (?). -func (repo *Repository) LocalWikiPath() string { - return path.Join(LocalWikiPath(), com.ToStr(repo.ID)) -} - -// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date. -func (repo *Repository) updateLocalWiki() error { - // Don't pass branch name here because it fails to clone and - // checkout to a specific branch when wiki is an empty repository. - var branch = "" - if com.IsExist(repo.LocalWikiPath()) { - branch = "master" - } - return UpdateLocalCopyBranch(repo.WikiPath(), repo.LocalWikiPath(), branch) -} - -func discardLocalWikiChanges(localPath string) error { - return discardLocalRepoBranchChanges(localPath, "master") -} - // nameAllowed checks if a wiki name is allowed func nameAllowed(name string) error { for _, reservedName := range reservedWikiNames { @@ -132,7 +102,6 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con if err = nameAllowed(newWikiName); err != nil { return err } - wikiWorkingPool.CheckIn(com.ToStr(repo.ID)) defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID)) @@ -140,54 +109,113 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con return fmt.Errorf("InitWiki: %v", err) } - localPath := repo.LocalWikiPath() - if err = discardLocalWikiChanges(localPath); err != nil { - return fmt.Errorf("discardLocalWikiChanges: %v", err) - } else if err = repo.updateLocalWiki(); err != nil { - return fmt.Errorf("UpdateLocalWiki: %v", err) + hasMasterBranch := git.IsBranchExist(repo.WikiPath(), "master") + + basePath, err := CreateTemporaryPath("update-wiki") + if err != nil { + return err + } + defer RemoveTemporaryPath(basePath) + + cloneOpts := git.CloneRepoOptions{ + Bare: true, + Shared: true, } - newWikiPath := path.Join(localPath, WikiNameToFilename(newWikiName)) + if hasMasterBranch { + cloneOpts.Branch = "master" + } - // If not a new file, show perform update not create. + if err := git.Clone(repo.WikiPath(), basePath, cloneOpts); err != nil { + log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) + return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) + } + + gitRepo, err := git.OpenRepository(basePath) + if err != nil { + log.Error("Unable to open temporary repository: %s (%v)", basePath, err) + return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) + } + + if hasMasterBranch { + if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil { + log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err) + return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err) + } + } + + newWikiPath := WikiNameToFilename(newWikiName) if isNew { - if com.IsExist(newWikiPath) { - return ErrWikiAlreadyExist{newWikiPath} + filesInIndex, err := gitRepo.LsFiles(newWikiPath) + if err != nil { + log.Error("%v", err) + return err + } + for _, file := range filesInIndex { + if file == newWikiPath { + return ErrWikiAlreadyExist{newWikiPath} + } } } else { - oldWikiPath := path.Join(localPath, WikiNameToFilename(oldWikiName)) - if err := os.Remove(oldWikiPath); err != nil { - return fmt.Errorf("Failed to remove %s: %v", oldWikiPath, err) + oldWikiPath := WikiNameToFilename(oldWikiName) + filesInIndex, err := gitRepo.LsFiles(oldWikiPath) + if err != nil { + log.Error("%v", err) + return err + } + found := false + for _, file := range filesInIndex { + if file == oldWikiPath { + found = true + break + } + } + if found { + err := gitRepo.RemoveFilesFromIndex(oldWikiPath) + if err != nil { + log.Error("%v", err) + return err + } } } - // SECURITY: if new file is a symlink to non-exist critical file, - // attack content can be written to the target file (e.g. authorized_keys2) - // as a new page operation. - // So we want to make sure the symlink is removed before write anything. - // The new file we created will be in normal text format. - if err = os.RemoveAll(newWikiPath); err != nil { + // FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here + + objectHash, err := gitRepo.HashObject(strings.NewReader(content)) + if err != nil { + log.Error("%v", err) return err } - if err = ioutil.WriteFile(newWikiPath, []byte(content), 0666); err != nil { - return fmt.Errorf("WriteFile: %v", err) + if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil { + log.Error("%v", err) + return err } - if len(message) == 0 { - message = "Update page '" + newWikiName + "'" + tree, err := gitRepo.WriteTree() + if err != nil { + log.Error("%v", err) + return err } - if err = git.AddChanges(localPath, true); err != nil { - return fmt.Errorf("AddChanges: %v", err) - } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ - Committer: doer.NewGitSig(), - Message: message, - }); err != nil { - return fmt.Errorf("CommitChanges: %v", err) - } else if err = git.Push(localPath, git.PushOptions{ + + commitTreeOpts := git.CommitTreeOpts{ + Message: message, + } + if hasMasterBranch { + commitTreeOpts.Parents = []string{"HEAD"} + } + commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, commitTreeOpts) + if err != nil { + log.Error("%v", err) + return err + } + + if err := git.Push(basePath, git.PushOptions{ Remote: "origin", - Branch: "master", + Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"), + Env: PushingEnvironment(doer, repo), }); err != nil { + log.Error("%v", err) return fmt.Errorf("Push: %v", err) } @@ -210,31 +238,74 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error) wikiWorkingPool.CheckIn(com.ToStr(repo.ID)) defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID)) - localPath := repo.LocalWikiPath() - if err = discardLocalWikiChanges(localPath); err != nil { - return fmt.Errorf("discardLocalWikiChanges: %v", err) - } else if err = repo.updateLocalWiki(); err != nil { - return fmt.Errorf("UpdateLocalWiki: %v", err) + if err = repo.InitWiki(); err != nil { + return fmt.Errorf("InitWiki: %v", err) } - filename := path.Join(localPath, WikiNameToFilename(wikiName)) + basePath, err := CreateTemporaryPath("update-wiki") + if err != nil { + return err + } + defer RemoveTemporaryPath(basePath) - if err := os.Remove(filename); err != nil { - return fmt.Errorf("Failed to remove %s: %v", filename, err) + if err := git.Clone(repo.WikiPath(), basePath, git.CloneRepoOptions{ + Bare: true, + Shared: true, + Branch: "master", + }); err != nil { + log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) + return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) } + gitRepo, err := git.OpenRepository(basePath) + if err != nil { + log.Error("Unable to open temporary repository: %s (%v)", basePath, err) + return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) + } + + if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil { + log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err) + return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err) + } + + wikiPath := WikiNameToFilename(wikiName) + filesInIndex, err := gitRepo.LsFiles(wikiPath) + found := false + for _, file := range filesInIndex { + if file == wikiPath { + found = true + break + } + } + if found { + err := gitRepo.RemoveFilesFromIndex(wikiPath) + if err != nil { + return err + } + } else { + return os.ErrNotExist + } + + // FIXME: The wiki doesn't have lfs support at present - if this changes need to check attributes here + + tree, err := gitRepo.WriteTree() + if err != nil { + return err + } message := "Delete page '" + wikiName + "'" - if err = git.AddChanges(localPath, true); err != nil { - return fmt.Errorf("AddChanges: %v", err) - } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ - Committer: doer.NewGitSig(), - Message: message, - }); err != nil { - return fmt.Errorf("CommitChanges: %v", err) - } else if err = git.Push(localPath, git.PushOptions{ + commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, git.CommitTreeOpts{ + Message: message, + Parents: []string{"HEAD"}, + }) + if err != nil { + return err + } + + if err := git.Push(basePath, git.PushOptions{ Remote: "origin", - Branch: "master", + Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"), + Env: PushingEnvironment(doer, repo), }); err != nil { return fmt.Errorf("Push: %v", err) } diff --git a/models/wiki_test.go b/models/wiki_test.go index 5280b3ea01..991a3d95b9 100644 --- a/models/wiki_test.go +++ b/models/wiki_test.go @@ -5,13 +5,12 @@ package models import ( - "path" "path/filepath" "testing" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - "github.com/Unknwon/com" "github.com/stretchr/testify/assert" ) @@ -145,13 +144,6 @@ func TestRepository_InitWiki(t *testing.T) { assert.True(t, repo2.HasWiki()) } -func TestRepository_LocalWikiPath(t *testing.T) { - PrepareTestEnv(t) - repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - expected := filepath.Join(setting.AppDataPath, setting.Repository.Local.LocalWikiPath, "1") - assert.Equal(t, expected, repo.LocalWikiPath()) -} - func TestRepository_AddWikiPage(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) const wikiContent = "This is the wiki content" @@ -166,8 +158,15 @@ func TestRepository_AddWikiPage(t *testing.T) { t.Run("test wiki exist: "+wikiName, func(t *testing.T) { t.Parallel() assert.NoError(t, repo.AddWikiPage(doer, wikiName, wikiContent, commitMsg)) - expectedPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(wikiName)) - assert.True(t, com.IsExist(expectedPath)) + // Now need to show that the page has been added: + gitRepo, err := git.OpenRepository(repo.WikiPath()) + assert.NoError(t, err) + masterTree, err := gitRepo.GetTree("master") + assert.NoError(t, err) + wikiPath := WikiNameToFilename(wikiName) + entry, err := masterTree.GetTreeEntryByPath(wikiPath) + assert.NoError(t, err) + assert.Equal(t, wikiPath, entry.Name(), "%s not addded correctly", wikiName) }) } @@ -200,11 +199,20 @@ func TestRepository_EditWikiPage(t *testing.T) { } { PrepareTestEnv(t) assert.NoError(t, repo.EditWikiPage(doer, "Home", newWikiName, newWikiContent, commitMsg)) - newPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(newWikiName)) - assert.True(t, com.IsExist(newPath)) + + // Now need to show that the page has been added: + gitRepo, err := git.OpenRepository(repo.WikiPath()) + assert.NoError(t, err) + masterTree, err := gitRepo.GetTree("master") + assert.NoError(t, err) + wikiPath := WikiNameToFilename(newWikiName) + entry, err := masterTree.GetTreeEntryByPath(wikiPath) + assert.NoError(t, err) + assert.Equal(t, wikiPath, entry.Name(), "%s not editted correctly", newWikiName) + if newWikiName != "Home" { - oldPath := path.Join(repo.LocalWikiPath(), "Home.md") - assert.False(t, com.IsExist(oldPath)) + _, err := masterTree.GetTreeEntryByPath("Home.md") + assert.Error(t, err) } } } @@ -214,6 +222,13 @@ func TestRepository_DeleteWikiPage(t *testing.T) { repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) assert.NoError(t, repo.DeleteWikiPage(doer, "Home")) - wikiPath := path.Join(repo.LocalWikiPath(), "Home.md") - assert.False(t, com.IsExist(wikiPath)) + + // Now need to show that the page has been added: + gitRepo, err := git.OpenRepository(repo.WikiPath()) + assert.NoError(t, err) + masterTree, err := gitRepo.GetTree("master") + assert.NoError(t, err) + wikiPath := WikiNameToFilename("Home") + _, err = masterTree.GetTreeEntryByPath(wikiPath) + assert.Error(t, err) } diff --git a/modules/git/command.go b/modules/git/command.go index 3602717702..d6221ce268 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -52,9 +52,15 @@ func (c *Command) AddArguments(args ...string) *Command { return c } -// RunInDirTimeoutPipeline executes the command in given directory with given timeout, +// RunInDirTimeoutEnvPipeline executes the command in given directory with given timeout, // it pipes stdout and stderr to given io.Writer. -func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error { +func (c *Command) RunInDirTimeoutEnvPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer) error { + return c.RunInDirTimeoutEnvFullPipeline(env, timeout, dir, stdout, stderr, nil) +} + +// RunInDirTimeoutEnvFullPipeline executes the command in given directory with given timeout, +// it pipes stdout and stderr to given io.Writer and passes in an io.Reader as stdin. +func (c *Command) RunInDirTimeoutEnvFullPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error { if timeout == -1 { timeout = DefaultCommandExecutionTimeout } @@ -69,9 +75,11 @@ func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, std defer cancel() cmd := exec.CommandContext(ctx, c.name, c.args...) + cmd.Env = env cmd.Dir = dir cmd.Stdout = stdout cmd.Stderr = stderr + cmd.Stdin = stdin if err := cmd.Start(); err != nil { return err } @@ -83,12 +91,30 @@ func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, std return ctx.Err() } +// RunInDirTimeoutPipeline executes the command in given directory with given timeout, +// it pipes stdout and stderr to given io.Writer. +func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error { + return c.RunInDirTimeoutEnvPipeline(nil, timeout, dir, stdout, stderr) +} + +// RunInDirTimeoutFullPipeline executes the command in given directory with given timeout, +// it pipes stdout and stderr to given io.Writer, and stdin from the given io.Reader +func (c *Command) RunInDirTimeoutFullPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error { + return c.RunInDirTimeoutEnvFullPipeline(nil, timeout, dir, stdout, stderr, stdin) +} + // RunInDirTimeout executes the command in given directory with given timeout, // and returns stdout in []byte and error (combined with stderr). func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) { + return c.RunInDirTimeoutEnv(nil, timeout, dir) +} + +// RunInDirTimeoutEnv executes the command in given directory with given timeout, +// and returns stdout in []byte and error (combined with stderr). +func (c *Command) RunInDirTimeoutEnv(env []string, timeout time.Duration, dir string) ([]byte, error) { stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) - if err := c.RunInDirTimeoutPipeline(timeout, dir, stdout, stderr); err != nil { + if err := c.RunInDirTimeoutEnvPipeline(env, timeout, dir, stdout, stderr); err != nil { return nil, concatenateError(err, stderr.String()) } @@ -101,7 +127,13 @@ func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, er // RunInDirPipeline executes the command in given directory, // it pipes stdout and stderr to given io.Writer. func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error { - return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr) + return c.RunInDirFullPipeline(dir, stdout, stderr, nil) +} + +// RunInDirFullPipeline executes the command in given directory, +// it pipes stdout and stderr to given io.Writer. +func (c *Command) RunInDirFullPipeline(dir string, stdout, stderr io.Writer, stdin io.Reader) error { + return c.RunInDirTimeoutFullPipeline(-1, dir, stdout, stderr, stdin) } // RunInDirBytes executes the command in given directory @@ -113,7 +145,13 @@ func (c *Command) RunInDirBytes(dir string) ([]byte, error) { // RunInDir executes the command in given directory // and returns stdout in string and error (combined with stderr). func (c *Command) RunInDir(dir string) (string, error) { - stdout, err := c.RunInDirTimeout(-1, dir) + return c.RunInDirWithEnv(dir, nil) +} + +// RunInDirWithEnv executes the command in given directory +// and returns stdout in string and error (combined with stderr). +func (c *Command) RunInDirWithEnv(dir string, env []string) (string, error) { + stdout, err := c.RunInDirTimeoutEnv(env, -1, dir) if err != nil { return "", err } diff --git a/modules/git/repo.go b/modules/git/repo.go index 8355f8811f..4be3164130 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -109,11 +109,13 @@ func OpenRepository(repoPath string) (*Repository, error) { // CloneRepoOptions options when clone a repository type CloneRepoOptions struct { - Timeout time.Duration - Mirror bool - Bare bool - Quiet bool - Branch string + Timeout time.Duration + Mirror bool + Bare bool + Quiet bool + Branch string + Shared bool + NoCheckout bool } // Clone clones original repository to target path. @@ -133,10 +135,17 @@ func Clone(from, to string, opts CloneRepoOptions) (err error) { if opts.Quiet { cmd.AddArguments("--quiet") } + if opts.Shared { + cmd.AddArguments("-s") + } + if opts.NoCheckout { + cmd.AddArguments("--no-checkout") + } + if len(opts.Branch) > 0 { cmd.AddArguments("-b", opts.Branch) } - cmd.AddArguments(from, to) + cmd.AddArguments("--", from, to) if opts.Timeout <= 0 { opts.Timeout = -1 @@ -181,6 +190,7 @@ type PushOptions struct { Remote string Branch string Force bool + Env []string } // Push pushs local commits to given remote branch. @@ -190,7 +200,7 @@ func Push(repoPath string, opts PushOptions) error { cmd.AddArguments("-f") } cmd.AddArguments(opts.Remote, opts.Branch) - _, err := cmd.RunInDir(repoPath) + _, err := cmd.RunInDirWithEnv(repoPath, opts.Env) return err } diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 83689ee9dc..116bdbee82 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -17,7 +17,7 @@ const BranchPrefix = "refs/heads/" // IsReferenceExist returns true if given reference exists in the repository. func IsReferenceExist(repoPath, name string) bool { - _, err := NewCommand("show-ref", "--verify", name).RunInDir(repoPath) + _, err := NewCommand("show-ref", "--verify", "--", name).RunInDir(repoPath) return err == nil } @@ -145,9 +145,9 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro } // CreateBranch create a new branch -func (repo *Repository) CreateBranch(branch, newBranch string) error { +func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { cmd := NewCommand("branch") - cmd.AddArguments(branch, newBranch) + cmd.AddArguments("--", branch, oldbranchOrCommit) _, err := cmd.RunInDir(repo.Path) diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go new file mode 100644 index 0000000000..4d26563a91 --- /dev/null +++ b/modules/git/repo_index.go @@ -0,0 +1,98 @@ +// Copyright 2019 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 git + +import ( + "bytes" + "strings" +) + +// ReadTreeToIndex reads a treeish to the index +func (repo *Repository) ReadTreeToIndex(treeish string) error { + if len(treeish) != 40 { + res, err := NewCommand("rev-parse", treeish).RunInDir(repo.Path) + if err != nil { + return err + } + if len(res) > 0 { + treeish = res[:len(res)-1] + } + } + id, err := NewIDFromString(treeish) + if err != nil { + return err + } + return repo.readTreeToIndex(id) +} + +func (repo *Repository) readTreeToIndex(id SHA1) error { + _, err := NewCommand("read-tree", id.String()).RunInDir(repo.Path) + if err != nil { + return err + } + return nil +} + +// EmptyIndex empties the index +func (repo *Repository) EmptyIndex() error { + _, err := NewCommand("read-tree", "--empty").RunInDir(repo.Path) + return err +} + +// LsFiles checks if the given filenames are in the index +func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { + cmd := NewCommand("ls-files", "-z", "--") + for _, arg := range filenames { + if arg != "" { + cmd.AddArguments(arg) + } + } + res, err := cmd.RunInDirBytes(repo.Path) + if err != nil { + return nil, err + } + filelist := make([]string, 0, len(filenames)) + for _, line := range bytes.Split(res, []byte{'\000'}) { + filelist = append(filelist, string(line)) + } + + return filelist, err +} + +// RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present. +func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { + cmd := NewCommand("update-index", "--remove", "-z", "--index-info") + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + buffer := new(bytes.Buffer) + for _, file := range filenames { + if file != "" { + buffer.WriteString("0 0000000000000000000000000000000000000000\t") + buffer.WriteString(file) + buffer.WriteByte('\000') + } + } + return cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, bytes.NewReader(buffer.Bytes())) +} + +// AddObjectToIndex adds the provided object hash to the index at the provided filename +func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error { + cmd := NewCommand("update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename) + _, err := cmd.RunInDir(repo.Path) + return err +} + +// WriteTree writes the current index as a tree to the object db and returns its hash +func (repo *Repository) WriteTree() (*Tree, error) { + res, err := NewCommand("write-tree").RunInDir(repo.Path) + if err != nil { + return nil, err + } + id, err := NewIDFromString(strings.TrimSpace(res)) + if err != nil { + return nil, err + } + return NewTree(repo, id), nil +} diff --git a/modules/git/repo_object.go b/modules/git/repo_object.go index 3be8400d22..67060e30b0 100644 --- a/modules/git/repo_object.go +++ b/modules/git/repo_object.go @@ -4,6 +4,12 @@ package git +import ( + "bytes" + "io" + "strings" +) + // ObjectType git object type type ObjectType string @@ -17,3 +23,24 @@ const ( // ObjectTag tag object type ObjectTag ObjectType = "tag" ) + +// HashObject takes a reader and returns SHA1 hash for that reader +func (repo *Repository) HashObject(reader io.Reader) (SHA1, error) { + idStr, err := repo.hashObject(reader) + if err != nil { + return SHA1{}, err + } + return NewIDFromString(idStr) +} + +func (repo *Repository) hashObject(reader io.Reader) (string, error) { + cmd := NewCommand("hash-object", "-w", "--stdin") + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + err := cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, reader) + + if err != nil { + return "", err + } + return strings.TrimSpace(stdout.String()), nil +} diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go index 8bb7291744..7d32d3685c 100644 --- a/modules/git/repo_tree.go +++ b/modules/git/repo_tree.go @@ -6,6 +6,11 @@ package git import ( + "fmt" + "os" + "strings" + "time" + "gopkg.in/src-d/go-git.v4/plumbing" ) @@ -47,3 +52,48 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) { treeObject.ResolvedID = resolvedID return treeObject, nil } + +// CommitTreeOpts represents the possible options to CommitTree +type CommitTreeOpts struct { + Parents []string + Message string + KeyID string + NoGPGSign bool +} + +// CommitTree creates a commit from a given tree id for the user with provided message +func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) { + commitTimeStr := time.Now().Format(time.UnixDate) + + // Because this may call hooks we should pass in the environment + env := append(os.Environ(), + "GIT_AUTHOR_NAME="+sig.Name, + "GIT_AUTHOR_EMAIL="+sig.Email, + "GIT_AUTHOR_DATE="+commitTimeStr, + "GIT_COMMITTER_NAME="+sig.Name, + "GIT_COMMITTER_EMAIL="+sig.Email, + "GIT_COMMITTER_DATE="+commitTimeStr, + ) + cmd := NewCommand("commit-tree", tree.ID.String()) + + for _, parent := range opts.Parents { + cmd.AddArguments("-p", parent) + } + + cmd.AddArguments("-m", opts.Message) + + if opts.KeyID != "" { + cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID)) + } + + if opts.NoGPGSign { + cmd.AddArguments("--no-gpg-sign") + } + + res, err := cmd.RunInDirWithEnv(repo.Path, env) + + if err != nil { + return SHA1{}, err + } + return NewIDFromString(strings.TrimSpace(res)) +} diff --git a/modules/repofiles/temp_repo.go b/modules/repofiles/temp_repo.go index 46e03f5650..ec35628676 100644 --- a/modules/repofiles/temp_repo.go +++ b/modules/repofiles/temp_repo.go @@ -11,17 +11,15 @@ import ( "io" "os" "os/exec" - "path" "regexp" "strings" "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" - - "github.com/Unknwon/com" ) // TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone @@ -33,13 +31,9 @@ type TemporaryUploadRepository struct { // NewTemporaryUploadRepository creates a new temporary upload repository func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepository, error) { - timeStr := com.ToStr(time.Now().Nanosecond()) // SHOULD USE SOMETHING UNIQUE - basePath := path.Join(models.LocalCopyPath(), "upload-"+timeStr+".git") - if err := os.MkdirAll(path.Dir(basePath), os.ModePerm); err != nil { - return nil, fmt.Errorf("failed to create dir %s: %v", basePath, err) - } - if repo.RepoPath() == "" { - return nil, fmt.Errorf("no path to repository on system") + basePath, err := models.CreateTemporaryPath("upload") + if err != nil { + return nil, err } t := &TemporaryUploadRepository{repo: repo, basePath: basePath} return t, nil @@ -47,8 +41,8 @@ func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepo // Close the repository cleaning up all files func (t *TemporaryUploadRepository) Close() { - if _, err := os.Stat(t.basePath); !os.IsNotExist(err) { - os.RemoveAll(t.basePath) + if err := models.RemoveTemporaryPath(t.basePath); err != nil { + log.Error("Failed to remove temporary path %s: %v", t.basePath, err) } } @@ -282,27 +276,8 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t // Push the provided commitHash to the repository branch by the provided user func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error { - isWiki := "false" - if strings.HasSuffix(t.repo.Name, ".wiki") { - isWiki = "true" - } - - sig := doer.NewGitSig() - - // FIXME: Should we add SSH_ORIGINAL_COMMAND to this // Because calls hooks we need to pass in the environment - env := append(os.Environ(), - "GIT_AUTHOR_NAME="+sig.Name, - "GIT_AUTHOR_EMAIL="+sig.Email, - "GIT_COMMITTER_NAME="+sig.Name, - "GIT_COMMITTER_EMAIL="+sig.Email, - models.EnvRepoName+"="+t.repo.Name, - models.EnvRepoUsername+"="+t.repo.OwnerName, - models.EnvRepoIsWiki+"="+isWiki, - models.EnvPusherName+"="+doer.Name, - models.EnvPusherID+"="+fmt.Sprintf("%d", doer.ID), - models.ProtectedBranchRepoID+"="+fmt.Sprintf("%d", t.repo.ID), - ) + env := models.PushingEnvironment(doer, t.repo) if _, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute, t.basePath, diff --git a/modules/repofiles/update_test.go b/modules/repofiles/update_test.go deleted file mode 100644 index a3a0b0a420..0000000000 --- a/modules/repofiles/update_test.go +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2019 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 repofiles - -import ( - "testing" - "time" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/test" - - "github.com/stretchr/testify/assert" -) - -func getCreateRepoFileOptions(repo *models.Repository) *UpdateRepoFileOptions { - return &UpdateRepoFileOptions{ - OldBranch: repo.DefaultBranch, - NewBranch: repo.DefaultBranch, - TreePath: "new/file.txt", - Message: "Creates new/file.txt", - Content: "This is a NEW file", - IsNewFile: true, - Author: nil, - Committer: nil, - } -} - -func getUpdateRepoFileOptions(repo *models.Repository) *UpdateRepoFileOptions { - return &UpdateRepoFileOptions{ - OldBranch: repo.DefaultBranch, - NewBranch: repo.DefaultBranch, - TreePath: "README.md", - Message: "Updates README.md", - SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", - Content: "This is UPDATED content for the README file", - IsNewFile: false, - Author: nil, - Committer: nil, - } -} - -func getExpectedFileResponseForCreate(commitID string) *api.FileResponse { - return &api.FileResponse{ - Content: &api.FileContentResponse{ - Name: "file.txt", - Path: "new/file.txt", - SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", - Size: 18, - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/new/file.txt", - HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/new/file.txt", - GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", - DownloadURL: "https://try.gitea.io/user2/repo1/raw/branch/master/new/file.txt", - Type: "blob", - Links: &api.FileLinksResponse{ - Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/new/file.txt", - GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", - HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/new/file.txt", - }, - }, - Commit: &api.FileCommitResponse{ - CommitMeta: api.CommitMeta{ - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/" + commitID, - SHA: commitID, - }, - HTMLURL: "https://try.gitea.io/user2/repo1/commit/" + commitID, - Author: &api.CommitUser{ - Identity: api.Identity{ - Name: "User Two", - Email: "user2@", - }, - Date: time.Now().UTC().Format(time.RFC3339), - }, - Committer: &api.CommitUser{ - Identity: api.Identity{ - Name: "User Two", - Email: "user2@", - }, - Date: time.Now().UTC().Format(time.RFC3339), - }, - Parents: []*api.CommitMeta{ - { - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", - SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", - }, - }, - Message: "Updates README.md\n", - Tree: &api.CommitMeta{ - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", - SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc", - }, - }, - Verification: &api.PayloadCommitVerification{ - Verified: false, - Reason: "unsigned", - Signature: "", - Payload: "", - }, - } -} - -func getExpectedFileResponseForUpdate(commitID string) *api.FileResponse { - return &api.FileResponse{ - Content: &api.FileContentResponse{ - Name: "README.md", - Path: "README.md", - SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", - Size: 43, - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md", - HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md", - GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", - DownloadURL: "https://try.gitea.io/user2/repo1/raw/branch/master/README.md", - Type: "blob", - Links: &api.FileLinksResponse{ - Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md", - GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", - HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md", - }, - }, - Commit: &api.FileCommitResponse{ - CommitMeta: api.CommitMeta{ - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/" + commitID, - SHA: commitID, - }, - HTMLURL: "https://try.gitea.io/user2/repo1/commit/" + commitID, - Author: &api.CommitUser{ - Identity: api.Identity{ - Name: "User Two", - Email: "user2@", - }, - Date: time.Now().UTC().Format(time.RFC3339), - }, - Committer: &api.CommitUser{ - Identity: api.Identity{ - Name: "User Two", - Email: "user2@", - }, - Date: time.Now().UTC().Format(time.RFC3339), - }, - Parents: []*api.CommitMeta{ - { - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", - SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", - }, - }, - Message: "Updates README.md\n", - Tree: &api.CommitMeta{ - URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", - SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc", - }, - }, - Verification: &api.PayloadCommitVerification{ - Verified: false, - Reason: "unsigned", - Signature: "", - Payload: "", - }, - } -} - -func TestCreateOrUpdateRepoFileForCreate(t *testing.T) { - // setup - models.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - repo := ctx.Repo.Repository - doer := ctx.User - opts := getCreateRepoFileOptions(repo) - - // test - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - - // asserts - assert.Nil(t, err) - gitRepo, _ := git.OpenRepository(repo.RepoPath()) - commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForCreate(commitID) - assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) -} - -func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) { - // setup - models.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - repo := ctx.Repo.Repository - doer := ctx.User - opts := getUpdateRepoFileOptions(repo) - - // test - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - - // asserts - assert.Nil(t, err) - gitRepo, _ := git.OpenRepository(repo.RepoPath()) - commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForUpdate(commitID) - assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) -} - -func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { - // setup - models.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - repo := ctx.Repo.Repository - doer := ctx.User - opts := getUpdateRepoFileOptions(repo) - suffix := "_new" - opts.FromTreePath = "README.md" - opts.TreePath = "README.md" + suffix // new file name, README.md_new - - // test - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - - // asserts - assert.Nil(t, err) - gitRepo, _ := git.OpenRepository(repo.RepoPath()) - commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForUpdate(commit.ID.String()) - // assert that the old file no longer exists in the last commit of the branch - fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath) - toEntry, err := commit.GetTreeEntryByPath(opts.TreePath) - assert.Nil(t, fromEntry) // Should no longer exist here - assert.NotNil(t, toEntry) // Should exist here - // assert SHA has remained the same but paths use the new file name - assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA) - assert.EqualValues(t, expectedFileResponse.Content.Name+suffix, fileResponse.Content.Name) - assert.EqualValues(t, expectedFileResponse.Content.Path+suffix, fileResponse.Content.Path) - assert.EqualValues(t, expectedFileResponse.Content.URL+suffix, fileResponse.Content.URL) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) -} - -// Test opts with branch names removed, should get same results as above test -func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) { - // setup - models.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - repo := ctx.Repo.Repository - doer := ctx.User - opts := getUpdateRepoFileOptions(repo) - opts.OldBranch = "" - opts.NewBranch = "" - - // test - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - - // asserts - assert.Nil(t, err) - gitRepo, _ := git.OpenRepository(repo.RepoPath()) - commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch) - expectedFileResponse := getExpectedFileResponseForUpdate(commitID) - assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) -} - -func TestCreateOrUpdateRepoFileErrors(t *testing.T) { - // setup - models.PrepareTestEnv(t) - ctx := test.MockContext(t, "user2/repo1") - ctx.SetParams(":id", "1") - test.LoadRepo(t, ctx, 1) - test.LoadRepoCommit(t, ctx) - test.LoadUser(t, ctx, 2) - test.LoadGitRepo(t, ctx) - repo := ctx.Repo.Repository - doer := ctx.User - - t.Run("bad branch", func(t *testing.T) { - opts := getUpdateRepoFileOptions(repo) - opts.OldBranch = "bad_branch" - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - assert.Error(t, err) - assert.Nil(t, fileResponse) - expectedError := "branch does not exist [name: " + opts.OldBranch + "]" - assert.EqualError(t, err, expectedError) - }) - - t.Run("bad SHA", func(t *testing.T) { - opts := getUpdateRepoFileOptions(repo) - origSHA := opts.SHA - opts.SHA = "bad_sha" - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - assert.Nil(t, fileResponse) - assert.Error(t, err) - expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]" - assert.EqualError(t, err, expectedError) - }) - - t.Run("new branch already exists", func(t *testing.T) { - opts := getUpdateRepoFileOptions(repo) - opts.NewBranch = "develop" - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - assert.Nil(t, fileResponse) - assert.Error(t, err) - expectedError := "branch already exists [name: " + opts.NewBranch + "]" - assert.EqualError(t, err, expectedError) - }) - - t.Run("treePath is empty:", func(t *testing.T) { - opts := getUpdateRepoFileOptions(repo) - opts.TreePath = "" - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - assert.Nil(t, fileResponse) - assert.Error(t, err) - expectedError := "path contains a malformed path component [path: ]" - assert.EqualError(t, err, expectedError) - }) - - t.Run("treePath is a git directory:", func(t *testing.T) { - opts := getUpdateRepoFileOptions(repo) - opts.TreePath = ".git" - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - assert.Nil(t, fileResponse) - assert.Error(t, err) - expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]" - assert.EqualError(t, err, expectedError) - }) - - t.Run("create file that already exists", func(t *testing.T) { - opts := getCreateRepoFileOptions(repo) - opts.TreePath = "README.md" //already exists - fileResponse, err := CreateOrUpdateRepoFile(repo, doer, opts) - assert.Nil(t, fileResponse) - assert.Error(t, err) - expectedError := "repository file already exists [path: " + opts.TreePath + "]" - assert.EqualError(t, err, expectedError) - }) -} diff --git a/modules/setting/repository.go b/modules/setting/repository.go index f47661efdf..98e3d6e826 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -53,7 +53,6 @@ var ( // Repository local settings Local struct { LocalCopyPath string - LocalWikiPath string } `ini:"-"` // Pull request settings @@ -105,10 +104,8 @@ var ( // Repository local settings Local: struct { LocalCopyPath string - LocalWikiPath string }{ LocalCopyPath: "tmp/local-repo", - LocalWikiPath: "tmp/local-wiki", }, // Pull request settings diff --git a/routers/repo/branch.go b/routers/repo/branch.go index 8b987f0a60..ae87aa5b3a 100644 --- a/routers/repo/branch.go +++ b/routers/repo/branch.go @@ -74,12 +74,6 @@ func DeleteBranchPost(ctx *context.Context) { return } - // Delete branch in local copy if it exists - if err := ctx.Repo.Repository.DeleteLocalBranch(branchName); err != nil { - ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", branchName)) - return - } - ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", branchName)) } From 13583a650f1c32c850413684a4ac835804b22270 Mon Sep 17 00:00:00 2001 From: iliyan ivanov Date: Sat, 11 May 2019 23:27:39 +0300 Subject: [PATCH 10/12] fix syntax highlight in blame view #6895 (#6909) --- routers/repo/blame.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/repo/blame.go b/routers/repo/blame.go index 679b99a15e..964fdc8746 100644 --- a/routers/repo/blame.go +++ b/routers/repo/blame.go @@ -236,7 +236,7 @@ func renderBlame(ctx *context.Context, blameParts []models.BlamePart, commitName //Code line line = gotemplate.HTMLEscapeString(line) - if i != len(lines) { + if i != len(lines)-1 { line += "\n" } if len(part.Lines)-1 == index && len(blameParts)-1 != pi { From a92ab34493d099ab5d74d6f0c8160904428443d8 Mon Sep 17 00:00:00 2001 From: Xaver Maierhofer Date: Sun, 12 May 2019 13:10:12 +0200 Subject: [PATCH 11/12] Fix input size for dependency select (#6913) --- public/css/index.css | 2 +- public/less/_repository.less | 13 +++++++++++-- templates/repo/issue/view_content/sidebar.tmpl | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index 99145d0222..8a7e61d500 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -1 +1 @@ -.tribute-container{box-shadow:0 1px 3px 1px #c7c7c7}.tribute-container ul{background:#fff}.tribute-container li{padding:8px 12px;border-bottom:1px solid #dcdcdc}.tribute-container li img{display:inline-block;vertical-align:middle;width:28px;height:28px;margin-right:5px}.tribute-container li span.fullname{font-weight:400;font-size:.8rem;margin-left:3px}.tribute-container li.highlight,.tribute-container li:hover{background:#2185D0;color:#fff}.emoji{width:1.5em;height:1.5em;display:inline-block;background-size:contain}.ui.label .emoji{height:1.2em!important}@font-face{font-family:Lato;src:url(../vendor/assets/lato-fonts/lato-regular.eot);src:url(../vendor/assets/lato-fonts/lato-regular.eot?#iefix) format('embedded-opentype'),url(../vendor/assets/lato-fonts/lato-regular.woff2) format('woff2'),url(../vendor/assets/lato-fonts/lato-regular.woff) format('woff'),url(../vendor/assets/lato-fonts/lato-regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:Lato;src:url(../vendor/assets/lato-fonts/lato-italic.eot);src:url(../vendor/assets/lato-fonts/lato-italic.eot?#iefix) format('embedded-opentype'),url(../vendor/assets/lato-fonts/lato-italic.woff2) format('woff2'),url(../vendor/assets/lato-fonts/lato-italic.woff) format('woff'),url(../vendor/assets/lato-fonts/lato-italic.ttf) format('truetype');font-weight:400;font-style:italic}@font-face{font-family:Lato;src:url(../vendor/assets/lato-fonts/lato-bold.eot);src:url(../vendor/assets/lato-fonts/lato-bold.eot?#iefix) format('embedded-opentype'),url(../vendor/assets/lato-fonts/lato-bold.woff2) format('woff2'),url(../vendor/assets/lato-fonts/lato-bold.woff) format('woff'),url(../vendor/assets/lato-fonts/lato-bold.ttf) format('truetype');font-weight:700;font-style:normal}@font-face{font-family:Lato;src:url(../vendor/assets/lato-fonts/lato-bolditalic.eot);src:url(../vendor/assets/lato-fonts/lato-bolditalic.eot?#iefix) format('embedded-opentype'),url(../vendor/assets/lato-fonts/lato-bolditalic.woff2) format('woff2'),url(../vendor/assets/lato-fonts/lato-bolditalic.woff) format('woff'),url(../vendor/assets/lato-fonts/lato-bolditalic.ttf) format('truetype');font-weight:700;font-style:italic}@font-face{font-family:'Yu Gothic';src:local('Yu Gothic Medium');font-weight:400}@font-face{font-family:'Yu Gothic';src:local('Yu Gothic Bold');font-weight:700}textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}h1,h2,h3,h4,h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.home .hero h1,.home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.ui.accordion .title:not(.ui),.ui.button,.ui.card>.content>.header.ui.card>.content>.header,.ui.category.search>.results .category>.name,.ui.form input:not([type]),.ui.form input[type=date],.ui.form input[type=datetime-local],.ui.form input[type=email],.ui.form input[type=file],.ui.form input[type=number],.ui.form input[type=password],.ui.form input[type=search],.ui.form input[type=tel],.ui.form input[type=text],.ui.form input[type=time],.ui.form input[type=url],.ui.header,.ui.input input,.ui.input>input,.ui.items>.item>.content>.header,.ui.language>.menu>.item,.ui.list .list>.item .header,.ui.list>.item .header,.ui.menu,.ui.message .header,.ui.modal>.header,.ui.popup>.header,.ui.search>.results .result .title,.ui.search>.results>.message .header,.ui.statistic>.label,.ui.statistic>.value,.ui.statistics .statistic>.label,.ui.statistics .statistic>.value,.ui.steps .step .title,.ui.text.container,body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}body{background-color:#fff;overflow-y:auto;-webkit-font-smoothing:antialiased;display:flex;flex-direction:column}:lang(ja) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}:lang(ja) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}:lang(ja) h1,:lang(ja) h2,:lang(ja) h3,:lang(ja) h4,:lang(ja) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}:lang(ja) .home .hero h1,:lang(ja) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}.ui.language>.menu>.item:lang(ja),:lang(ja) .ui.accordion .title:not(.ui),:lang(ja) .ui.button,:lang(ja) .ui.card>.content>.header.ui.card>.content>.header,:lang(ja) .ui.category.search>.results .category>.name,:lang(ja) .ui.form input:not([type]),:lang(ja) .ui.form input[type=date],:lang(ja) .ui.form input[type=datetime-local],:lang(ja) .ui.form input[type=email],:lang(ja) .ui.form input[type=file],:lang(ja) .ui.form input[type=number],:lang(ja) .ui.form input[type=password],:lang(ja) .ui.form input[type=search],:lang(ja) .ui.form input[type=tel],:lang(ja) .ui.form input[type=text],:lang(ja) .ui.form input[type=time],:lang(ja) .ui.form input[type=url],:lang(ja) .ui.header,:lang(ja) .ui.input input,:lang(ja) .ui.input>input,:lang(ja) .ui.items>.item>.content>.header,:lang(ja) .ui.list .list>.item .header,:lang(ja) .ui.list>.item .header,:lang(ja) .ui.menu,:lang(ja) .ui.message .header,:lang(ja) .ui.modal>.header,:lang(ja) .ui.popup>.header,:lang(ja) .ui.search>.results .result .title,:lang(ja) .ui.search>.results>.message .header,:lang(ja) .ui.statistic>.label,:lang(ja) .ui.statistic>.value,:lang(ja) .ui.statistics .statistic>.label,:lang(ja) .ui.statistics .statistic>.value,:lang(ja) .ui.steps .step .title,:lang(ja) .ui.text.container,:lang(ja) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}:lang(zh-CN) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}:lang(zh-CN) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}:lang(zh-CN) h1,:lang(zh-CN) h2,:lang(zh-CN) h3,:lang(zh-CN) h4,:lang(zh-CN) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}:lang(zh-CN) .home .hero h1,:lang(zh-CN) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}.ui.language>.menu>.item:lang(zh-CN),:lang(zh-CN) .ui.accordion .title:not(.ui),:lang(zh-CN) .ui.button,:lang(zh-CN) .ui.card>.content>.header.ui.card>.content>.header,:lang(zh-CN) .ui.category.search>.results .category>.name,:lang(zh-CN) .ui.form input:not([type]),:lang(zh-CN) .ui.form input[type=date],:lang(zh-CN) .ui.form input[type=datetime-local],:lang(zh-CN) .ui.form input[type=email],:lang(zh-CN) .ui.form input[type=file],:lang(zh-CN) .ui.form input[type=number],:lang(zh-CN) .ui.form input[type=password],:lang(zh-CN) .ui.form input[type=search],:lang(zh-CN) .ui.form input[type=tel],:lang(zh-CN) .ui.form input[type=text],:lang(zh-CN) .ui.form input[type=time],:lang(zh-CN) .ui.form input[type=url],:lang(zh-CN) .ui.header,:lang(zh-CN) .ui.input input,:lang(zh-CN) .ui.input>input,:lang(zh-CN) .ui.items>.item>.content>.header,:lang(zh-CN) .ui.list .list>.item .header,:lang(zh-CN) .ui.list>.item .header,:lang(zh-CN) .ui.menu,:lang(zh-CN) .ui.message .header,:lang(zh-CN) .ui.modal>.header,:lang(zh-CN) .ui.popup>.header,:lang(zh-CN) .ui.search>.results .result .title,:lang(zh-CN) .ui.search>.results>.message .header,:lang(zh-CN) .ui.statistic>.label,:lang(zh-CN) .ui.statistic>.value,:lang(zh-CN) .ui.statistics .statistic>.label,:lang(zh-CN) .ui.statistics .statistic>.value,:lang(zh-CN) .ui.steps .step .title,:lang(zh-CN) .ui.text.container,:lang(zh-CN) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}:lang(zh-TW) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}:lang(zh-TW) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}:lang(zh-TW) h1,:lang(zh-TW) h2,:lang(zh-TW) h3,:lang(zh-TW) h4,:lang(zh-TW) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}:lang(zh-TW) .home .hero h1,:lang(zh-TW) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}.ui.language>.menu>.item:lang(zh-TW),:lang(zh-TW) .ui.accordion .title:not(.ui),:lang(zh-TW) .ui.button,:lang(zh-TW) .ui.card>.content>.header.ui.card>.content>.header,:lang(zh-TW) .ui.category.search>.results .category>.name,:lang(zh-TW) .ui.form input:not([type]),:lang(zh-TW) .ui.form input[type=date],:lang(zh-TW) .ui.form input[type=datetime-local],:lang(zh-TW) .ui.form input[type=email],:lang(zh-TW) .ui.form input[type=file],:lang(zh-TW) .ui.form input[type=number],:lang(zh-TW) .ui.form input[type=password],:lang(zh-TW) .ui.form input[type=search],:lang(zh-TW) .ui.form input[type=tel],:lang(zh-TW) .ui.form input[type=text],:lang(zh-TW) .ui.form input[type=time],:lang(zh-TW) .ui.form input[type=url],:lang(zh-TW) .ui.header,:lang(zh-TW) .ui.input input,:lang(zh-TW) .ui.input>input,:lang(zh-TW) .ui.items>.item>.content>.header,:lang(zh-TW) .ui.list .list>.item .header,:lang(zh-TW) .ui.list>.item .header,:lang(zh-TW) .ui.menu,:lang(zh-TW) .ui.message .header,:lang(zh-TW) .ui.modal>.header,:lang(zh-TW) .ui.popup>.header,:lang(zh-TW) .ui.search>.results .result .title,:lang(zh-TW) .ui.search>.results>.message .header,:lang(zh-TW) .ui.statistic>.label,:lang(zh-TW) .ui.statistic>.value,:lang(zh-TW) .ui.statistics .statistic>.label,:lang(zh-TW) .ui.statistics .statistic>.value,:lang(zh-TW) .ui.steps .step .title,:lang(zh-TW) .ui.text.container,:lang(zh-TW) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}:lang(zh-HK) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}:lang(zh-HK) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}:lang(zh-HK) h1,:lang(zh-HK) h2,:lang(zh-HK) h3,:lang(zh-HK) h4,:lang(zh-HK) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}:lang(zh-HK) .home .hero h1,:lang(zh-HK) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}.ui.language>.menu>.item:lang(zh-HK),:lang(zh-HK) .ui.accordion .title:not(.ui),:lang(zh-HK) .ui.button,:lang(zh-HK) .ui.card>.content>.header.ui.card>.content>.header,:lang(zh-HK) .ui.category.search>.results .category>.name,:lang(zh-HK) .ui.form input:not([type]),:lang(zh-HK) .ui.form input[type=date],:lang(zh-HK) .ui.form input[type=datetime-local],:lang(zh-HK) .ui.form input[type=email],:lang(zh-HK) .ui.form input[type=file],:lang(zh-HK) .ui.form input[type=number],:lang(zh-HK) .ui.form input[type=password],:lang(zh-HK) .ui.form input[type=search],:lang(zh-HK) .ui.form input[type=tel],:lang(zh-HK) .ui.form input[type=text],:lang(zh-HK) .ui.form input[type=time],:lang(zh-HK) .ui.form input[type=url],:lang(zh-HK) .ui.header,:lang(zh-HK) .ui.input input,:lang(zh-HK) .ui.input>input,:lang(zh-HK) .ui.items>.item>.content>.header,:lang(zh-HK) .ui.list .list>.item .header,:lang(zh-HK) .ui.list>.item .header,:lang(zh-HK) .ui.menu,:lang(zh-HK) .ui.message .header,:lang(zh-HK) .ui.modal>.header,:lang(zh-HK) .ui.popup>.header,:lang(zh-HK) .ui.search>.results .result .title,:lang(zh-HK) .ui.search>.results>.message .header,:lang(zh-HK) .ui.statistic>.label,:lang(zh-HK) .ui.statistic>.value,:lang(zh-HK) .ui.statistics .statistic>.label,:lang(zh-HK) .ui.statistics .statistic>.value,:lang(zh-HK) .ui.steps .step .title,:lang(zh-HK) .ui.text.container,:lang(zh-HK) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}:lang(ko) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}:lang(ko) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}:lang(ko) h1,:lang(ko) h2,:lang(ko) h3,:lang(ko) h4,:lang(ko) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}:lang(ko) .home .hero h1,:lang(ko) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}.ui.language>.menu>.item:lang(ko),:lang(ko) .ui.accordion .title:not(.ui),:lang(ko) .ui.button,:lang(ko) .ui.card>.content>.header.ui.card>.content>.header,:lang(ko) .ui.category.search>.results .category>.name,:lang(ko) .ui.form input:not([type]),:lang(ko) .ui.form input[type=date],:lang(ko) .ui.form input[type=datetime-local],:lang(ko) .ui.form input[type=email],:lang(ko) .ui.form input[type=file],:lang(ko) .ui.form input[type=number],:lang(ko) .ui.form input[type=password],:lang(ko) .ui.form input[type=search],:lang(ko) .ui.form input[type=tel],:lang(ko) .ui.form input[type=text],:lang(ko) .ui.form input[type=time],:lang(ko) .ui.form input[type=url],:lang(ko) .ui.header,:lang(ko) .ui.input input,:lang(ko) .ui.input>input,:lang(ko) .ui.items>.item>.content>.header,:lang(ko) .ui.list .list>.item .header,:lang(ko) .ui.list>.item .header,:lang(ko) .ui.menu,:lang(ko) .ui.message .header,:lang(ko) .ui.modal>.header,:lang(ko) .ui.popup>.header,:lang(ko) .ui.search>.results .result .title,:lang(ko) .ui.search>.results>.message .header,:lang(ko) .ui.statistic>.label,:lang(ko) .ui.statistic>.value,:lang(ko) .ui.statistics .statistic>.label,:lang(ko) .ui.statistics .statistic>.value,:lang(ko) .ui.steps .step .title,:lang(ko) .ui.text.container,:lang(ko) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}img{border-radius:3px}table{border-collapse:collapse}a{cursor:pointer}.rounded{border-radius:.28571429rem!important}code,pre{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}code.raw,pre.raw{padding:7px 12px;margin:10px 0;background-color:#f8f8f8;border:1px solid #ddd;border-radius:3px;font-size:13px;line-height:1.5;overflow:auto}code.wrap,pre.wrap{white-space:pre-wrap;word-break:break-all;overflow-wrap:break-word;word-wrap:break-word}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.full.height{flex-grow:1;padding-bottom:80px}.following.bar{z-index:900;left:0;margin:0!important}.following.bar.light{background-color:#fff;border-bottom:1px solid #DDD;box-shadow:0 2px 3px rgba(0,0,0,.04)}.following.bar .column .menu{margin-top:0}.following.bar .top.menu a.item.brand{padding-left:0}.following.bar .brand .ui.mini.image{width:30px}.following.bar .top.menu .dropdown.item.active,.following.bar .top.menu .dropdown.item:hover,.following.bar .top.menu a.item:hover{background-color:transparent}.following.bar .top.menu a.item:hover{color:rgba(0,0,0,.45)}.following.bar .top.menu .menu{z-index:900}.following.bar .octicon{margin-right:.75em}.following.bar .octicon.fitted{margin-right:0}.following.bar .searchbox{background-color:#f4f4f4!important}.following.bar .searchbox:focus{background-color:#e9e9e9!important}.following.bar .text .octicon{width:16px;text-align:center}.following.bar #navbar{width:100vw;min-height:52px;padding:0 .5rem}.following.bar #navbar .brand{margin:0}@media only screen and (max-width:767px){.following.bar #navbar:not(.shown)>:not(:first-child){display:none}}.right.stackable.menu{margin-left:auto;display:flex;align-items:inherit;flex-direction:inherit}.ui.left{float:left}.ui.right{float:right}.ui.button,.ui.menu .item{-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}.ui.container.fluid.padded{padding:0 10px 0 10px}.ui.form .ui.button{font-weight:400}.ui.floating.label{z-index:10}.ui.transparent.label{background-color:transparent}.ui.menu,.ui.segment,.ui.vertical.menu{box-shadow:none}.ui .menu:not(.vertical) .item>.button.compact{padding:.58928571em 1.125em}.ui .menu:not(.vertical) .item>.button.small{font-size:.92857143rem}.ui.menu .ui.dropdown.item .menu .item{margin-right:auto}.ui.dropdown .menu>.item>.floating.label{z-index:11}.ui.dropdown .menu .menu>.item>.floating.label{z-index:21}.ui .text.red{color:#d95c5c!important}.ui .text.red a{color:#d95c5c!important}.ui .text.red a:hover{color:#E67777!important}.ui .text.blue{color:#428bca!important}.ui .text.blue a{color:#15c!important}.ui .text.blue a:hover{color:#428bca!important}.ui .text.black{color:#444}.ui .text.black:hover{color:#000}.ui .text.grey{color:#767676!important}.ui .text.grey a{color:#444!important}.ui .text.grey a:hover{color:#000!important}.ui .text.light.grey{color:#888!important}.ui .text.green{color:#6cc644!important}.ui .text.purple{color:#6e5494!important}.ui .text.yellow{color:#FBBD08!important}.ui .text.gold{color:#a1882b!important}.ui .text.left{text-align:left!important}.ui .text.right{text-align:right!important}.ui .text.small{font-size:.75em}.ui .text.normal{font-weight:400}.ui .text.bold{font-weight:700}.ui .text.italic{font-style:italic}.ui .text.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.ui .text.thin{font-weight:400}.ui .text.middle{vertical-align:middle}.ui .message{text-align:center}.ui.bottom.attached.message{font-weight:700;text-align:left;color:#000}.ui.bottom.attached.message .pull-right{color:#000}.ui.bottom.attached.message .pull-right>span,.ui.bottom.attached.message>span{color:#21ba45}.ui .header>i+.content{padding-left:.75rem;vertical-align:middle}.ui .warning.header{background-color:#F9EDBE!important;border-color:#F0C36D}.ui .warning.segment{border-color:#F0C36D}.ui .info.segment{border:1px solid #c5d5dd}.ui .info.segment.top{background-color:#e6f1f6!important}.ui .info.segment.top h3,.ui .info.segment.top h4{margin-top:0}.ui .info.segment.top h3:last-child{margin-top:4px}.ui .info.segment.top>:last-child{margin-bottom:0}.ui .normal.header{font-weight:400}.ui .avatar.image{border-radius:3px}.ui .form .fake{display:none!important}.ui .form .sub.field{margin-left:25px}.ui .sha.label{font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;font-size:13px;padding:6px 10px 4px 10px;font-weight:400;margin:0 6px}.ui.status.buttons .octicon{margin-right:4px}.ui.inline.delete-button{padding:8px 15px;font-weight:400}.ui .background.red{background-color:#d95c5c!important}.ui .background.blue{background-color:#428bca!important}.ui .background.black{background-color:#444}.ui .background.grey{background-color:#767676!important}.ui .background.light.grey{background-color:#888!important}.ui .background.green{background-color:#6cc644!important}.ui .background.purple{background-color:#6e5494!important}.ui .background.yellow{background-color:#FBBD08!important}.ui .background.gold{background-color:#a1882b!important}.ui .branch-tag-choice{line-height:20px}@media only screen and (max-width:767px){.ui.pagination.menu .item.navigation span.navigation_label,.ui.pagination.menu .item:not(.active):not(.navigation){display:none}}.file-comment{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.87)}.ui.floating.dropdown .overflow.menu .scrolling.menu.items{border-radius:0!important;box-shadow:none!important;border-bottom:1px solid rgba(34,36,38,.15)}.user-menu>.item{width:100%;border-radius:0!important}.scrolling.menu .item.selected{font-weight:700!important}footer{background-color:#fff;border-top:1px solid #d6d6d6;width:100%;flex-basis:40px;color:#888}footer .container{width:100vw!important;padding:0 .5rem}footer .container .fa{width:16px;text-align:center;color:#428bca}footer .container .links>*{border-left:1px solid #d6d6d6;padding-left:8px;margin-left:5px}footer .container .links>:first-child{border-left:none}footer .ui.language .menu{max-height:500px;overflow-y:auto;margin-bottom:7px}footer .ui.left,footer .ui.right{line-height:40px}.hide{display:none}.hide.show-outdated{display:none!important}.hide.hide-outdated{display:none!important}.center{text-align:center}.img-1{width:2px!important;height:2px!important}.img-2{width:4px!important;height:4px!important}.img-3{width:6px!important;height:6px!important}.img-4{width:8px!important;height:8px!important}.img-5{width:10px!important;height:10px!important}.img-6{width:12px!important;height:12px!important}.img-7{width:14px!important;height:14px!important}.img-8{width:16px!important;height:16px!important}.img-9{width:18px!important;height:18px!important}.img-10{width:20px!important;height:20px!important}.img-11{width:22px!important;height:22px!important}.img-12{width:24px!important;height:24px!important}.img-13{width:26px!important;height:26px!important}.img-14{width:28px!important;height:28px!important}.img-15{width:30px!important;height:30px!important}.img-16{width:32px!important;height:32px!important}@media only screen and (min-width:768px){.mobile-only,.ui.button.mobile-only{display:none}.sr-mobile-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}}@media only screen and (max-width:767px){.not-mobile{display:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}@media only screen and (max-width:991px) and (min-width:768px){.ui.container{width:95%}}.hljs{background:inherit!important;padding:0!important}.ui.menu.new-menu{justify-content:center!important;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}@media only screen and (max-width:1200px){.ui.menu.new-menu{overflow-x:auto!important;justify-content:left!important;padding-bottom:5px}.ui.menu.new-menu::-webkit-scrollbar{height:8px;display:none}.ui.menu.new-menu:hover::-webkit-scrollbar{display:block}.ui.menu.new-menu::-webkit-scrollbar-track{background:rgba(0,0,0,.01)}.ui.menu.new-menu::-webkit-scrollbar-thumb{background:rgba(0,0,0,.2)}.ui.menu.new-menu:after{position:absolute;margin-top:-15px;display:block;background-image:linear-gradient(to right,rgba(255,255,255,0),#fff 100%);content:' ';right:0;height:53px;z-index:1000;width:60px;clear:none;visibility:visible}.ui.menu.new-menu a.item:last-child{padding-right:30px!important}}[v-cloak]{display:none!important}.repos-search{padding-bottom:0!important}.repos-filter{margin-top:0!important;border-bottom-width:0!important;margin-bottom:2px!important}#user-heatmap{width:107%;text-align:center}#user-heatmap svg:not(:root){overflow:inherit;padding:0!important}@media only screen and (max-width:1200px){#user-heatmap{display:none}}.heatmap-color-0{background-color:#f4f4f4}.heatmap-color-1{background-color:#d7e5db}.heatmap-color-2{background-color:#adc7ab}.heatmap-color-3{background-color:#83a87b}.heatmap-color-4{background-color:#598a4b}.heatmap-color-5{background-color:#2f6b1b}.archived-icon{color:#b3b3b3!important}.archived-icon{color:#b3b3b3!important}.oauth2-authorize-application-box{margin-top:3em!important}.ui.tabular.menu .item{color:rgba(0,0,0,.5)}.ui.tabular.menu .item:hover{color:rgba(0,0,0,.8)}.ui.tabular.menu .item.active{color:rgba(0,0,0,.9)}.markdown:not(code){overflow:hidden;font-size:16px;line-height:1.6!important;word-wrap:break-word}.markdown:not(code).ui.segment{padding:3em}.markdown:not(code).file-view{padding:2em 2em 2em!important}.markdown:not(code)>:first-child{margin-top:0!important}.markdown:not(code)>:last-child{margin-bottom:0!important}.markdown:not(code) a:not([href]){color:inherit;text-decoration:none}.markdown:not(code) .absent{color:#c00}.markdown:not(code) .anchor{position:absolute;top:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}.markdown:not(code) .anchor:focus{outline:0}.markdown:not(code) h1,.markdown:not(code) h2,.markdown:not(code) h3,.markdown:not(code) h4,.markdown:not(code) h5,.markdown:not(code) h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:700;line-height:1.4}.markdown:not(code) h1:first-of-type,.markdown:not(code) h2:first-of-type,.markdown:not(code) h3:first-of-type,.markdown:not(code) h4:first-of-type,.markdown:not(code) h5:first-of-type,.markdown:not(code) h6:first-of-type{margin-top:0!important}.markdown:not(code) h1 .octicon-link,.markdown:not(code) h2 .octicon-link,.markdown:not(code) h3 .octicon-link,.markdown:not(code) h4 .octicon-link,.markdown:not(code) h5 .octicon-link,.markdown:not(code) h6 .octicon-link{display:none;color:#000;vertical-align:middle}.markdown:not(code) h1:hover .anchor,.markdown:not(code) h2:hover .anchor,.markdown:not(code) h3:hover .anchor,.markdown:not(code) h4:hover .anchor,.markdown:not(code) h5:hover .anchor,.markdown:not(code) h6:hover .anchor{padding-left:8px;margin-left:-30px;text-decoration:none}.markdown:not(code) h1:hover .anchor .octicon-link,.markdown:not(code) h2:hover .anchor .octicon-link,.markdown:not(code) h3:hover .anchor .octicon-link,.markdown:not(code) h4:hover .anchor .octicon-link,.markdown:not(code) h5:hover .anchor .octicon-link,.markdown:not(code) h6:hover .anchor .octicon-link{display:inline-block}.markdown:not(code) h1 code,.markdown:not(code) h1 tt,.markdown:not(code) h2 code,.markdown:not(code) h2 tt,.markdown:not(code) h3 code,.markdown:not(code) h3 tt,.markdown:not(code) h4 code,.markdown:not(code) h4 tt,.markdown:not(code) h5 code,.markdown:not(code) h5 tt,.markdown:not(code) h6 code,.markdown:not(code) h6 tt{font-size:inherit}.markdown:not(code) h1{padding-bottom:.3em;font-size:2.25em;line-height:1.2;border-bottom:1px solid #eee}.markdown:not(code) h1 .anchor{line-height:1}.markdown:not(code) h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px solid #eee}.markdown:not(code) h2 .anchor{line-height:1}.markdown:not(code) h3{font-size:1.5em;line-height:1.43}.markdown:not(code) h3 .anchor{line-height:1.2}.markdown:not(code) h4{font-size:1.25em}.markdown:not(code) h4 .anchor{line-height:1.2}.markdown:not(code) h5{font-size:1em}.markdown:not(code) h5 .anchor{line-height:1.1}.markdown:not(code) h6{font-size:1em;color:#777}.markdown:not(code) h6 .anchor{line-height:1.1}.markdown:not(code) blockquote,.markdown:not(code) dl,.markdown:not(code) ol,.markdown:not(code) p,.markdown:not(code) pre,.markdown:not(code) table,.markdown:not(code) ul{margin-top:0;margin-bottom:16px}.markdown:not(code) blockquote{margin-left:0}.markdown:not(code) hr{height:4px;padding:0;margin:16px 0;background-color:#e7e7e7;border:0 none}.markdown:not(code) ol,.markdown:not(code) ul{padding-left:2em}.markdown:not(code) ol.no-list,.markdown:not(code) ul.no-list{padding:0;list-style-type:none}.markdown:not(code) ol ol,.markdown:not(code) ol ul,.markdown:not(code) ul ol,.markdown:not(code) ul ul{margin-top:0;margin-bottom:0}.markdown:not(code) ol ol,.markdown:not(code) ul ol{list-style-type:lower-roman}.markdown:not(code) li>p{margin-top:0}.markdown:not(code) dl{padding:0}.markdown:not(code) dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}.markdown:not(code) dl dd{padding:0 16px;margin-bottom:16px}.markdown:not(code) blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}.markdown:not(code) blockquote>:first-child{margin-top:0}.markdown:not(code) blockquote>:last-child{margin-bottom:0}.markdown:not(code) table{width:auto;overflow:auto;word-break:normal;word-break:keep-all;display:block}.markdown:not(code) table th{font-weight:700}.markdown:not(code) table td,.markdown:not(code) table th{padding:6px 13px!important;border:1px solid #ddd!important}.markdown:not(code) table tr{background-color:#fff;border-top:1px solid #ccc}.markdown:not(code) table tr:nth-child(2n){background-color:#f8f8f8}.markdown:not(code) img{max-width:100%;box-sizing:border-box}.markdown:not(code) .emoji{max-width:none}.markdown:not(code) span.frame{display:block;overflow:hidden}.markdown:not(code) span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #ddd}.markdown:not(code) span.frame span img{display:block;float:left}.markdown:not(code) span.frame span span{display:block;padding:5px 0 0;clear:both;color:#333}.markdown:not(code) span.align-center{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown:not(code) span.align-center span img{margin:0 auto;text-align:center}.markdown:not(code) span.align-right{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown:not(code) span.align-right span img{margin:0;text-align:right}.markdown:not(code) span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown:not(code) span.float-left span{margin:13px 0 0}.markdown:not(code) span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown:not(code) span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown:not(code) code,.markdown:not(code) tt{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px}.markdown:not(code) code:after,.markdown:not(code) code:before,.markdown:not(code) tt:after,.markdown:not(code) tt:before{letter-spacing:-.2em;content:"\00a0"}.markdown:not(code) code br,.markdown:not(code) tt br{display:none}.markdown:not(code) del code{text-decoration:inherit}.markdown:not(code) pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0;border:0}.markdown:not(code) .highlight{margin-bottom:16px}.markdown:not(code) .highlight pre,.markdown:not(code) pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border-radius:3px}.markdown:not(code) .highlight pre{margin-bottom:0;word-break:normal}.markdown:not(code) pre{word-wrap:normal}.markdown:not(code) pre code,.markdown:not(code) pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown:not(code) pre code:after,.markdown:not(code) pre code:before,.markdown:not(code) pre tt:after,.markdown:not(code) pre tt:before{content:normal}.markdown:not(code) kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#555;vertical-align:middle;background-color:#fcfcfc;border:solid 1px #ccc;border-bottom-color:#bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb}.markdown:not(code) input[type=checkbox]{vertical-align:middle!important}.markdown:not(code) .csv-data td,.markdown:not(code) .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown:not(code) .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown:not(code) .csv-data tr{border-top:0}.markdown:not(code) .csv-data th{font-weight:700;background:#f8f8f8;border-top:0}.markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em}.home .logo{max-width:220px}@media only screen and (max-width:767px){.home .hero h1{font-size:3.5em}.home .hero h2{font-size:2em}}@media only screen and (min-width:768px){.home .hero h1{font-size:5.5em}.home .hero h2{font-size:3em}}.home .hero .octicon{color:#5aa509;font-size:40px;width:50px}.home .hero.header{font-size:20px}.home p.large{font-size:16px}.home .stackable{padding-top:30px}.home a{color:#5aa509}.signup{padding-top:15px}@media only screen and (max-width:880px){footer .ui.container .left,footer .ui.container .right{display:block;text-align:center;float:none}}.install{padding-top:45px}.install form label{text-align:right;width:320px!important}.install form input{width:35%!important}.install form .field{text-align:left}.install form .field .help{margin-left:335px!important}.install form .field.optional .title{margin-left:38%}.install .ui .checkbox{margin-left:40%!important}.install .ui .checkbox label{width:auto!important}.form .help{color:#999;padding-top:.6em;padding-bottom:.6em;display:inline-block}.ui.attached.header{background:#f0f0f0}.ui.attached.header .right{margin-top:-5px}.ui.attached.header .right .button{padding:8px 10px;font-weight:400}#create-page-form form{margin:auto}#create-page-form form .ui.message{text-align:center}@media only screen and (min-width:768px){#create-page-form form{width:800px!important}#create-page-form form .header{padding-left:280px!important}#create-page-form form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}#create-page-form form .help{margin-left:265px!important}#create-page-form form .optional .title{margin-left:250px!important}#create-page-form form input,#create-page-form form textarea{width:50%!important}}@media only screen and (max-width:767px){#create-page-form form .optional .title{margin-left:15px}#create-page-form form .inline.field>label{display:block}}.signin .oauth2 div{display:inline-block}.signin .oauth2 div p{margin:10px 5px 0 0;float:left}.signin .oauth2 a{margin-right:3px}.signin .oauth2 a:last-child{margin-right:0}.signin .oauth2 img{width:32px;height:32px}.signin .oauth2 img.openidConnect{width:auto}@media only screen and (min-width:768px){.g-recaptcha{margin:0 auto!important;width:304px;padding-left:30px}}@media screen and (max-height:575px){#rc-imageselect,.g-recaptcha{transform:scale(.77);transform-origin:0 0}}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{margin:auto}.user.activate form .ui.message,.user.forgot.password form .ui.message,.user.reset.password form .ui.message,.user.signin form .ui.message,.user.signup form .ui.message{text-align:center}@media only screen and (min-width:768px){.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:800px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:280px!important}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.user.activate form .help,.user.forgot.password form .help,.user.reset.password form .help,.user.signin form .help,.user.signup form .help{margin-left:265px!important}.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:250px!important}.user.activate form input,.user.activate form textarea,.user.forgot.password form input,.user.forgot.password form textarea,.user.reset.password form input,.user.reset.password form textarea,.user.signin form input,.user.signin form textarea,.user.signup form input,.user.signup form textarea{width:50%!important}}@media only screen and (max-width:767px){.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:15px}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{display:block}}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:700px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:0!important;text-align:center}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{width:200px}@media only screen and (max-width:768px){.user.activate form .inline.field>label,.user.activate form input,.user.forgot.password form .inline.field>label,.user.forgot.password form input,.user.reset.password form .inline.field>label,.user.reset.password form input,.user.signin form .inline.field>label,.user.signin form input,.user.signup form .inline.field>label,.user.signup form input{width:100%!important}}.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{margin:auto}.repository.new.fork form .ui.message,.repository.new.migrate form .ui.message,.repository.new.repo form .ui.message{text-align:center}@media only screen and (min-width:768px){.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{width:800px!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:280px!important}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.repository.new.fork form .help,.repository.new.migrate form .help,.repository.new.repo form .help{margin-left:265px!important}.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:250px!important}.repository.new.fork form input,.repository.new.fork form textarea,.repository.new.migrate form input,.repository.new.migrate form textarea,.repository.new.repo form input,.repository.new.repo form textarea{width:50%!important}}@media only screen and (max-width:767px){.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:15px}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{display:block}}.repository.new.fork form .dropdown .dropdown.icon,.repository.new.migrate form .dropdown .dropdown.icon,.repository.new.repo form .dropdown .dropdown.icon{margin-top:-7px!important;padding-bottom:5px}.repository.new.fork form .dropdown .text,.repository.new.migrate form .dropdown .text,.repository.new.repo form .dropdown .text{margin-right:0!important}.repository.new.fork form .dropdown .text i,.repository.new.migrate form .dropdown .text i,.repository.new.repo form .dropdown .text i{margin-right:0!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:0!important;text-align:center}@media only screen and (max-width:768px){.repository.new.fork form .selection.dropdown,.repository.new.fork form input,.repository.new.fork form label,.repository.new.migrate form .selection.dropdown,.repository.new.migrate form input,.repository.new.migrate form label,.repository.new.repo form .selection.dropdown,.repository.new.repo form input,.repository.new.repo form label{width:100%!important}.repository.new.fork form .field a,.repository.new.fork form .field button,.repository.new.migrate form .field a,.repository.new.migrate form .field button,.repository.new.repo form .field a,.repository.new.repo form .field button{margin-bottom:1em;width:100%}}@media only screen and (min-width:768px){.repository.new.repo .ui.form #auto-init{margin-left:265px!important}}.repository.new.repo .ui.form .selection.dropdown:not(.owner){width:50%!important}@media only screen and (max-width:768px){.repository.new.repo .ui.form .selection.dropdown:not(.owner){width:100%!important}}.new.webhook form .help{margin-left:25px}.new.webhook .events.fields .column{padding-left:40px}.githook textarea{font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}@media only screen and (max-width:768px){.new.org .ui.form .field a,.new.org .ui.form .field button{margin-bottom:1em;width:100%}.new.org .ui.form .field input{width:100%!important}}.repository{padding-top:15px}.repository .repo-header .ui.compact.menu{margin-left:1rem}.repository .repo-header .ui.header{margin-top:0}.repository .repo-header .mega-octicon{width:30px;font-size:30px}.repository .repo-header .ui.huge.breadcrumb{font-weight:400;font-size:1.5rem}.repository .repo-header .fork-flag{margin-left:36px;margin-top:3px;display:block;font-size:12px;white-space:nowrap}.repository .repo-header .octicon.octicon-repo-forked{margin-top:-1px;font-size:15px}.repository .repo-header .button{margin-top:2px;margin-bottom:2px}.repository .tabs .navbar{justify-content:initial}.repository .navbar{display:flex;justify-content:space-between}.repository .navbar .ui.label{margin-left:7px;padding:3px 5px}.repository .owner.dropdown{min-width:40%!important}.repository #file-buttons{margin-left:auto!important;font-weight:400}.repository #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository .metas .menu{max-height:300px;overflow-x:auto}.repository .metas .ui.list .hide{display:none!important}.repository .metas .ui.list .item{padding:0}.repository .metas .ui.list .label.color{padding:0 8px;margin-right:5px}.repository .metas .ui.list a{margin:2px 0}.repository .metas .ui.list a .text{color:#444}.repository .metas .ui.list a .text:hover{color:#000}.repository .metas #deadlineForm input{width:12.8rem;border-radius:4px 0 0 4px;border-right:0;white-space:nowrap}.repository .header-wrapper{background-color:#FAFAFA;margin-top:-15px;padding-top:15px}.repository .header-wrapper .ui.tabs.divider{border-bottom:none}.repository .header-wrapper .ui.tabular .octicon{margin-right:5px}.repository .filter.menu .label.color{border-radius:3px;margin-left:15px;padding:0 8px}.repository .filter.menu .octicon{float:left;margin:5px -7px 0 -5px;width:16px}.repository .filter.menu.labels .octicon{margin:-2px -7px 0 -5px}.repository .filter.menu .text{margin-left:.9em}.repository .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.repository .filter.menu .dropdown.item{margin:1px;padding-right:0}.repository .select-label .item{max-width:250px;overflow:hidden;text-overflow:ellipsis}.repository .select-label .desc{padding-left:16px}.repository .ui.tabs.container{margin-top:14px;margin-bottom:0}.repository .ui.tabs.container .ui.menu{border-bottom:none}.repository .ui.tabs.divider{margin-top:0;margin-bottom:20px}.repository #clone-panel{width:350px}@media only screen and (max-width:768px){.repository #clone-panel{width:100%}}.repository #clone-panel input{border-radius:0;padding:5px 10px;width:50%}.repository #clone-panel .clone.button{font-size:13px;padding:0 5px}.repository #clone-panel .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository #clone-panel .icon.button{padding:0 10px}.repository #clone-panel .dropdown .menu{right:0!important;left:auto!important}.repository.file.list .repo-description{display:flex;justify-content:space-between;align-items:center}.repository.file.list #repo-desc{font-size:1.2em}.repository.file.list .choose.reference .header .icon{font-size:1.4em}.repository.file.list .repo-path .divider,.repository.file.list .repo-path .section{display:inline}.repository.file.list #file-buttons{font-weight:400}.repository.file.list #file-buttons .ui.button{padding:8px 10px;font-weight:400}@media only screen and (max-width:768px){.repository.file.list #file-buttons .ui.tiny.blue.buttons{width:100%}}.repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400}.repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px}.repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777}.repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px}.repository.file.list #repo-files-table tbody .octicon.octicon-file-directory,.repository.file.list #repo-files-table tbody .octicon.octicon-file-submodule,.repository.file.list #repo-files-table tbody .octicon.octicon-file-symlink-directory{color:#1e70bf}.repository.file.list #repo-files-table td{padding-top:8px;padding-bottom:8px;overflow:initial}.repository.file.list #repo-files-table td.name{max-width:150px}.repository.file.list #repo-files-table td.message{max-width:400px}.repository.file.list #repo-files-table td.age{width:120px}.repository.file.list #repo-files-table td .truncate{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;vertical-align:top;white-space:nowrap}.repository.file.list #repo-files-table td.message .isSigned{cursor:default}.repository.file.list #repo-files-table tr:hover{background-color:#ffE}.repository.file.list #repo-files-table .jumpable-path{color:#888}.repository.file.list .non-diff-file-content .header .icon{font-size:1em}.repository.file.list .non-diff-file-content .header .file-actions{margin-top:0;margin-bottom:-5px;padding-left:20px}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon{display:inline-block;padding:5px;margin-left:5px;line-height:1;color:#767676;vertical-align:middle;background:0 0;border:0;outline:0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon:hover{color:#4078c0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon-danger:hover{color:#bd2c00}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon.disabled{color:#bbb;cursor:default}.repository.file.list .non-diff-file-content .header .file-actions #delete-file-form{display:inline-block}.repository.file.list .non-diff-file-content .view-raw{padding:5px}.repository.file.list .non-diff-file-content .view-raw *{max-width:100%}.repository.file.list .non-diff-file-content .view-raw img{padding:5px 5px 0 5px}.repository.file.list .non-diff-file-content .plain-text{padding:1em 2em 1em 2em}.repository.file.list .non-diff-file-content pre{overflow:auto}.repository.file.list .non-diff-file-content .code-view *{font-size:12px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;line-height:20px}.repository.file.list .non-diff-file-content .code-view table{width:100%}.repository.file.list .non-diff-file-content .code-view .lines-num{vertical-align:top;text-align:right;color:#999;background:#f5f5f5;width:1%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.repository.file.list .non-diff-file-content .code-view .lines-num span{line-height:20px;padding:0 10px;cursor:pointer;display:block}.repository.file.list .non-diff-file-content .code-view .lines-code,.repository.file.list .non-diff-file-content .code-view .lines-num{padding:0}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs,.repository.file.list .non-diff-file-content .code-view .lines-code ol,.repository.file.list .non-diff-file-content .code-view .lines-code pre,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs,.repository.file.list .non-diff-file-content .code-view .lines-num ol,.repository.file.list .non-diff-file-content .code-view .lines-num pre{background-color:#fff;margin:0;padding:0!important}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-code ol li,.repository.file.list .non-diff-file-content .code-view .lines-code pre li,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-num ol li,.repository.file.list .non-diff-file-content .code-view .lines-num pre li{display:block;width:100%}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-code ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-code pre li.active,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-num ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-num pre li.active{background:#ffd}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-code ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-code pre li:before,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-num ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-num pre li:before{content:' '}.repository.file.list .non-diff-file-content .code-view .lines-commit{vertical-align:top;color:#999;padding:0;background:#f5f5f5;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info{width:350px;max-width:350px;display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;padding:0 0 0 10px}.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info .blame-data{display:flex;font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial}.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info .blame-data .blame-message{flex-grow:2;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;line-height:20px}.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info .blame-data .blame-avatar,.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info .blame-data .blame-time{flex-shrink:0}.repository.file.list .non-diff-file-content .code-view .lines-commit .ui.avatar.image{height:18px;width:18px}.repository.file.list .non-diff-file-content .code-view .lines-code .bottom-line,.repository.file.list .non-diff-file-content .code-view .lines-commit .bottom-line,.repository.file.list .non-diff-file-content .code-view .lines-num .bottom-line{border-bottom:1px solid #eaecef}.repository.file.list .non-diff-file-content .code-view .active{background:#ffd}.repository.file.list .sidebar{padding-left:0}.repository.file.list .sidebar .octicon{width:16px}.repository.file.editor .treepath{width:100%}.repository.file.editor .treepath input{vertical-align:middle;box-shadow:rgba(0,0,0,.0745098) 0 1px 2px inset;width:inherit;padding:7px 8px;margin-right:5px}.repository.file.editor .tabular.menu .octicon{margin-right:5px}.repository.file.editor .commit-form-wrapper{padding-left:64px}.repository.file.editor .commit-form-wrapper .commit-avatar{float:left;margin-left:-64px;width:3em;height:auto}.repository.file.editor .commit-form-wrapper .commit-form{position:relative;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form:after,.repository.file.editor .commit-form-wrapper .commit-form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.file.editor .commit-form-wrapper .commit-form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#fff}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name{display:inline-block;padding:3px 6px;font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.65);background-color:rgba(209,227,237,.45);border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input{position:relative;margin-left:25px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input{width:240px!important;padding-left:26px!important}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch{position:absolute;top:9px;left:10px;color:#b0c4ce}.repository.options #interval{width:100px!important;min-width:100px}.repository.options .danger .item{padding:20px 15px}.repository.options .danger .ui.divider{margin:0}.repository.new.issue .comment.form .comment .avatar{width:3em}.repository.new.issue .comment.form .content{margin-left:4em}.repository.new.issue .comment.form .content:after,.repository.new.issue .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.new.issue .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.new.issue .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.new.issue .comment.form .content:after{border-right-color:#fff}.repository.new.issue .comment.form .content .markdown{font-size:14px}.repository.new.issue .comment.form .metas{min-width:220px}.repository.new.issue .comment.form .metas .filter.menu{max-height:300px;overflow-x:auto}.repository.view.issue .title{padding-bottom:0!important}.repository.view.issue .title h1{font-weight:300;font-size:2.3rem;margin-bottom:5px}.repository.view.issue .title h1 .ui.input{font-size:.5em;vertical-align:top;width:50%;min-width:600px}.repository.view.issue .title h1 .ui.input input{font-size:1.5em;padding:6px 10px}.repository.view.issue .title .index{font-weight:300;color:#aaa;letter-spacing:-1px}.repository.view.issue .title .label{margin-right:10px}.repository.view.issue .title .edit-zone{margin-top:10px}.repository.view.issue .pull-desc code{color:#0166E6}.repository.view.issue .pull.tabular.menu{margin-bottom:10px}.repository.view.issue .pull.tabular.menu .octicon{margin-right:5px}.repository.view.issue .pull.tab.segment{border:none;padding:0;padding-top:10px;box-shadow:none;background-color:inherit}.repository.view.issue .pull .merge.box .avatar{margin-left:10px;margin-top:10px}.repository.view.issue .pull .review-item .avatar,.repository.view.issue .pull .review-item .type-icon{float:none;display:inline-block;text-align:center;vertical-align:middle}.repository.view.issue .pull .review-item .avatar .octicon,.repository.view.issue .pull .review-item .type-icon .octicon{width:23px;font-size:23px;margin-top:.45em}.repository.view.issue .pull .review-item .text{margin:.3em 0 .5em .5em}.repository.view.issue .pull .review-item .type-icon{float:right;margin-right:1em}.repository.view.issue .pull .review-item .divider{margin:.5rem 0}.repository.view.issue .pull .review-item .review-content{padding:1em 0 1em 3.8em}.repository.view.issue .comment-list:before{display:block;content:"";position:absolute;margin-top:12px;margin-bottom:14px;top:0;bottom:0;left:96px;width:2px;background-color:#f3f3f3;z-index:-1}.repository.view.issue .comment-list .comment .avatar{width:3em}.repository.view.issue .comment-list .comment .tag{color:#767676;margin-top:3px;padding:2px 5px;font-size:12px;border:1px solid rgba(0,0,0,.1);border-radius:3px}.repository.view.issue .comment-list .comment .actions .item{float:left}.repository.view.issue .comment-list .comment .actions .item.tag{margin-right:5px}.repository.view.issue .comment-list .comment .actions .item.action{margin-top:6px;margin-left:10px}.repository.view.issue .comment-list .comment .content{margin-left:4em}.repository.view.issue .comment-list .comment .content>.header{font-weight:400;padding:auto 15px;position:relative;color:#767676;background-color:#f7f7f7;border-bottom:1px solid #eee;border-top-left-radius:3px;border-top-right-radius:3px}.repository.view.issue .comment-list .comment .content>.header:after,.repository.view.issue .comment-list .comment .content>.header:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.view.issue .comment-list .comment .content>.header:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.view.issue .comment-list .comment .content>.header:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.view.issue .comment-list .comment .content>.header .text{max-width:78%;padding-top:10px;padding-bottom:10px}.repository.view.issue .comment-list .comment .content .markdown{font-size:14px}.repository.view.issue .comment-list .comment .content .no-content{color:#767676;font-style:italic}.repository.view.issue .comment-list .comment .content>.bottom.segment{background:#f3f4f5}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.images::after{clear:both;content:' ';display:block}.repository.view.issue .comment-list .comment .content>.bottom.segment a{display:block;float:left;margin:5px;padding:5px;height:150px;border:solid 1px #eee;border-radius:3px;max-width:150px;background-color:#fff}.repository.view.issue .comment-list .comment .content>.bottom.segment a:before{content:' ';display:inline-block;height:100%;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.image{max-height:100%;width:auto;margin:0;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image{font-size:128px;color:#000}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image:hover{color:#000}.repository.view.issue .comment-list .comment .ui.form .field:first-child{clear:none}.repository.view.issue .comment-list .comment .ui.form .tab.segment{border:none;padding:0;padding-top:10px}.repository.view.issue .comment-list .comment .ui.form textarea{height:200px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.repository.view.issue .comment-list .comment .edit.buttons{margin-top:10px}.repository.view.issue .comment-list .event{position:relative;margin:15px 0 15px 79px;padding-left:25px}.repository.view.issue .comment-list .event .octicon{width:30px;float:left;text-align:center}.repository.view.issue .comment-list .event .octicon.octicon-circle-slash{margin-top:5px;margin-left:-34.5px;font-size:20px;color:#bd2c00}.repository.view.issue .comment-list .event .octicon.octicon-primitive-dot{margin-left:-28.5px;margin-right:-1px;font-size:30px;color:#6cc644}.repository.view.issue .comment-list .event .octicon.octicon-bookmark{margin-top:3px;margin-left:-31px;margin-right:-1px;font-size:25px}.repository.view.issue .comment-list .event .octicon.octicon-comment{margin-top:4px;margin-left:-35px;font-size:24px}.repository.view.issue .comment-list .event .octicon.octicon-eye{margin-top:3px;margin-left:-35px;margin-right:0;font-size:22px}.repository.view.issue .comment-list .event .octicon.octicon-x{margin-left:-33px;font-size:25px}.repository.view.issue .comment-list .event .detail{font-size:.9rem;margin-top:5px;margin-left:35px}.repository.view.issue .comment-list .event .detail .octicon.octicon-git-commit{margin-top:2px}.repository.view.issue .ui.segment.metas{margin-top:-3px}.repository.view.issue .ui.participants img{margin-top:5px;margin-right:5px}.repository.view.issue .ui.depending .item.is-closed .title{text-decoration:line-through}.repository .comment.form .ui.comments{margin-top:-12px;max-width:100%}.repository .comment.form .content .field:first-child{clear:none}.repository .comment.form .content .form:after,.repository .comment.form .content .form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository .comment.form .content .form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository .comment.form .content .form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository .comment.form .content .form:after{border-right-color:#fff}.repository .comment.form .content .tab.segment{border:none;padding:0;padding-top:10px}.repository .comment.form .content textarea{height:200px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.repository .label.list{list-style:none;padding-top:15px}.repository .label.list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .label.list .item a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .label.list .item a:hover{color:#000}.repository .label.list .item a.open-issues{margin-right:30px}.repository .label.list .item .ui.label{font-size:1em}.repository .milestone.list{list-style:none;padding-top:15px}.repository .milestone.list>.item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .milestone.list>.item>a{padding-top:5px;padding-right:10px;color:#000}.repository .milestone.list>.item>a:hover{color:#4078c0}.repository .milestone.list>.item .ui.progress{width:40%;padding:0;border:0;margin:0}.repository .milestone.list>.item .ui.progress .bar{height:20px}.repository .milestone.list>.item .meta{color:#999;padding-top:5px}.repository .milestone.list>.item .meta .issue-stats .octicon{padding-left:5px}.repository .milestone.list>.item .meta .overdue{color:red}.repository .milestone.list>.item .operate{margin-top:-15px}.repository .milestone.list>.item .operate>a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .milestone.list>.item .operate>a:hover{color:#000}.repository .milestone.list>.item .content{padding-top:10px}.repository.new.milestone textarea{height:200px}.repository.new.milestone #deadline{width:150px}.repository.compare.pull .choose.branch .octicon{padding-right:10px}.repository.compare.pull .comment.form .content:after,.repository.compare.pull .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.compare.pull .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.compare.pull .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.compare.pull .comment.form .content:after{border-right-color:#fff}.repository .filter.dropdown .menu{margin-top:1px!important}.repository.branches .commit-divergence .bar-group{position:relative;float:left;padding-bottom:6px;width:90px}.repository.branches .commit-divergence .bar-group:last-child{border-left:1px solid #b4b4b4}.repository.branches .commit-divergence .count{margin:0 3px}.repository.branches .commit-divergence .count.count-ahead{text-align:left}.repository.branches .commit-divergence .count.count-behind{text-align:right}.repository.branches .commit-divergence .bar{height:4px;position:absolute;background-color:#d4d4d5}.repository.branches .commit-divergence .bar.bar-behind{right:0}.repository.branches .commit-divergence .bar.bar-ahead{left:0}.repository.commits .header .search input{font-weight:400;padding:5px 10px}.repository #commits-table thead th:first-of-type{padding-left:15px}.repository #commits-table thead .sha{width:140px}.repository #commits-table thead .shatd{text-align:center}.repository #commits-table td.sha .sha.label{margin:0}.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n){background-color:rgba(0,0,0,.02)!important}.repository #commits-table td.sha .sha.label.isSigned,.repository #repo-files-table .sha.label.isSigned{border:1px solid #BBB}.repository #commits-table td.sha .sha.label.isSigned .detail.icon,.repository #repo-files-table .sha.label.isSigned .detail.icon{background:#FAFAFA;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #BBB;border-top-left-radius:0;border-bottom-left-radius:0}.repository #commits-table td.sha .sha.label.isSigned.isVerified,.repository #repo-files-table .sha.label.isSigned.isVerified{border:1px solid #21BA45;background:rgba(33,186,69,.1)}.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid rgba(33,186,69,.5)}.repository .diff-detail-box{padding:7px 0;background:#fff;line-height:30px}.repository .diff-detail-box>div:after{clear:both;content:"";display:block}.repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px}.repository .diff-detail-box ol li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #DDD;padding-left:6px}.repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle}.repository .diff-detail-box span.status.modify{background-color:#f0db88}.repository .diff-detail-box span.status.add{background-color:#b4e2b4}.repository .diff-detail-box span.status.del{background-color:#e9aeae}.repository .diff-detail-box span.status.rename{background-color:#dad8ff}.repository .diff-detail-box .detail-files{background:#fff;margin:0}.repository .diff-box .header{display:flex;align-items:center}.repository .diff-box .header .count{margin-right:12px;font-size:13px;flex:0 0 auto}.repository .diff-box .header .count .bar{background-color:#bd2c00;height:12px;width:40px;display:inline-block;margin:2px 4px 0 4px;vertical-align:text-top}.repository .diff-box .header .count .bar .add{background-color:#55a532;height:12px}.repository .diff-box .header .file{flex:1;color:#888;word-break:break-all}.repository .diff-box .header .button{margin:-5px 0 -5px 12px;padding:8px 10px;flex:0 0 auto}.repository .diff-file-box .header{background-color:#f7f7f7}.repository .diff-file-box .file-body.file-code .lines-num{text-align:right;color:#A7A7A7;background:#fafafa;width:1%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:top}.repository .diff-file-box .file-body.file-code .lines-num span.fold{display:block;text-align:center}.repository .diff-file-box .file-body.file-code .lines-num-old{border-right:1px solid #DDD}.repository .diff-file-box .code-diff{font-size:12px}.repository .diff-file-box .code-diff td{padding:0;padding-left:10px;border-top:none}.repository .diff-file-box .code-diff pre{margin:0}.repository .diff-file-box .code-diff .lines-num{border-color:#d4d4d5;border-right-width:1px;border-right-style:solid;padding:0 5px}.repository .diff-file-box .code-diff tbody tr td.halfwidth{width:49%}.repository .diff-file-box .code-diff tbody tr td.tag-code,.repository .diff-file-box .code-diff tbody tr.tag-code td{background-color:#F0F0F0!important;border-color:#D2CECE!important;padding-top:8px;padding-bottom:8px}.repository .diff-file-box .code-diff tbody tr .removed-code{background-color:#f99}.repository .diff-file-box .code-diff tbody tr .added-code{background-color:#9f9}.repository .diff-file-box .code-diff-unified tbody tr.del-code td{background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-unified tbody tr.add-code td{background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split table,.repository .diff-file-box .code-diff-split tbody{width:100%}.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(2),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(4){background-color:#fafafa}.repository .diff-file-box .code-diff-split tbody tr td.del-code,.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(2){background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-split tbody tr td.add-code,.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(4){background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split tbody tr td:nth-child(3){border-left-width:1px;border-left-style:solid}.repository .diff-file-box.file-content{clear:right}.repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px}.repository .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden}.repository .repo-search-result{padding-top:10px;padding-bottom:10px}.repository .repo-search-result .lines-num a{color:inherit}.repository.quickstart .guide .item{padding:1em}.repository.quickstart .guide .item small{font-weight:400}.repository.quickstart .guide .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository.quickstart .guide .ui.action.small.input{width:100%}.repository.quickstart .guide #repo-clone-url{border-radius:0;padding:5px 10px;font-size:1.2em}.repository.release #release-list{border-top:1px solid #DDD;margin-top:20px;padding-top:15px}.repository.release #release-list>li{list-style:none}.repository.release #release-list>li .detail,.repository.release #release-list>li .meta{padding-top:30px;padding-bottom:40px}.repository.release #release-list>li .meta{text-align:right;position:relative}.repository.release #release-list>li .meta .tag:not(.icon){display:block;margin-top:15px}.repository.release #release-list>li .meta .commit{display:block;margin-top:10px}.repository.release #release-list>li .detail{border-left:1px solid #DDD}.repository.release #release-list>li .detail .author img{margin-bottom:-3px}.repository.release #release-list>li .detail .download{margin-top:20px}.repository.release #release-list>li .detail .download>a .octicon{margin-left:5px;margin-right:5px}.repository.release #release-list>li .detail .download .list{padding-left:0;border-top:1px solid #eee}.repository.release #release-list>li .detail .download .list li{list-style:none;display:block;padding-top:8px;padding-bottom:8px;border-bottom:1px solid #eee}.repository.release #release-list>li .detail .dot{width:9px;height:9px;background-color:#ccc;z-index:999;position:absolute;display:block;left:-5px;top:40px;border-radius:6px;border:1px solid #FFF}.repository.new.release .target{min-width:500px}.repository.new.release .target #tag-name{margin-top:-4px}.repository.new.release .target .at{margin-left:-5px;margin-right:5px}.repository.new.release .target .dropdown.icon{margin:0;padding-top:3px}.repository.new.release .target .selection.dropdown{padding-top:10px;padding-bottom:10px}.repository.new.release .prerelease.field{margin-bottom:0}@media only screen and (max-width:438px){.repository.new.release .field button,.repository.new.release .field input{width:100%}}@media only screen and (max-width:768px){.repository.new.release .field button{margin-bottom:1em}}.repository.forks .list{margin-top:0}.repository.forks .list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px solid #DDD}.repository.forks .list .item .ui.avatar{float:left;margin-right:5px}.repository.forks .list .item .link{padding-top:5px}.repository.wiki.start .ui.segment{padding-top:70px;padding-bottom:100px}.repository.wiki.start .ui.segment .mega-octicon{font-size:48px}.repository.wiki.new .CodeMirror .CodeMirror-code{font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.repository.wiki.new .CodeMirror .CodeMirror-code .cm-comment{background:inherit}.repository.wiki.new .editor-preview{background-color:#fff}.repository.wiki.view .choose.page{margin-top:-5px}.repository.wiki.view .ui.sub.header{text-transform:none}.repository.wiki.view>.markdown{padding:15px 30px}.repository.wiki.view>.markdown h1:first-of-type,.repository.wiki.view>.markdown h2:first-of-type,.repository.wiki.view>.markdown h3:first-of-type,.repository.wiki.view>.markdown h4:first-of-type,.repository.wiki.view>.markdown h5:first-of-type,.repository.wiki.view>.markdown h6:first-of-type{margin-top:0}@media only screen and (max-width:767px){.repository.wiki .dividing.header .stackable.grid .button{margin-top:2px;margin-bottom:2px}}.repository.settings.collaboration .collaborator.list{padding:0}.repository.settings.collaboration .collaborator.list>.item{margin:0;line-height:2em}.repository.settings.collaboration .collaborator.list>.item:not(:last-child){border-bottom:1px solid #DDD}.repository.settings.collaboration #repo-collab-form #search-user-box .results{left:7px}.repository.settings.collaboration #repo-collab-form .ui.button{margin-left:5px;margin-top:-3px}.repository.settings.branches .protected-branches .selection.dropdown{width:300px}.repository.settings.branches .protected-branches .item{border:1px solid #eaeaea;padding:10px 15px}.repository.settings.branches .protected-branches .item:not(:last-child){border-bottom:0}.repository.settings.branches .branch-protection .help{margin-left:26px;padding-top:0}.repository.settings.branches .branch-protection .fields{margin-left:20px;display:block}.repository.settings.branches .branch-protection .whitelist{margin-left:26px}.repository.settings.branches .branch-protection .whitelist .dropdown img{display:inline-block}.repository.settings.webhook .events .column{padding-bottom:0}.repository.settings.webhook .events .help{font-size:13px;margin-left:26px;padding-top:0}.repository .ui.attached.isSigned.isVerified:not(.positive){border-left:1px solid #A3C293;border-right:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified.top:not(.positive){border-top:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified:not(.positive):last-child{border-bottom:1px solid #A3C293}.repository .ui.segment.sub-menu{padding:7px;line-height:0}.repository .ui.segment.sub-menu .list{width:100%;display:flex}.repository .ui.segment.sub-menu .list .item{width:100%;border-radius:3px}.repository .ui.segment.sub-menu .list .item a{color:#000}.repository .ui.segment.sub-menu .list .item a:hover{color:#666}.repository .ui.segment.sub-menu .list .item.active{background:rgba(0,0,0,.05)}.repository .segment.reactions.dropdown .menu,.repository .select-reaction.dropdown .menu{right:0!important;left:auto!important}.repository .segment.reactions.dropdown .menu>.header,.repository .select-reaction.dropdown .menu>.header{margin:.75rem 0 .5rem}.repository .segment.reactions.dropdown .menu>.item,.repository .select-reaction.dropdown .menu>.item{float:left;padding:.5rem .5rem!important}.repository .segment.reactions.dropdown .menu>.item img.emoji,.repository .select-reaction.dropdown .menu>.item img.emoji{margin-right:0}.repository .segment.reactions{padding:.3em 1em}.repository .segment.reactions .ui.label{padding:.4em}.repository .segment.reactions .ui.label.disabled{cursor:default}.repository .segment.reactions .ui.label>img{height:1.5em!important}.repository .segment.reactions .select-reaction{float:none}.repository .segment.reactions .select-reaction:not(.active) a{display:none}.repository .segment.reactions:hover .select-reaction a{display:block}.user-cards .list{padding:0}.user-cards .list .item{list-style:none;width:32%;margin:10px 10px 10px 0;padding-bottom:14px;float:left}.user-cards .list .item .avatar{width:48px;height:48px;float:left;display:block;margin-right:10px}.user-cards .list .item .name{margin-top:0;margin-bottom:0;font-weight:400}.user-cards .list .item .meta{margin-top:5px}#search-repo-box .results .result .image,#search-user-box .results .result .image{float:left;margin-right:8px;width:2em;height:2em}#search-repo-box .results .result .content,#search-user-box .results .result .content{margin:6px 0}#issue-filters.hide{display:none}#issue-actions{margin-top:-1rem!important}#issue-actions.hide{display:none}.ui.checkbox.issue-checkbox{vertical-align:middle}.issue.list{list-style:none}.issue.list>.item{padding-top:15px;padding-bottom:10px;border-bottom:1px dashed #AAA}.issue.list>.item .title{color:#444;font-size:15px;font-weight:700;margin:0 6px}.issue.list>.item .title:hover{color:#000}.issue.list>.item .comment{padding-right:10px;color:#666}.issue.list>.item .desc{padding-top:5px;color:#999}.issue.list>.item .desc .checklist{padding-left:5px}.issue.list>.item .desc .checklist .progress-bar{margin-left:2px;width:80px;height:6px;display:inline-block;background-color:#eee;overflow:hidden;border-radius:3px;vertical-align:2px!important}.issue.list>.item .desc .checklist .progress-bar .progress{background-color:#ccc;display:block;height:100%}.issue.list>.item .desc a.milestone{padding-left:5px;color:#999!important}.issue.list>.item .desc a.milestone:hover{color:#000!important}.issue.list>.item .desc .assignee{margin-top:-5px;margin-right:5px}.issue.list>.item .desc .overdue{color:red}.page.buttons{padding-top:15px}.ui.form .dropzone{width:100%;margin-bottom:10px;border:2px dashed #0087F7;box-shadow:none!important}.ui.form .dropzone .dz-error-message{top:140px}.settings .content{margin-top:2px}.settings .content .segment,.settings .content>.header{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.settings .list>.item .green{color:#21BA45}.settings .list>.item:not(:first-child){border-top:1px solid #eaeaea;padding:1rem;margin:15px -1rem -1rem -1rem}.settings .list>.item>.mega-octicon{display:table-cell}.settings .list>.item>.mega-octicon+.content{display:table-cell;padding:0 0 0 .5em;vertical-align:top}.settings .list>.item .info{margin-top:10px}.settings .list>.item .info .tab.segment{border:none;padding:10px 0 0}.settings .list.key .meta{padding-top:5px;color:#666}.settings .list.email>.item:not(:first-child){min-height:60px}.settings .list.collaborator>.item{padding:0}.ui.vertical.menu .header.item{font-size:1.1em;background:#f0f0f0}.edit-label.modal .form .column,.new-label.segment .form .column{padding-right:0}.edit-label.modal .form .buttons,.new-label.segment .form .buttons{margin-left:auto;padding-top:15px}.edit-label.modal .form .color.picker.column,.new-label.segment .form .color.picker.column{width:auto}.edit-label.modal .form .color.picker.column .color-picker,.new-label.segment .form .color.picker.column .color-picker{height:35px;width:auto;padding-left:30px}.edit-label.modal .form .minicolors-swatch.minicolors-sprite,.new-label.segment .form .minicolors-swatch.minicolors-sprite{top:10px;left:10px;width:15px;height:15px}.edit-label.modal .form .precolors,.new-label.segment .form .precolors{padding-left:0;padding-right:0;margin:3px 10px auto 10px;width:120px}.edit-label.modal .form .precolors .color,.new-label.segment .form .precolors .color{float:left;width:15px;height:15px}#avatar-arrow:after,#avatar-arrow:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}#avatar-arrow:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}#avatar-arrow:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}#delete-repo-modal .ui.message,#transfer-repo-modal .ui.message{width:100%!important}.tab-size-1{-moz-tab-size:1!important;-o-tab-size:1!important;tab-size:1!important}.tab-size-2{-moz-tab-size:2!important;-o-tab-size:2!important;tab-size:2!important}.tab-size-3{-moz-tab-size:3!important;-o-tab-size:3!important;tab-size:3!important}.tab-size-4{-moz-tab-size:4!important;-o-tab-size:4!important;tab-size:4!important}.tab-size-5{-moz-tab-size:5!important;-o-tab-size:5!important;tab-size:5!important}.tab-size-6{-moz-tab-size:6!important;-o-tab-size:6!important;tab-size:6!important}.tab-size-7{-moz-tab-size:7!important;-o-tab-size:7!important;tab-size:7!important}.tab-size-8{-moz-tab-size:8!important;-o-tab-size:8!important;tab-size:8!important}.tab-size-9{-moz-tab-size:9!important;-o-tab-size:9!important;tab-size:9!important}.tab-size-10{-moz-tab-size:10!important;-o-tab-size:10!important;tab-size:10!important}.tab-size-11{-moz-tab-size:11!important;-o-tab-size:11!important;tab-size:11!important}.tab-size-12{-moz-tab-size:12!important;-o-tab-size:12!important;tab-size:12!important}.tab-size-13{-moz-tab-size:13!important;-o-tab-size:13!important;tab-size:13!important}.tab-size-14{-moz-tab-size:14!important;-o-tab-size:14!important;tab-size:14!important}.tab-size-15{-moz-tab-size:15!important;-o-tab-size:15!important;tab-size:15!important}.tab-size-16{-moz-tab-size:16!important;-o-tab-size:16!important;tab-size:16!important}.stats-table{display:table;width:100%}.stats-table .table-cell{display:table-cell}.stats-table .table-cell.tiny{height:.5em}tbody.commit-list{vertical-align:baseline}.commit-body{white-space:pre-wrap}@media only screen and (max-width:767px){.ui.stackable.menu.mobile--margin-between-items>.item{margin-top:5px;margin-bottom:5px}.ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0}}#topic_edit{margin-top:5px}#repo-topics{margin-top:5px}.repo-topic{cursor:pointer}@media only screen and (max-width:768px){.new-dependency-drop-list{width:100%}}#manage_topic{font-size:12px}.label+#manage_topic{margin-left:5px}.repo-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap}.repo-header .repo-buttons{display:flex;align-items:center}.repo-buttons .disabled-repo-button .label{opacity:.5}.repo-buttons .disabled-repo-button a.button{opacity:.5;cursor:not-allowed}.repo-buttons .disabled-repo-button a.button:hover{background:0 0!important;color:rgba(0,0,0,.6)!important;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset!important}.repo-buttons .ui.labeled.button>.label{border-left:none!important;margin:0!important}.CodeMirror{font:14px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.CodeMirror.cm-s-default{border-radius:3px;padding:0!important}.CodeMirror .cm-comment{background:inherit!important}.repository.file.editor .tab[data-tab=write]{padding:0!important}.repository.file.editor .tab[data-tab=write] .editor-toolbar{border:none!important}.repository.file.editor .tab[data-tab=write] .CodeMirror{border-left:none;border-right:none;border-bottom:none}.organization{padding-top:15px}.organization .head .ui.header .text{vertical-align:middle;font-size:1.6rem;margin-left:15px}.organization .head .ui.header .ui.right{margin-top:5px}.organization.new.org form{margin:auto}.organization.new.org form .ui.message{text-align:center}@media only screen and (min-width:768px){.organization.new.org form{width:800px!important}.organization.new.org form .header{padding-left:280px!important}.organization.new.org form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.organization.new.org form .help{margin-left:265px!important}.organization.new.org form .optional .title{margin-left:250px!important}.organization.new.org form input,.organization.new.org form textarea{width:50%!important}}@media only screen and (max-width:767px){.organization.new.org form .optional .title{margin-left:15px}.organization.new.org form .inline.field>label{display:block}}.organization.new.org form .header{padding-left:0!important;text-align:center}.organization.options input{min-width:300px}.organization.profile #org-avatar{width:100px;height:100px;margin-right:15px}.organization.profile #org-info .ui.header{font-size:36px;margin-bottom:0}.organization.profile #org-info .desc{font-size:16px;margin-bottom:10px}.organization.profile #org-info .meta .item{display:inline-block;margin-right:10px}.organization.profile #org-info .meta .item .icon{margin-right:5px}.organization.profile .ui.top.header .ui.right{margin-top:0}.organization.profile .teams .item{padding:10px 15px}.organization.profile .members .ui.avatar,.organization.teams .members .ui.avatar{width:48px;height:48px;margin-right:5px}.organization.invite #invite-box{margin:auto;margin-top:50px;width:500px!important}.organization.invite #invite-box #search-user-box input{margin-left:0;width:300px}.organization.invite #invite-box .ui.button{margin-left:5px;margin-top:-3px}.organization.members .list .item{margin-left:0;margin-right:0;border-bottom:1px solid #eee}.organization.members .list .item .ui.avatar{width:48px;height:48px}.organization.members .list .item .meta{line-height:24px}.organization.teams .detail .item{padding:10px 15px}.organization.teams .detail .item:not(:last-child){border-bottom:1px solid #eee}.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #DDD}.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}.user:not(.icon){padding-top:15px}.user.profile .ui.card .username{display:block}.user.profile .ui.card .extra.content{padding:0}.user.profile .ui.card .extra.content ul{margin:0;padding:0}.user.profile .ui.card .extra.content ul li{padding:10px;list-style:none}.user.profile .ui.card .extra.content ul li:not(:last-child){border-bottom:1px solid #eaeaea}.user.profile .ui.card .extra.content ul li .octicon{margin-left:1px;margin-right:5px}.user.profile .ui.card .extra.content ul li.follow .ui.button{width:100%}@media only screen and (max-width:768px){.user.profile .ui.card #profile-avatar{height:250px;overflow:hidden}.user.profile .ui.card #profile-avatar img{max-height:768px;max-width:768px}}@media only screen and (max-width:768px){.user.profile .ui.card{width:100%}}.user.profile .ui.repository.list{margin-top:25px}.user.profile #loading-heatmap{margin-bottom:1em}.user.followers .header.name{font-size:20px;line-height:24px;vertical-align:middle}.user.followers .follow .ui.button{padding:8px 15px}.user.notification .octicon{float:left;font-size:2em}.user.notification .content{float:left;margin-left:7px}.user.notification table form{display:inline-block}.user.notification table button{padding:3px 3px 3px 5px}.user.notification table tr{cursor:pointer}.user.notification .octicon.green{color:#21ba45}.user.notification .octicon.red{color:#d01919}.user.notification .octicon.purple{color:#a333c8}.user.notification .octicon.blue{color:#2185d0}.user.link-account:not(.icon){padding-top:15px;padding-bottom:5px}.user.settings .iconFloat{float:left}.dashboard{padding-top:15px}.dashboard.feeds .context.user.menu,.dashboard.issues .context.user.menu{z-index:101;min-width:200px}.dashboard.feeds .context.user.menu .ui.header,.dashboard.issues .context.user.menu .ui.header{font-size:1rem;text-transform:none}.dashboard.feeds .filter.menu .item,.dashboard.issues .filter.menu .item{text-align:left}.dashboard.feeds .filter.menu .item .text,.dashboard.issues .filter.menu .item .text{height:16px;vertical-align:middle}.dashboard.feeds .filter.menu .item .text.truncate,.dashboard.issues .filter.menu .item .text.truncate{width:85%}.dashboard.feeds .filter.menu .item .floating.label,.dashboard.issues .filter.menu .item .floating.label{top:7px;left:90%;width:15%}@media only screen and (max-width:768px){.dashboard.feeds .filter.menu .item .floating.label,.dashboard.issues .filter.menu .item .floating.label{top:10px;left:auto;width:auto;right:13px}}.dashboard.feeds .filter.menu .jump.item,.dashboard.issues .filter.menu .jump.item{margin:1px;padding-right:0}.dashboard.feeds .filter.menu .menu,.dashboard.issues .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}@media only screen and (max-width:768px){.dashboard.feeds .filter.menu,.dashboard.issues .filter.menu{width:100%}}.dashboard.feeds .right.stackable.menu>.item.active,.dashboard.issues .right.stackable.menu>.item.active{color:#d9453d}.dashboard .dashboard-repos{margin:0 1px}.dashboard .dashboard-navbar{width:100vw;padding:0 .5rem}.feeds .news>.ui.grid{margin-left:auto;margin-right:auto}.feeds .news .ui.avatar{margin-top:13px}.feeds .news .time-since{font-size:13px}.feeds .news .issue.title{width:80%}.feeds .news .push.news .content ul{font-size:13px;list-style:none;padding-left:10px}.feeds .news .push.news .content ul img{margin-bottom:-2px}.feeds .news .push.news .content ul .text.truncate{width:80%;margin-bottom:-5px}.feeds .news .commit-id{font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.feeds .news code{padding:1px;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px;word-break:break-all}.feeds .list .header .ui.label{margin-top:-4px;padding:4px 5px;font-weight:400}.feeds .list .header .plus.icon{margin-top:5px}.feeds .list ul{list-style:none;margin:0;padding-left:0}.feeds .list ul li:not(:last-child){border-bottom:1px solid #EAEAEA}.feeds .list ul li.private{background-color:#fcf8e9}.feeds .list ul li a{padding:6px 1.2em;display:block}.feeds .list ul li a .octicon{color:#888}.feeds .list ul li a .octicon.rear{font-size:15px}.feeds .list ul li a .star-num{font-size:12px}.feeds .list .repo-owner-name-list .item-name{max-width:70%;margin-bottom:-4px}.feeds .list #collaborative-repo-list .owner-and-repo{max-width:80%;margin-bottom:-5px}.feeds .list #collaborative-repo-list .owner-name{max-width:120px;margin-bottom:-5px}.admin{padding-top:15px}.admin .table.segment{padding:0;font-size:13px}.admin .table.segment:not(.striped){padding-top:5px}.admin .table.segment:not(.striped) thead th:last-child{padding-right:5px!important}.admin .table.segment th{padding-top:5px;padding-bottom:5px}.admin .table.segment:not(.select) td:first-of-type,.admin .table.segment:not(.select) th:first-of-type{padding-left:15px!important}.admin .ui.header,.admin .ui.segment{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.admin.user .email{max-width:200px}.admin dl.admin-dl-horizontal{padding:20px;margin:0}.admin dl.admin-dl-horizontal dd{margin-left:275px}.admin dl.admin-dl-horizontal dt{font-weight:bolder;float:left;width:285px;clear:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.admin.config #test-mail-btn{margin-left:5px}.explore{padding-top:15px}.explore .navbar{justify-content:center;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}.explore .navbar .octicon{width:16px;text-align:center;margin-right:5px}.ui.repository.list .item{padding-bottom:25px}.ui.repository.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.repository.list .item .ui.header{font-size:1.5rem;padding-bottom:10px}.ui.repository.list .item .ui.header .name{word-break:break-all}.ui.repository.list .item .ui.header .metas{color:#888;font-size:14px;font-weight:400}.ui.repository.list .item .ui.header .metas span:not(:last-child){margin-right:5px}.ui.repository.list .item .time{font-size:12px;color:grey}.ui.repository.list .item .ui.tags{margin-bottom:1em}.ui.repository.branches .time{font-size:12px;color:grey}.ui.user.list .item{padding-bottom:25px}.ui.user.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.user.list .item .ui.avatar.image{width:40px;height:40px}.ui.user.list .item .description{margin-top:5px}.ui.user.list .item .description .octicon:not(:first-child){margin-left:5px}.ui.user.list .item .description a{color:#333}.ui.user.list .item .description a:hover{text-decoration:underline}.ui.button.add-code-comment{font-size:14px;height:16px;padding:2px 0 0;position:relative;width:16px;z-index:5;float:left;margin:-2px -10px -2px -20px;opacity:0;transition:transform .1s ease-in-out;transform:scale(1,1)}.ui.button.add-code-comment:hover{transform:scale(1.2,1.2)}.focus-lines-new .ui.button.add-code-comment.add-code-comment-right,.focus-lines-old .ui.button.add-code-comment.add-code-comment-left{opacity:1}.comment-code-cloud{padding:4px;margin:0 auto;position:relative;border:1px solid #f1f1f1;margin-top:13px;margin-right:10px;margin-bottom:5px}.comment-code-cloud:before{content:" ";width:0;height:0;border-left:13px solid transparent;border-right:13px solid transparent;border-bottom:13px solid #f1f1f1;left:20px;position:absolute;top:-13px}.comment-code-cloud .attached.tab{border:none;padding:0;margin:0}.comment-code-cloud .attached.tab.markdown{padding:1em;min-height:168px}.comment-code-cloud .attached.header{padding:.1rem 1rem}.comment-code-cloud .right.menu.options .item{padding:.85714286em .442857em;cursor:pointer}.comment-code-cloud .ui.form textarea{border:0}.comment-code-cloud .ui.attached.tabular.menu{background:#f7f7f7;border:1px solid #d4d4d5;padding-top:5px;padding-left:5px;margin-top:0}.comment-code-cloud .footer{border-top:1px solid #f1f1f1;margin-top:10px}.comment-code-cloud .footer .markdown-info{display:inline-block;margin:5px 0;font-size:12px;color:rgba(0,0,0,.6)}.comment-code-cloud .footer .ui.right.floated{padding-top:6px}.comment-code-cloud .footer:after{clear:both;content:"";display:block}.comment-code-cloud button.comment-form-reply{margin:.5em .5em .5em 4.5em}.comment-code-cloud form.comment-form-reply{margin:0 0 0 4em}.file-comment{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.87)} \ No newline at end of file +.tribute-container{box-shadow:0 1px 3px 1px #c7c7c7}.tribute-container ul{background:#fff}.tribute-container li{padding:8px 12px;border-bottom:1px solid #dcdcdc}.tribute-container li img{display:inline-block;vertical-align:middle;width:28px;height:28px;margin-right:5px}.tribute-container li span.fullname{font-weight:400;font-size:.8rem;margin-left:3px}.tribute-container li.highlight,.tribute-container li:hover{background:#2185D0;color:#fff}.emoji{width:1.5em;height:1.5em;display:inline-block;background-size:contain}.ui.label .emoji{height:1.2em!important}@font-face{font-family:Lato;src:url(../vendor/assets/lato-fonts/lato-regular.eot);src:url(../vendor/assets/lato-fonts/lato-regular.eot?#iefix) format('embedded-opentype'),url(../vendor/assets/lato-fonts/lato-regular.woff2) format('woff2'),url(../vendor/assets/lato-fonts/lato-regular.woff) format('woff'),url(../vendor/assets/lato-fonts/lato-regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:Lato;src:url(../vendor/assets/lato-fonts/lato-italic.eot);src:url(../vendor/assets/lato-fonts/lato-italic.eot?#iefix) format('embedded-opentype'),url(../vendor/assets/lato-fonts/lato-italic.woff2) format('woff2'),url(../vendor/assets/lato-fonts/lato-italic.woff) format('woff'),url(../vendor/assets/lato-fonts/lato-italic.ttf) format('truetype');font-weight:400;font-style:italic}@font-face{font-family:Lato;src:url(../vendor/assets/lato-fonts/lato-bold.eot);src:url(../vendor/assets/lato-fonts/lato-bold.eot?#iefix) format('embedded-opentype'),url(../vendor/assets/lato-fonts/lato-bold.woff2) format('woff2'),url(../vendor/assets/lato-fonts/lato-bold.woff) format('woff'),url(../vendor/assets/lato-fonts/lato-bold.ttf) format('truetype');font-weight:700;font-style:normal}@font-face{font-family:Lato;src:url(../vendor/assets/lato-fonts/lato-bolditalic.eot);src:url(../vendor/assets/lato-fonts/lato-bolditalic.eot?#iefix) format('embedded-opentype'),url(../vendor/assets/lato-fonts/lato-bolditalic.woff2) format('woff2'),url(../vendor/assets/lato-fonts/lato-bolditalic.woff) format('woff'),url(../vendor/assets/lato-fonts/lato-bolditalic.ttf) format('truetype');font-weight:700;font-style:italic}@font-face{font-family:'Yu Gothic';src:local('Yu Gothic Medium');font-weight:400}@font-face{font-family:'Yu Gothic';src:local('Yu Gothic Bold');font-weight:700}textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}h1,h2,h3,h4,h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.home .hero h1,.home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.ui.accordion .title:not(.ui),.ui.button,.ui.card>.content>.header.ui.card>.content>.header,.ui.category.search>.results .category>.name,.ui.form input:not([type]),.ui.form input[type=date],.ui.form input[type=datetime-local],.ui.form input[type=email],.ui.form input[type=file],.ui.form input[type=number],.ui.form input[type=password],.ui.form input[type=search],.ui.form input[type=tel],.ui.form input[type=text],.ui.form input[type=time],.ui.form input[type=url],.ui.header,.ui.input input,.ui.input>input,.ui.items>.item>.content>.header,.ui.language>.menu>.item,.ui.list .list>.item .header,.ui.list>.item .header,.ui.menu,.ui.message .header,.ui.modal>.header,.ui.popup>.header,.ui.search>.results .result .title,.ui.search>.results>.message .header,.ui.statistic>.label,.ui.statistic>.value,.ui.statistics .statistic>.label,.ui.statistics .statistic>.value,.ui.steps .step .title,.ui.text.container,body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}body{background-color:#fff;overflow-y:auto;-webkit-font-smoothing:antialiased;display:flex;flex-direction:column}:lang(ja) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}:lang(ja) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}:lang(ja) h1,:lang(ja) h2,:lang(ja) h3,:lang(ja) h4,:lang(ja) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}:lang(ja) .home .hero h1,:lang(ja) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}.ui.language>.menu>.item:lang(ja),:lang(ja) .ui.accordion .title:not(.ui),:lang(ja) .ui.button,:lang(ja) .ui.card>.content>.header.ui.card>.content>.header,:lang(ja) .ui.category.search>.results .category>.name,:lang(ja) .ui.form input:not([type]),:lang(ja) .ui.form input[type=date],:lang(ja) .ui.form input[type=datetime-local],:lang(ja) .ui.form input[type=email],:lang(ja) .ui.form input[type=file],:lang(ja) .ui.form input[type=number],:lang(ja) .ui.form input[type=password],:lang(ja) .ui.form input[type=search],:lang(ja) .ui.form input[type=tel],:lang(ja) .ui.form input[type=text],:lang(ja) .ui.form input[type=time],:lang(ja) .ui.form input[type=url],:lang(ja) .ui.header,:lang(ja) .ui.input input,:lang(ja) .ui.input>input,:lang(ja) .ui.items>.item>.content>.header,:lang(ja) .ui.list .list>.item .header,:lang(ja) .ui.list>.item .header,:lang(ja) .ui.menu,:lang(ja) .ui.message .header,:lang(ja) .ui.modal>.header,:lang(ja) .ui.popup>.header,:lang(ja) .ui.search>.results .result .title,:lang(ja) .ui.search>.results>.message .header,:lang(ja) .ui.statistic>.label,:lang(ja) .ui.statistic>.value,:lang(ja) .ui.statistics .statistic>.label,:lang(ja) .ui.statistics .statistic>.value,:lang(ja) .ui.steps .step .title,:lang(ja) .ui.text.container,:lang(ja) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Hiragino Kaku Gothic ProN','Yu Gothic','Source Han Sans JP','Noto Sans CJK JP','Droid Sans Japanese',Meiryo,'MS PGothic',sans-serif}:lang(zh-CN) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}:lang(zh-CN) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}:lang(zh-CN) h1,:lang(zh-CN) h2,:lang(zh-CN) h3,:lang(zh-CN) h4,:lang(zh-CN) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}:lang(zh-CN) .home .hero h1,:lang(zh-CN) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}.ui.language>.menu>.item:lang(zh-CN),:lang(zh-CN) .ui.accordion .title:not(.ui),:lang(zh-CN) .ui.button,:lang(zh-CN) .ui.card>.content>.header.ui.card>.content>.header,:lang(zh-CN) .ui.category.search>.results .category>.name,:lang(zh-CN) .ui.form input:not([type]),:lang(zh-CN) .ui.form input[type=date],:lang(zh-CN) .ui.form input[type=datetime-local],:lang(zh-CN) .ui.form input[type=email],:lang(zh-CN) .ui.form input[type=file],:lang(zh-CN) .ui.form input[type=number],:lang(zh-CN) .ui.form input[type=password],:lang(zh-CN) .ui.form input[type=search],:lang(zh-CN) .ui.form input[type=tel],:lang(zh-CN) .ui.form input[type=text],:lang(zh-CN) .ui.form input[type=time],:lang(zh-CN) .ui.form input[type=url],:lang(zh-CN) .ui.header,:lang(zh-CN) .ui.input input,:lang(zh-CN) .ui.input>input,:lang(zh-CN) .ui.items>.item>.content>.header,:lang(zh-CN) .ui.list .list>.item .header,:lang(zh-CN) .ui.list>.item .header,:lang(zh-CN) .ui.menu,:lang(zh-CN) .ui.message .header,:lang(zh-CN) .ui.modal>.header,:lang(zh-CN) .ui.popup>.header,:lang(zh-CN) .ui.search>.results .result .title,:lang(zh-CN) .ui.search>.results>.message .header,:lang(zh-CN) .ui.statistic>.label,:lang(zh-CN) .ui.statistic>.value,:lang(zh-CN) .ui.statistics .statistic>.label,:lang(zh-CN) .ui.statistics .statistic>.value,:lang(zh-CN) .ui.steps .step .title,:lang(zh-CN) .ui.text.container,:lang(zh-CN) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang SC','Hiragino Sans GB','Source Han Sans CN','Source Han Sans SC','Noto Sans CJK SC','Microsoft YaHei','Heiti SC',SimHei,sans-serif}:lang(zh-TW) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}:lang(zh-TW) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}:lang(zh-TW) h1,:lang(zh-TW) h2,:lang(zh-TW) h3,:lang(zh-TW) h4,:lang(zh-TW) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}:lang(zh-TW) .home .hero h1,:lang(zh-TW) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}.ui.language>.menu>.item:lang(zh-TW),:lang(zh-TW) .ui.accordion .title:not(.ui),:lang(zh-TW) .ui.button,:lang(zh-TW) .ui.card>.content>.header.ui.card>.content>.header,:lang(zh-TW) .ui.category.search>.results .category>.name,:lang(zh-TW) .ui.form input:not([type]),:lang(zh-TW) .ui.form input[type=date],:lang(zh-TW) .ui.form input[type=datetime-local],:lang(zh-TW) .ui.form input[type=email],:lang(zh-TW) .ui.form input[type=file],:lang(zh-TW) .ui.form input[type=number],:lang(zh-TW) .ui.form input[type=password],:lang(zh-TW) .ui.form input[type=search],:lang(zh-TW) .ui.form input[type=tel],:lang(zh-TW) .ui.form input[type=text],:lang(zh-TW) .ui.form input[type=time],:lang(zh-TW) .ui.form input[type=url],:lang(zh-TW) .ui.header,:lang(zh-TW) .ui.input input,:lang(zh-TW) .ui.input>input,:lang(zh-TW) .ui.items>.item>.content>.header,:lang(zh-TW) .ui.list .list>.item .header,:lang(zh-TW) .ui.list>.item .header,:lang(zh-TW) .ui.menu,:lang(zh-TW) .ui.message .header,:lang(zh-TW) .ui.modal>.header,:lang(zh-TW) .ui.popup>.header,:lang(zh-TW) .ui.search>.results .result .title,:lang(zh-TW) .ui.search>.results>.message .header,:lang(zh-TW) .ui.statistic>.label,:lang(zh-TW) .ui.statistic>.value,:lang(zh-TW) .ui.statistics .statistic>.label,:lang(zh-TW) .ui.statistics .statistic>.value,:lang(zh-TW) .ui.steps .step .title,:lang(zh-TW) .ui.text.container,:lang(zh-TW) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang TC','Hiragino Sans TC','Source Han Sans TW','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU,sans-serif}:lang(zh-HK) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}:lang(zh-HK) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}:lang(zh-HK) h1,:lang(zh-HK) h2,:lang(zh-HK) h3,:lang(zh-HK) h4,:lang(zh-HK) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}:lang(zh-HK) .home .hero h1,:lang(zh-HK) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}.ui.language>.menu>.item:lang(zh-HK),:lang(zh-HK) .ui.accordion .title:not(.ui),:lang(zh-HK) .ui.button,:lang(zh-HK) .ui.card>.content>.header.ui.card>.content>.header,:lang(zh-HK) .ui.category.search>.results .category>.name,:lang(zh-HK) .ui.form input:not([type]),:lang(zh-HK) .ui.form input[type=date],:lang(zh-HK) .ui.form input[type=datetime-local],:lang(zh-HK) .ui.form input[type=email],:lang(zh-HK) .ui.form input[type=file],:lang(zh-HK) .ui.form input[type=number],:lang(zh-HK) .ui.form input[type=password],:lang(zh-HK) .ui.form input[type=search],:lang(zh-HK) .ui.form input[type=tel],:lang(zh-HK) .ui.form input[type=text],:lang(zh-HK) .ui.form input[type=time],:lang(zh-HK) .ui.form input[type=url],:lang(zh-HK) .ui.header,:lang(zh-HK) .ui.input input,:lang(zh-HK) .ui.input>input,:lang(zh-HK) .ui.items>.item>.content>.header,:lang(zh-HK) .ui.list .list>.item .header,:lang(zh-HK) .ui.list>.item .header,:lang(zh-HK) .ui.menu,:lang(zh-HK) .ui.message .header,:lang(zh-HK) .ui.modal>.header,:lang(zh-HK) .ui.popup>.header,:lang(zh-HK) .ui.search>.results .result .title,:lang(zh-HK) .ui.search>.results>.message .header,:lang(zh-HK) .ui.statistic>.label,:lang(zh-HK) .ui.statistic>.value,:lang(zh-HK) .ui.statistics .statistic>.label,:lang(zh-HK) .ui.statistics .statistic>.value,:lang(zh-HK) .ui.steps .step .title,:lang(zh-HK) .ui.text.container,:lang(zh-HK) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'PingFang HK','Hiragino Sans TC','Source Han Sans HK','Source Han Sans TC','Noto Sans CJK TC','Microsoft JhengHei','Heiti TC',PMingLiU_HKSCS,PMingLiU,sans-serif}:lang(ko) textarea{font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}:lang(ko) .markdown:not(code){font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}:lang(ko) h1,:lang(ko) h2,:lang(ko) h3,:lang(ko) h4,:lang(ko) h5{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}:lang(ko) .home .hero h1,:lang(ko) .home .hero h2{font-family:'PT Sans Narrow',Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}.ui.language>.menu>.item:lang(ko),:lang(ko) .ui.accordion .title:not(.ui),:lang(ko) .ui.button,:lang(ko) .ui.card>.content>.header.ui.card>.content>.header,:lang(ko) .ui.category.search>.results .category>.name,:lang(ko) .ui.form input:not([type]),:lang(ko) .ui.form input[type=date],:lang(ko) .ui.form input[type=datetime-local],:lang(ko) .ui.form input[type=email],:lang(ko) .ui.form input[type=file],:lang(ko) .ui.form input[type=number],:lang(ko) .ui.form input[type=password],:lang(ko) .ui.form input[type=search],:lang(ko) .ui.form input[type=tel],:lang(ko) .ui.form input[type=text],:lang(ko) .ui.form input[type=time],:lang(ko) .ui.form input[type=url],:lang(ko) .ui.header,:lang(ko) .ui.input input,:lang(ko) .ui.input>input,:lang(ko) .ui.items>.item>.content>.header,:lang(ko) .ui.list .list>.item .header,:lang(ko) .ui.list>.item .header,:lang(ko) .ui.menu,:lang(ko) .ui.message .header,:lang(ko) .ui.modal>.header,:lang(ko) .ui.popup>.header,:lang(ko) .ui.search>.results .result .title,:lang(ko) .ui.search>.results>.message .header,:lang(ko) .ui.statistic>.label,:lang(ko) .ui.statistic>.value,:lang(ko) .ui.statistics .statistic>.label,:lang(ko) .ui.statistics .statistic>.value,:lang(ko) .ui.steps .step .title,:lang(ko) .ui.text.container,:lang(ko) body{font-family:Lato,-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial,'Apple SD Gothic Neo',NanumBarunGothic,'Malgun Gothic',Gulim,Dotum,'Nanum Gothic','Source Han Sans KR','Noto Sans CJK KR',sans-serif}img{border-radius:3px}table{border-collapse:collapse}a{cursor:pointer}.rounded{border-radius:.28571429rem!important}code,pre{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}code.raw,pre.raw{padding:7px 12px;margin:10px 0;background-color:#f8f8f8;border:1px solid #ddd;border-radius:3px;font-size:13px;line-height:1.5;overflow:auto}code.wrap,pre.wrap{white-space:pre-wrap;word-break:break-all;overflow-wrap:break-word;word-wrap:break-word}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.full.height{flex-grow:1;padding-bottom:80px}.following.bar{z-index:900;left:0;margin:0!important}.following.bar.light{background-color:#fff;border-bottom:1px solid #DDD;box-shadow:0 2px 3px rgba(0,0,0,.04)}.following.bar .column .menu{margin-top:0}.following.bar .top.menu a.item.brand{padding-left:0}.following.bar .brand .ui.mini.image{width:30px}.following.bar .top.menu .dropdown.item.active,.following.bar .top.menu .dropdown.item:hover,.following.bar .top.menu a.item:hover{background-color:transparent}.following.bar .top.menu a.item:hover{color:rgba(0,0,0,.45)}.following.bar .top.menu .menu{z-index:900}.following.bar .octicon{margin-right:.75em}.following.bar .octicon.fitted{margin-right:0}.following.bar .searchbox{background-color:#f4f4f4!important}.following.bar .searchbox:focus{background-color:#e9e9e9!important}.following.bar .text .octicon{width:16px;text-align:center}.following.bar #navbar{width:100vw;min-height:52px;padding:0 .5rem}.following.bar #navbar .brand{margin:0}@media only screen and (max-width:767px){.following.bar #navbar:not(.shown)>:not(:first-child){display:none}}.right.stackable.menu{margin-left:auto;display:flex;align-items:inherit;flex-direction:inherit}.ui.left{float:left}.ui.right{float:right}.ui.button,.ui.menu .item{-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}.ui.container.fluid.padded{padding:0 10px 0 10px}.ui.form .ui.button{font-weight:400}.ui.floating.label{z-index:10}.ui.transparent.label{background-color:transparent}.ui.menu,.ui.segment,.ui.vertical.menu{box-shadow:none}.ui .menu:not(.vertical) .item>.button.compact{padding:.58928571em 1.125em}.ui .menu:not(.vertical) .item>.button.small{font-size:.92857143rem}.ui.menu .ui.dropdown.item .menu .item{margin-right:auto}.ui.dropdown .menu>.item>.floating.label{z-index:11}.ui.dropdown .menu .menu>.item>.floating.label{z-index:21}.ui .text.red{color:#d95c5c!important}.ui .text.red a{color:#d95c5c!important}.ui .text.red a:hover{color:#E67777!important}.ui .text.blue{color:#428bca!important}.ui .text.blue a{color:#15c!important}.ui .text.blue a:hover{color:#428bca!important}.ui .text.black{color:#444}.ui .text.black:hover{color:#000}.ui .text.grey{color:#767676!important}.ui .text.grey a{color:#444!important}.ui .text.grey a:hover{color:#000!important}.ui .text.light.grey{color:#888!important}.ui .text.green{color:#6cc644!important}.ui .text.purple{color:#6e5494!important}.ui .text.yellow{color:#FBBD08!important}.ui .text.gold{color:#a1882b!important}.ui .text.left{text-align:left!important}.ui .text.right{text-align:right!important}.ui .text.small{font-size:.75em}.ui .text.normal{font-weight:400}.ui .text.bold{font-weight:700}.ui .text.italic{font-style:italic}.ui .text.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.ui .text.thin{font-weight:400}.ui .text.middle{vertical-align:middle}.ui .message{text-align:center}.ui.bottom.attached.message{font-weight:700;text-align:left;color:#000}.ui.bottom.attached.message .pull-right{color:#000}.ui.bottom.attached.message .pull-right>span,.ui.bottom.attached.message>span{color:#21ba45}.ui .header>i+.content{padding-left:.75rem;vertical-align:middle}.ui .warning.header{background-color:#F9EDBE!important;border-color:#F0C36D}.ui .warning.segment{border-color:#F0C36D}.ui .info.segment{border:1px solid #c5d5dd}.ui .info.segment.top{background-color:#e6f1f6!important}.ui .info.segment.top h3,.ui .info.segment.top h4{margin-top:0}.ui .info.segment.top h3:last-child{margin-top:4px}.ui .info.segment.top>:last-child{margin-bottom:0}.ui .normal.header{font-weight:400}.ui .avatar.image{border-radius:3px}.ui .form .fake{display:none!important}.ui .form .sub.field{margin-left:25px}.ui .sha.label{font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;font-size:13px;padding:6px 10px 4px 10px;font-weight:400;margin:0 6px}.ui.status.buttons .octicon{margin-right:4px}.ui.inline.delete-button{padding:8px 15px;font-weight:400}.ui .background.red{background-color:#d95c5c!important}.ui .background.blue{background-color:#428bca!important}.ui .background.black{background-color:#444}.ui .background.grey{background-color:#767676!important}.ui .background.light.grey{background-color:#888!important}.ui .background.green{background-color:#6cc644!important}.ui .background.purple{background-color:#6e5494!important}.ui .background.yellow{background-color:#FBBD08!important}.ui .background.gold{background-color:#a1882b!important}.ui .branch-tag-choice{line-height:20px}@media only screen and (max-width:767px){.ui.pagination.menu .item.navigation span.navigation_label,.ui.pagination.menu .item:not(.active):not(.navigation){display:none}}.file-comment{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.87)}.ui.floating.dropdown .overflow.menu .scrolling.menu.items{border-radius:0!important;box-shadow:none!important;border-bottom:1px solid rgba(34,36,38,.15)}.user-menu>.item{width:100%;border-radius:0!important}.scrolling.menu .item.selected{font-weight:700!important}footer{background-color:#fff;border-top:1px solid #d6d6d6;width:100%;flex-basis:40px;color:#888}footer .container{width:100vw!important;padding:0 .5rem}footer .container .fa{width:16px;text-align:center;color:#428bca}footer .container .links>*{border-left:1px solid #d6d6d6;padding-left:8px;margin-left:5px}footer .container .links>:first-child{border-left:none}footer .ui.language .menu{max-height:500px;overflow-y:auto;margin-bottom:7px}footer .ui.left,footer .ui.right{line-height:40px}.hide{display:none}.hide.show-outdated{display:none!important}.hide.hide-outdated{display:none!important}.center{text-align:center}.img-1{width:2px!important;height:2px!important}.img-2{width:4px!important;height:4px!important}.img-3{width:6px!important;height:6px!important}.img-4{width:8px!important;height:8px!important}.img-5{width:10px!important;height:10px!important}.img-6{width:12px!important;height:12px!important}.img-7{width:14px!important;height:14px!important}.img-8{width:16px!important;height:16px!important}.img-9{width:18px!important;height:18px!important}.img-10{width:20px!important;height:20px!important}.img-11{width:22px!important;height:22px!important}.img-12{width:24px!important;height:24px!important}.img-13{width:26px!important;height:26px!important}.img-14{width:28px!important;height:28px!important}.img-15{width:30px!important;height:30px!important}.img-16{width:32px!important;height:32px!important}@media only screen and (min-width:768px){.mobile-only,.ui.button.mobile-only{display:none}.sr-mobile-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}}@media only screen and (max-width:767px){.not-mobile{display:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}@media only screen and (max-width:991px) and (min-width:768px){.ui.container{width:95%}}.hljs{background:inherit!important;padding:0!important}.ui.menu.new-menu{justify-content:center!important;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}@media only screen and (max-width:1200px){.ui.menu.new-menu{overflow-x:auto!important;justify-content:left!important;padding-bottom:5px}.ui.menu.new-menu::-webkit-scrollbar{height:8px;display:none}.ui.menu.new-menu:hover::-webkit-scrollbar{display:block}.ui.menu.new-menu::-webkit-scrollbar-track{background:rgba(0,0,0,.01)}.ui.menu.new-menu::-webkit-scrollbar-thumb{background:rgba(0,0,0,.2)}.ui.menu.new-menu:after{position:absolute;margin-top:-15px;display:block;background-image:linear-gradient(to right,rgba(255,255,255,0),#fff 100%);content:' ';right:0;height:53px;z-index:1000;width:60px;clear:none;visibility:visible}.ui.menu.new-menu a.item:last-child{padding-right:30px!important}}[v-cloak]{display:none!important}.repos-search{padding-bottom:0!important}.repos-filter{margin-top:0!important;border-bottom-width:0!important;margin-bottom:2px!important}#user-heatmap{width:107%;text-align:center}#user-heatmap svg:not(:root){overflow:inherit;padding:0!important}@media only screen and (max-width:1200px){#user-heatmap{display:none}}.heatmap-color-0{background-color:#f4f4f4}.heatmap-color-1{background-color:#d7e5db}.heatmap-color-2{background-color:#adc7ab}.heatmap-color-3{background-color:#83a87b}.heatmap-color-4{background-color:#598a4b}.heatmap-color-5{background-color:#2f6b1b}.archived-icon{color:#b3b3b3!important}.archived-icon{color:#b3b3b3!important}.oauth2-authorize-application-box{margin-top:3em!important}.ui.tabular.menu .item{color:rgba(0,0,0,.5)}.ui.tabular.menu .item:hover{color:rgba(0,0,0,.8)}.ui.tabular.menu .item.active{color:rgba(0,0,0,.9)}.markdown:not(code){overflow:hidden;font-size:16px;line-height:1.6!important;word-wrap:break-word}.markdown:not(code).ui.segment{padding:3em}.markdown:not(code).file-view{padding:2em 2em 2em!important}.markdown:not(code)>:first-child{margin-top:0!important}.markdown:not(code)>:last-child{margin-bottom:0!important}.markdown:not(code) a:not([href]){color:inherit;text-decoration:none}.markdown:not(code) .absent{color:#c00}.markdown:not(code) .anchor{position:absolute;top:0;left:0;display:block;padding-right:6px;padding-left:30px;margin-left:-30px}.markdown:not(code) .anchor:focus{outline:0}.markdown:not(code) h1,.markdown:not(code) h2,.markdown:not(code) h3,.markdown:not(code) h4,.markdown:not(code) h5,.markdown:not(code) h6{position:relative;margin-top:1em;margin-bottom:16px;font-weight:700;line-height:1.4}.markdown:not(code) h1:first-of-type,.markdown:not(code) h2:first-of-type,.markdown:not(code) h3:first-of-type,.markdown:not(code) h4:first-of-type,.markdown:not(code) h5:first-of-type,.markdown:not(code) h6:first-of-type{margin-top:0!important}.markdown:not(code) h1 .octicon-link,.markdown:not(code) h2 .octicon-link,.markdown:not(code) h3 .octicon-link,.markdown:not(code) h4 .octicon-link,.markdown:not(code) h5 .octicon-link,.markdown:not(code) h6 .octicon-link{display:none;color:#000;vertical-align:middle}.markdown:not(code) h1:hover .anchor,.markdown:not(code) h2:hover .anchor,.markdown:not(code) h3:hover .anchor,.markdown:not(code) h4:hover .anchor,.markdown:not(code) h5:hover .anchor,.markdown:not(code) h6:hover .anchor{padding-left:8px;margin-left:-30px;text-decoration:none}.markdown:not(code) h1:hover .anchor .octicon-link,.markdown:not(code) h2:hover .anchor .octicon-link,.markdown:not(code) h3:hover .anchor .octicon-link,.markdown:not(code) h4:hover .anchor .octicon-link,.markdown:not(code) h5:hover .anchor .octicon-link,.markdown:not(code) h6:hover .anchor .octicon-link{display:inline-block}.markdown:not(code) h1 code,.markdown:not(code) h1 tt,.markdown:not(code) h2 code,.markdown:not(code) h2 tt,.markdown:not(code) h3 code,.markdown:not(code) h3 tt,.markdown:not(code) h4 code,.markdown:not(code) h4 tt,.markdown:not(code) h5 code,.markdown:not(code) h5 tt,.markdown:not(code) h6 code,.markdown:not(code) h6 tt{font-size:inherit}.markdown:not(code) h1{padding-bottom:.3em;font-size:2.25em;line-height:1.2;border-bottom:1px solid #eee}.markdown:not(code) h1 .anchor{line-height:1}.markdown:not(code) h2{padding-bottom:.3em;font-size:1.75em;line-height:1.225;border-bottom:1px solid #eee}.markdown:not(code) h2 .anchor{line-height:1}.markdown:not(code) h3{font-size:1.5em;line-height:1.43}.markdown:not(code) h3 .anchor{line-height:1.2}.markdown:not(code) h4{font-size:1.25em}.markdown:not(code) h4 .anchor{line-height:1.2}.markdown:not(code) h5{font-size:1em}.markdown:not(code) h5 .anchor{line-height:1.1}.markdown:not(code) h6{font-size:1em;color:#777}.markdown:not(code) h6 .anchor{line-height:1.1}.markdown:not(code) blockquote,.markdown:not(code) dl,.markdown:not(code) ol,.markdown:not(code) p,.markdown:not(code) pre,.markdown:not(code) table,.markdown:not(code) ul{margin-top:0;margin-bottom:16px}.markdown:not(code) blockquote{margin-left:0}.markdown:not(code) hr{height:4px;padding:0;margin:16px 0;background-color:#e7e7e7;border:0 none}.markdown:not(code) ol,.markdown:not(code) ul{padding-left:2em}.markdown:not(code) ol.no-list,.markdown:not(code) ul.no-list{padding:0;list-style-type:none}.markdown:not(code) ol ol,.markdown:not(code) ol ul,.markdown:not(code) ul ol,.markdown:not(code) ul ul{margin-top:0;margin-bottom:0}.markdown:not(code) ol ol,.markdown:not(code) ul ol{list-style-type:lower-roman}.markdown:not(code) li>p{margin-top:0}.markdown:not(code) dl{padding:0}.markdown:not(code) dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:700}.markdown:not(code) dl dd{padding:0 16px;margin-bottom:16px}.markdown:not(code) blockquote{padding:0 15px;color:#777;border-left:4px solid #ddd}.markdown:not(code) blockquote>:first-child{margin-top:0}.markdown:not(code) blockquote>:last-child{margin-bottom:0}.markdown:not(code) table{width:auto;overflow:auto;word-break:normal;word-break:keep-all;display:block}.markdown:not(code) table th{font-weight:700}.markdown:not(code) table td,.markdown:not(code) table th{padding:6px 13px!important;border:1px solid #ddd!important}.markdown:not(code) table tr{background-color:#fff;border-top:1px solid #ccc}.markdown:not(code) table tr:nth-child(2n){background-color:#f8f8f8}.markdown:not(code) img{max-width:100%;box-sizing:border-box}.markdown:not(code) .emoji{max-width:none}.markdown:not(code) span.frame{display:block;overflow:hidden}.markdown:not(code) span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #ddd}.markdown:not(code) span.frame span img{display:block;float:left}.markdown:not(code) span.frame span span{display:block;padding:5px 0 0;clear:both;color:#333}.markdown:not(code) span.align-center{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown:not(code) span.align-center span img{margin:0 auto;text-align:center}.markdown:not(code) span.align-right{display:block;overflow:hidden;clear:both}.markdown:not(code) span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown:not(code) span.align-right span img{margin:0;text-align:right}.markdown:not(code) span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown:not(code) span.float-left span{margin:13px 0 0}.markdown:not(code) span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown:not(code) span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown:not(code) code,.markdown:not(code) tt{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px}.markdown:not(code) code:after,.markdown:not(code) code:before,.markdown:not(code) tt:after,.markdown:not(code) tt:before{letter-spacing:-.2em;content:"\00a0"}.markdown:not(code) code br,.markdown:not(code) tt br{display:none}.markdown:not(code) del code{text-decoration:inherit}.markdown:not(code) pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0;border:0}.markdown:not(code) .highlight{margin-bottom:16px}.markdown:not(code) .highlight pre,.markdown:not(code) pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border-radius:3px}.markdown:not(code) .highlight pre{margin-bottom:0;word-break:normal}.markdown:not(code) pre{word-wrap:normal}.markdown:not(code) pre code,.markdown:not(code) pre tt{display:inline;max-width:initial;padding:0;margin:0;overflow:initial;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown:not(code) pre code:after,.markdown:not(code) pre code:before,.markdown:not(code) pre tt:after,.markdown:not(code) pre tt:before{content:normal}.markdown:not(code) kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#555;vertical-align:middle;background-color:#fcfcfc;border:solid 1px #ccc;border-bottom-color:#bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb}.markdown:not(code) input[type=checkbox]{vertical-align:middle!important}.markdown:not(code) .csv-data td,.markdown:not(code) .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown:not(code) .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown:not(code) .csv-data tr{border-top:0}.markdown:not(code) .csv-data th{font-weight:700;background:#f8f8f8;border-top:0}.markdown:not(code) .ui.list .list,.markdown:not(code) ol.ui.list ol,.markdown:not(code) ul.ui.list ul{padding-left:2em}.home .logo{max-width:220px}@media only screen and (max-width:767px){.home .hero h1{font-size:3.5em}.home .hero h2{font-size:2em}}@media only screen and (min-width:768px){.home .hero h1{font-size:5.5em}.home .hero h2{font-size:3em}}.home .hero .octicon{color:#5aa509;font-size:40px;width:50px}.home .hero.header{font-size:20px}.home p.large{font-size:16px}.home .stackable{padding-top:30px}.home a{color:#5aa509}.signup{padding-top:15px}@media only screen and (max-width:880px){footer .ui.container .left,footer .ui.container .right{display:block;text-align:center;float:none}}.install{padding-top:45px}.install form label{text-align:right;width:320px!important}.install form input{width:35%!important}.install form .field{text-align:left}.install form .field .help{margin-left:335px!important}.install form .field.optional .title{margin-left:38%}.install .ui .checkbox{margin-left:40%!important}.install .ui .checkbox label{width:auto!important}.form .help{color:#999;padding-top:.6em;padding-bottom:.6em;display:inline-block}.ui.attached.header{background:#f0f0f0}.ui.attached.header .right{margin-top:-5px}.ui.attached.header .right .button{padding:8px 10px;font-weight:400}#create-page-form form{margin:auto}#create-page-form form .ui.message{text-align:center}@media only screen and (min-width:768px){#create-page-form form{width:800px!important}#create-page-form form .header{padding-left:280px!important}#create-page-form form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}#create-page-form form .help{margin-left:265px!important}#create-page-form form .optional .title{margin-left:250px!important}#create-page-form form input,#create-page-form form textarea{width:50%!important}}@media only screen and (max-width:767px){#create-page-form form .optional .title{margin-left:15px}#create-page-form form .inline.field>label{display:block}}.signin .oauth2 div{display:inline-block}.signin .oauth2 div p{margin:10px 5px 0 0;float:left}.signin .oauth2 a{margin-right:3px}.signin .oauth2 a:last-child{margin-right:0}.signin .oauth2 img{width:32px;height:32px}.signin .oauth2 img.openidConnect{width:auto}@media only screen and (min-width:768px){.g-recaptcha{margin:0 auto!important;width:304px;padding-left:30px}}@media screen and (max-height:575px){#rc-imageselect,.g-recaptcha{transform:scale(.77);transform-origin:0 0}}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{margin:auto}.user.activate form .ui.message,.user.forgot.password form .ui.message,.user.reset.password form .ui.message,.user.signin form .ui.message,.user.signup form .ui.message{text-align:center}@media only screen and (min-width:768px){.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:800px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:280px!important}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.user.activate form .help,.user.forgot.password form .help,.user.reset.password form .help,.user.signin form .help,.user.signup form .help{margin-left:265px!important}.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:250px!important}.user.activate form input,.user.activate form textarea,.user.forgot.password form input,.user.forgot.password form textarea,.user.reset.password form input,.user.reset.password form textarea,.user.signin form input,.user.signin form textarea,.user.signup form input,.user.signup form textarea{width:50%!important}}@media only screen and (max-width:767px){.user.activate form .optional .title,.user.forgot.password form .optional .title,.user.reset.password form .optional .title,.user.signin form .optional .title,.user.signup form .optional .title{margin-left:15px}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{display:block}}.user.activate form,.user.forgot.password form,.user.reset.password form,.user.signin form,.user.signup form{width:700px!important}.user.activate form .header,.user.forgot.password form .header,.user.reset.password form .header,.user.signin form .header,.user.signup form .header{padding-left:0!important;text-align:center}.user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{width:200px}@media only screen and (max-width:768px){.user.activate form .inline.field>label,.user.activate form input,.user.forgot.password form .inline.field>label,.user.forgot.password form input,.user.reset.password form .inline.field>label,.user.reset.password form input,.user.signin form .inline.field>label,.user.signin form input,.user.signup form .inline.field>label,.user.signup form input{width:100%!important}}.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{margin:auto}.repository.new.fork form .ui.message,.repository.new.migrate form .ui.message,.repository.new.repo form .ui.message{text-align:center}@media only screen and (min-width:768px){.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{width:800px!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:280px!important}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.repository.new.fork form .help,.repository.new.migrate form .help,.repository.new.repo form .help{margin-left:265px!important}.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:250px!important}.repository.new.fork form input,.repository.new.fork form textarea,.repository.new.migrate form input,.repository.new.migrate form textarea,.repository.new.repo form input,.repository.new.repo form textarea{width:50%!important}}@media only screen and (max-width:767px){.repository.new.fork form .optional .title,.repository.new.migrate form .optional .title,.repository.new.repo form .optional .title{margin-left:15px}.repository.new.fork form .inline.field>label,.repository.new.migrate form .inline.field>label,.repository.new.repo form .inline.field>label{display:block}}.repository.new.fork form .dropdown .dropdown.icon,.repository.new.migrate form .dropdown .dropdown.icon,.repository.new.repo form .dropdown .dropdown.icon{margin-top:-7px!important;padding-bottom:5px}.repository.new.fork form .dropdown .text,.repository.new.migrate form .dropdown .text,.repository.new.repo form .dropdown .text{margin-right:0!important}.repository.new.fork form .dropdown .text i,.repository.new.migrate form .dropdown .text i,.repository.new.repo form .dropdown .text i{margin-right:0!important}.repository.new.fork form .header,.repository.new.migrate form .header,.repository.new.repo form .header{padding-left:0!important;text-align:center}@media only screen and (max-width:768px){.repository.new.fork form .selection.dropdown,.repository.new.fork form input,.repository.new.fork form label,.repository.new.migrate form .selection.dropdown,.repository.new.migrate form input,.repository.new.migrate form label,.repository.new.repo form .selection.dropdown,.repository.new.repo form input,.repository.new.repo form label{width:100%!important}.repository.new.fork form .field a,.repository.new.fork form .field button,.repository.new.migrate form .field a,.repository.new.migrate form .field button,.repository.new.repo form .field a,.repository.new.repo form .field button{margin-bottom:1em;width:100%}}@media only screen and (min-width:768px){.repository.new.repo .ui.form #auto-init{margin-left:265px!important}}.repository.new.repo .ui.form .selection.dropdown:not(.owner){width:50%!important}@media only screen and (max-width:768px){.repository.new.repo .ui.form .selection.dropdown:not(.owner){width:100%!important}}.new.webhook form .help{margin-left:25px}.new.webhook .events.fields .column{padding-left:40px}.githook textarea{font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}@media only screen and (max-width:768px){.new.org .ui.form .field a,.new.org .ui.form .field button{margin-bottom:1em;width:100%}.new.org .ui.form .field input{width:100%!important}}.repository{padding-top:15px}.repository .repo-header .ui.compact.menu{margin-left:1rem}.repository .repo-header .ui.header{margin-top:0}.repository .repo-header .mega-octicon{width:30px;font-size:30px}.repository .repo-header .ui.huge.breadcrumb{font-weight:400;font-size:1.5rem}.repository .repo-header .fork-flag{margin-left:36px;margin-top:3px;display:block;font-size:12px;white-space:nowrap}.repository .repo-header .octicon.octicon-repo-forked{margin-top:-1px;font-size:15px}.repository .repo-header .button{margin-top:2px;margin-bottom:2px}.repository .tabs .navbar{justify-content:initial}.repository .navbar{display:flex;justify-content:space-between}.repository .navbar .ui.label{margin-left:7px;padding:3px 5px}.repository .owner.dropdown{min-width:40%!important}.repository #file-buttons{margin-left:auto!important;font-weight:400}.repository #file-buttons .ui.button{padding:8px 10px;font-weight:400}.repository .metas .menu{max-height:300px;overflow-x:auto}.repository .metas .ui.list .hide{display:none!important}.repository .metas .ui.list .item{padding:0}.repository .metas .ui.list .label.color{padding:0 8px;margin-right:5px}.repository .metas .ui.list a{margin:2px 0}.repository .metas .ui.list a .text{color:#444}.repository .metas .ui.list a .text:hover{color:#000}.repository .metas #deadlineForm input{width:12.8rem;border-radius:4px 0 0 4px;border-right:0;white-space:nowrap}.repository .header-wrapper{background-color:#FAFAFA;margin-top:-15px;padding-top:15px}.repository .header-wrapper .ui.tabs.divider{border-bottom:none}.repository .header-wrapper .ui.tabular .octicon{margin-right:5px}.repository .filter.menu .label.color{border-radius:3px;margin-left:15px;padding:0 8px}.repository .filter.menu .octicon{float:left;margin:5px -7px 0 -5px;width:16px}.repository .filter.menu.labels .octicon{margin:-2px -7px 0 -5px}.repository .filter.menu .text{margin-left:.9em}.repository .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}.repository .filter.menu .dropdown.item{margin:1px;padding-right:0}.repository .select-label .item{max-width:250px;overflow:hidden;text-overflow:ellipsis}.repository .select-label .desc{padding-left:16px}.repository .ui.tabs.container{margin-top:14px;margin-bottom:0}.repository .ui.tabs.container .ui.menu{border-bottom:none}.repository .ui.tabs.divider{margin-top:0;margin-bottom:20px}.repository #clone-panel{width:350px}@media only screen and (max-width:768px){.repository #clone-panel{width:100%}}.repository #clone-panel input{border-radius:0;padding:5px 10px;width:50%}.repository #clone-panel .clone.button{font-size:13px;padding:0 5px}.repository #clone-panel .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository #clone-panel .icon.button{padding:0 10px}.repository #clone-panel .dropdown .menu{right:0!important;left:auto!important}.repository.file.list .repo-description{display:flex;justify-content:space-between;align-items:center}.repository.file.list #repo-desc{font-size:1.2em}.repository.file.list .choose.reference .header .icon{font-size:1.4em}.repository.file.list .repo-path .divider,.repository.file.list .repo-path .section{display:inline}.repository.file.list #file-buttons{font-weight:400}.repository.file.list #file-buttons .ui.button{padding:8px 10px;font-weight:400}@media only screen and (max-width:768px){.repository.file.list #file-buttons .ui.tiny.blue.buttons{width:100%}}.repository.file.list #repo-files-table thead th{padding-top:8px;padding-bottom:5px;font-weight:400}.repository.file.list #repo-files-table thead .ui.avatar{margin-bottom:5px}.repository.file.list #repo-files-table tbody .octicon{margin-left:3px;margin-right:5px;color:#777}.repository.file.list #repo-files-table tbody .octicon.octicon-mail-reply{margin-right:10px}.repository.file.list #repo-files-table tbody .octicon.octicon-file-directory,.repository.file.list #repo-files-table tbody .octicon.octicon-file-submodule,.repository.file.list #repo-files-table tbody .octicon.octicon-file-symlink-directory{color:#1e70bf}.repository.file.list #repo-files-table td{padding-top:8px;padding-bottom:8px;overflow:initial}.repository.file.list #repo-files-table td.name{max-width:150px}.repository.file.list #repo-files-table td.message{max-width:400px}.repository.file.list #repo-files-table td.age{width:120px}.repository.file.list #repo-files-table td .truncate{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;vertical-align:top;white-space:nowrap}.repository.file.list #repo-files-table td.message .isSigned{cursor:default}.repository.file.list #repo-files-table tr:hover{background-color:#ffE}.repository.file.list #repo-files-table .jumpable-path{color:#888}.repository.file.list .non-diff-file-content .header .icon{font-size:1em}.repository.file.list .non-diff-file-content .header .file-actions{margin-top:0;margin-bottom:-5px;padding-left:20px}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon{display:inline-block;padding:5px;margin-left:5px;line-height:1;color:#767676;vertical-align:middle;background:0 0;border:0;outline:0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon:hover{color:#4078c0}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon-danger:hover{color:#bd2c00}.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon.disabled{color:#bbb;cursor:default}.repository.file.list .non-diff-file-content .header .file-actions #delete-file-form{display:inline-block}.repository.file.list .non-diff-file-content .view-raw{padding:5px}.repository.file.list .non-diff-file-content .view-raw *{max-width:100%}.repository.file.list .non-diff-file-content .view-raw img{padding:5px 5px 0 5px}.repository.file.list .non-diff-file-content .plain-text{padding:1em 2em 1em 2em}.repository.file.list .non-diff-file-content pre{overflow:auto}.repository.file.list .non-diff-file-content .code-view *{font-size:12px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;line-height:20px}.repository.file.list .non-diff-file-content .code-view table{width:100%}.repository.file.list .non-diff-file-content .code-view .lines-num{vertical-align:top;text-align:right;color:#999;background:#f5f5f5;width:1%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.repository.file.list .non-diff-file-content .code-view .lines-num span{line-height:20px;padding:0 10px;cursor:pointer;display:block}.repository.file.list .non-diff-file-content .code-view .lines-code,.repository.file.list .non-diff-file-content .code-view .lines-num{padding:0}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs,.repository.file.list .non-diff-file-content .code-view .lines-code ol,.repository.file.list .non-diff-file-content .code-view .lines-code pre,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs,.repository.file.list .non-diff-file-content .code-view .lines-num ol,.repository.file.list .non-diff-file-content .code-view .lines-num pre{background-color:#fff;margin:0;padding:0!important}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-code ol li,.repository.file.list .non-diff-file-content .code-view .lines-code pre li,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li,.repository.file.list .non-diff-file-content .code-view .lines-num ol li,.repository.file.list .non-diff-file-content .code-view .lines-num pre li{display:block;width:100%}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-code ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-code pre li.active,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li.active,.repository.file.list .non-diff-file-content .code-view .lines-num ol li.active,.repository.file.list .non-diff-file-content .code-view .lines-num pre li.active{background:#ffd}.repository.file.list .non-diff-file-content .code-view .lines-code .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-code ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-code pre li:before,.repository.file.list .non-diff-file-content .code-view .lines-num .hljs li:before,.repository.file.list .non-diff-file-content .code-view .lines-num ol li:before,.repository.file.list .non-diff-file-content .code-view .lines-num pre li:before{content:' '}.repository.file.list .non-diff-file-content .code-view .lines-commit{vertical-align:top;color:#999;padding:0;background:#f5f5f5;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info{width:350px;max-width:350px;display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;padding:0 0 0 10px}.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info .blame-data{display:flex;font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial}.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info .blame-data .blame-message{flex-grow:2;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;line-height:20px}.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info .blame-data .blame-avatar,.repository.file.list .non-diff-file-content .code-view .lines-commit .blame-info .blame-data .blame-time{flex-shrink:0}.repository.file.list .non-diff-file-content .code-view .lines-commit .ui.avatar.image{height:18px;width:18px}.repository.file.list .non-diff-file-content .code-view .lines-code .bottom-line,.repository.file.list .non-diff-file-content .code-view .lines-commit .bottom-line,.repository.file.list .non-diff-file-content .code-view .lines-num .bottom-line{border-bottom:1px solid #eaecef}.repository.file.list .non-diff-file-content .code-view .active{background:#ffd}.repository.file.list .sidebar{padding-left:0}.repository.file.list .sidebar .octicon{width:16px}.repository.file.editor .treepath{width:100%}.repository.file.editor .treepath input{vertical-align:middle;box-shadow:rgba(0,0,0,.0745098) 0 1px 2px inset;width:inherit;padding:7px 8px;margin-right:5px}.repository.file.editor .tabular.menu .octicon{margin-right:5px}.repository.file.editor .commit-form-wrapper{padding-left:64px}.repository.file.editor .commit-form-wrapper .commit-avatar{float:left;margin-left:-64px;width:3em;height:auto}.repository.file.editor .commit-form-wrapper .commit-form{position:relative;padding:15px;margin-bottom:10px;border:1px solid #ddd;border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form:after,.repository.file.editor .commit-form-wrapper .commit-form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.file.editor .commit-form-wrapper .commit-form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.file.editor .commit-form-wrapper .commit-form:after{border-right-color:#fff}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name{display:inline-block;padding:3px 6px;font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.65);background-color:rgba(209,227,237,.45);border-radius:3px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input{position:relative;margin-left:25px}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input{width:240px!important;padding-left:26px!important}.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch{position:absolute;top:9px;left:10px;color:#b0c4ce}.repository.options #interval{width:100px!important;min-width:100px}.repository.options .danger .item{padding:20px 15px}.repository.options .danger .ui.divider{margin:0}.repository.new.issue .comment.form .comment .avatar{width:3em}.repository.new.issue .comment.form .content{margin-left:4em}.repository.new.issue .comment.form .content:after,.repository.new.issue .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.new.issue .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.new.issue .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.new.issue .comment.form .content:after{border-right-color:#fff}.repository.new.issue .comment.form .content .markdown{font-size:14px}.repository.new.issue .comment.form .metas{min-width:220px}.repository.new.issue .comment.form .metas .filter.menu{max-height:300px;overflow-x:auto}.repository.view.issue .title{padding-bottom:0!important}.repository.view.issue .title h1{font-weight:300;font-size:2.3rem;margin-bottom:5px}.repository.view.issue .title h1 .ui.input{font-size:.5em;vertical-align:top;width:50%;min-width:600px}.repository.view.issue .title h1 .ui.input input{font-size:1.5em;padding:6px 10px}.repository.view.issue .title .index{font-weight:300;color:#aaa;letter-spacing:-1px}.repository.view.issue .title .label{margin-right:10px}.repository.view.issue .title .edit-zone{margin-top:10px}.repository.view.issue .pull-desc code{color:#0166E6}.repository.view.issue .pull.tabular.menu{margin-bottom:10px}.repository.view.issue .pull.tabular.menu .octicon{margin-right:5px}.repository.view.issue .pull.tab.segment{border:none;padding:0;padding-top:10px;box-shadow:none;background-color:inherit}.repository.view.issue .pull .merge.box .avatar{margin-left:10px;margin-top:10px}.repository.view.issue .pull .review-item .avatar,.repository.view.issue .pull .review-item .type-icon{float:none;display:inline-block;text-align:center;vertical-align:middle}.repository.view.issue .pull .review-item .avatar .octicon,.repository.view.issue .pull .review-item .type-icon .octicon{width:23px;font-size:23px;margin-top:.45em}.repository.view.issue .pull .review-item .text{margin:.3em 0 .5em .5em}.repository.view.issue .pull .review-item .type-icon{float:right;margin-right:1em}.repository.view.issue .pull .review-item .divider{margin:.5rem 0}.repository.view.issue .pull .review-item .review-content{padding:1em 0 1em 3.8em}.repository.view.issue .comment-list:before{display:block;content:"";position:absolute;margin-top:12px;margin-bottom:14px;top:0;bottom:0;left:96px;width:2px;background-color:#f3f3f3;z-index:-1}.repository.view.issue .comment-list .comment .avatar{width:3em}.repository.view.issue .comment-list .comment .tag{color:#767676;margin-top:3px;padding:2px 5px;font-size:12px;border:1px solid rgba(0,0,0,.1);border-radius:3px}.repository.view.issue .comment-list .comment .actions .item{float:left}.repository.view.issue .comment-list .comment .actions .item.tag{margin-right:5px}.repository.view.issue .comment-list .comment .actions .item.action{margin-top:6px;margin-left:10px}.repository.view.issue .comment-list .comment .content{margin-left:4em}.repository.view.issue .comment-list .comment .content>.header{font-weight:400;padding:auto 15px;position:relative;color:#767676;background-color:#f7f7f7;border-bottom:1px solid #eee;border-top-left-radius:3px;border-top-right-radius:3px}.repository.view.issue .comment-list .comment .content>.header:after,.repository.view.issue .comment-list .comment .content>.header:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.view.issue .comment-list .comment .content>.header:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.view.issue .comment-list .comment .content>.header:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.view.issue .comment-list .comment .content>.header .text{max-width:78%;padding-top:10px;padding-bottom:10px}.repository.view.issue .comment-list .comment .content .markdown{font-size:14px}.repository.view.issue .comment-list .comment .content .no-content{color:#767676;font-style:italic}.repository.view.issue .comment-list .comment .content>.bottom.segment{background:#f3f4f5}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.images::after{clear:both;content:' ';display:block}.repository.view.issue .comment-list .comment .content>.bottom.segment a{display:block;float:left;margin:5px;padding:5px;height:150px;border:solid 1px #eee;border-radius:3px;max-width:150px;background-color:#fff}.repository.view.issue .comment-list .comment .content>.bottom.segment a:before{content:' ';display:inline-block;height:100%;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment .ui.image{max-height:100%;width:auto;margin:0;vertical-align:middle}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image{font-size:128px;color:#000}.repository.view.issue .comment-list .comment .content>.bottom.segment span.ui.image:hover{color:#000}.repository.view.issue .comment-list .comment .ui.form .field:first-child{clear:none}.repository.view.issue .comment-list .comment .ui.form .tab.segment{border:none;padding:0;padding-top:10px}.repository.view.issue .comment-list .comment .ui.form textarea{height:200px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.repository.view.issue .comment-list .comment .edit.buttons{margin-top:10px}.repository.view.issue .comment-list .event{position:relative;margin:15px 0 15px 79px;padding-left:25px}.repository.view.issue .comment-list .event .octicon{width:30px;float:left;text-align:center}.repository.view.issue .comment-list .event .octicon.octicon-circle-slash{margin-top:5px;margin-left:-34.5px;font-size:20px;color:#bd2c00}.repository.view.issue .comment-list .event .octicon.octicon-primitive-dot{margin-left:-28.5px;margin-right:-1px;font-size:30px;color:#6cc644}.repository.view.issue .comment-list .event .octicon.octicon-bookmark{margin-top:3px;margin-left:-31px;margin-right:-1px;font-size:25px}.repository.view.issue .comment-list .event .octicon.octicon-comment{margin-top:4px;margin-left:-35px;font-size:24px}.repository.view.issue .comment-list .event .octicon.octicon-eye{margin-top:3px;margin-left:-35px;margin-right:0;font-size:22px}.repository.view.issue .comment-list .event .octicon.octicon-x{margin-left:-33px;font-size:25px}.repository.view.issue .comment-list .event .detail{font-size:.9rem;margin-top:5px;margin-left:35px}.repository.view.issue .comment-list .event .detail .octicon.octicon-git-commit{margin-top:2px}.repository.view.issue .ui.segment.metas{margin-top:-3px}.repository.view.issue .ui.participants img{margin-top:5px;margin-right:5px}.repository.view.issue .ui.depending .item.is-closed .title{text-decoration:line-through}.repository .comment.form .ui.comments{margin-top:-12px;max-width:100%}.repository .comment.form .content .field:first-child{clear:none}.repository .comment.form .content .form:after,.repository .comment.form .content .form:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository .comment.form .content .form:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository .comment.form .content .form:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository .comment.form .content .form:after{border-right-color:#fff}.repository .comment.form .content .tab.segment{border:none;padding:0;padding-top:10px}.repository .comment.form .content textarea{height:200px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.repository .label.list{list-style:none;padding-top:15px}.repository .label.list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .label.list .item a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .label.list .item a:hover{color:#000}.repository .label.list .item a.open-issues{margin-right:30px}.repository .label.list .item .ui.label{font-size:1em}.repository .milestone.list{list-style:none;padding-top:15px}.repository .milestone.list>.item{padding-top:10px;padding-bottom:10px;border-bottom:1px dashed #AAA}.repository .milestone.list>.item>a{padding-top:5px;padding-right:10px;color:#000}.repository .milestone.list>.item>a:hover{color:#4078c0}.repository .milestone.list>.item .ui.progress{width:40%;padding:0;border:0;margin:0}.repository .milestone.list>.item .ui.progress .bar{height:20px}.repository .milestone.list>.item .meta{color:#999;padding-top:5px}.repository .milestone.list>.item .meta .issue-stats .octicon{padding-left:5px}.repository .milestone.list>.item .meta .overdue{color:red}.repository .milestone.list>.item .operate{margin-top:-15px}.repository .milestone.list>.item .operate>a{font-size:15px;padding-top:5px;padding-right:10px;color:#666}.repository .milestone.list>.item .operate>a:hover{color:#000}.repository .milestone.list>.item .content{padding-top:10px}.repository.new.milestone textarea{height:200px}.repository.new.milestone #deadline{width:150px}.repository.compare.pull .choose.branch .octicon{padding-right:10px}.repository.compare.pull .comment.form .content:after,.repository.compare.pull .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.repository.compare.pull .comment.form .content:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}.repository.compare.pull .comment.form .content:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}.repository.compare.pull .comment.form .content:after{border-right-color:#fff}.repository .filter.dropdown .menu{margin-top:1px!important}.repository.branches .commit-divergence .bar-group{position:relative;float:left;padding-bottom:6px;width:90px}.repository.branches .commit-divergence .bar-group:last-child{border-left:1px solid #b4b4b4}.repository.branches .commit-divergence .count{margin:0 3px}.repository.branches .commit-divergence .count.count-ahead{text-align:left}.repository.branches .commit-divergence .count.count-behind{text-align:right}.repository.branches .commit-divergence .bar{height:4px;position:absolute;background-color:#d4d4d5}.repository.branches .commit-divergence .bar.bar-behind{right:0}.repository.branches .commit-divergence .bar.bar-ahead{left:0}.repository.commits .header .search input{font-weight:400;padding:5px 10px}.repository #commits-table thead th:first-of-type{padding-left:15px}.repository #commits-table thead .sha{width:140px}.repository #commits-table thead .shatd{text-align:center}.repository #commits-table td.sha .sha.label{margin:0}.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n){background-color:rgba(0,0,0,.02)!important}.repository #commits-table td.sha .sha.label.isSigned,.repository #repo-files-table .sha.label.isSigned{border:1px solid #BBB}.repository #commits-table td.sha .sha.label.isSigned .detail.icon,.repository #repo-files-table .sha.label.isSigned .detail.icon{background:#FAFAFA;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #BBB;border-top-left-radius:0;border-bottom-left-radius:0}.repository #commits-table td.sha .sha.label.isSigned.isVerified,.repository #repo-files-table .sha.label.isSigned.isVerified{border:1px solid #21BA45;background:rgba(33,186,69,.1)}.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid rgba(33,186,69,.5)}.repository .diff-detail-box{padding:7px 0;background:#fff;line-height:30px}.repository .diff-detail-box>div:after{clear:both;content:"";display:block}.repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px}.repository .diff-detail-box ol li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #DDD;padding-left:6px}.repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle}.repository .diff-detail-box span.status.modify{background-color:#f0db88}.repository .diff-detail-box span.status.add{background-color:#b4e2b4}.repository .diff-detail-box span.status.del{background-color:#e9aeae}.repository .diff-detail-box span.status.rename{background-color:#dad8ff}.repository .diff-detail-box .detail-files{background:#fff;margin:0}.repository .diff-box .header{display:flex;align-items:center}.repository .diff-box .header .count{margin-right:12px;font-size:13px;flex:0 0 auto}.repository .diff-box .header .count .bar{background-color:#bd2c00;height:12px;width:40px;display:inline-block;margin:2px 4px 0 4px;vertical-align:text-top}.repository .diff-box .header .count .bar .add{background-color:#55a532;height:12px}.repository .diff-box .header .file{flex:1;color:#888;word-break:break-all}.repository .diff-box .header .button{margin:-5px 0 -5px 12px;padding:8px 10px;flex:0 0 auto}.repository .diff-file-box .header{background-color:#f7f7f7}.repository .diff-file-box .file-body.file-code .lines-num{text-align:right;color:#A7A7A7;background:#fafafa;width:1%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:top}.repository .diff-file-box .file-body.file-code .lines-num span.fold{display:block;text-align:center}.repository .diff-file-box .file-body.file-code .lines-num-old{border-right:1px solid #DDD}.repository .diff-file-box .code-diff{font-size:12px}.repository .diff-file-box .code-diff td{padding:0;padding-left:10px;border-top:none}.repository .diff-file-box .code-diff pre{margin:0}.repository .diff-file-box .code-diff .lines-num{border-color:#d4d4d5;border-right-width:1px;border-right-style:solid;padding:0 5px}.repository .diff-file-box .code-diff tbody tr td.halfwidth{width:49%}.repository .diff-file-box .code-diff tbody tr td.tag-code,.repository .diff-file-box .code-diff tbody tr.tag-code td{background-color:#F0F0F0!important;border-color:#D2CECE!important;padding-top:8px;padding-bottom:8px}.repository .diff-file-box .code-diff tbody tr .removed-code{background-color:#f99}.repository .diff-file-box .code-diff tbody tr .added-code{background-color:#9f9}.repository .diff-file-box .code-diff-unified tbody tr.del-code td{background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-unified tbody tr.add-code td{background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split table,.repository .diff-file-box .code-diff-split tbody{width:100%}.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(2),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(4){background-color:#fafafa}.repository .diff-file-box .code-diff-split tbody tr td.del-code,.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(1),.repository .diff-file-box .code-diff-split tbody tr.del-code td:nth-child(2){background-color:#ffe0e0!important;border-color:#f1c0c0!important}.repository .diff-file-box .code-diff-split tbody tr td.add-code,.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(3),.repository .diff-file-box .code-diff-split tbody tr.add-code td:nth-child(4){background-color:#d6fcd6!important;border-color:#c1e9c1!important}.repository .diff-file-box .code-diff-split tbody tr td:nth-child(3){border-left-width:1px;border-left-style:solid}.repository .diff-file-box.file-content{clear:right}.repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px}.repository .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden}.repository .repo-search-result{padding-top:10px;padding-bottom:10px}.repository .repo-search-result .lines-num a{color:inherit}.repository.quickstart .guide .item{padding:1em}.repository.quickstart .guide .item small{font-weight:400}.repository.quickstart .guide .clone.button:first-child{border-radius:.28571429rem 0 0 .28571429rem}.repository.quickstart .guide .ui.action.small.input{width:100%}.repository.quickstart .guide #repo-clone-url{border-radius:0;padding:5px 10px;font-size:1.2em}.repository.release #release-list{border-top:1px solid #DDD;margin-top:20px;padding-top:15px}.repository.release #release-list>li{list-style:none}.repository.release #release-list>li .detail,.repository.release #release-list>li .meta{padding-top:30px;padding-bottom:40px}.repository.release #release-list>li .meta{text-align:right;position:relative}.repository.release #release-list>li .meta .tag:not(.icon){display:block;margin-top:15px}.repository.release #release-list>li .meta .commit{display:block;margin-top:10px}.repository.release #release-list>li .detail{border-left:1px solid #DDD}.repository.release #release-list>li .detail .author img{margin-bottom:-3px}.repository.release #release-list>li .detail .download{margin-top:20px}.repository.release #release-list>li .detail .download>a .octicon{margin-left:5px;margin-right:5px}.repository.release #release-list>li .detail .download .list{padding-left:0;border-top:1px solid #eee}.repository.release #release-list>li .detail .download .list li{list-style:none;display:block;padding-top:8px;padding-bottom:8px;border-bottom:1px solid #eee}.repository.release #release-list>li .detail .dot{width:9px;height:9px;background-color:#ccc;z-index:999;position:absolute;display:block;left:-5px;top:40px;border-radius:6px;border:1px solid #FFF}.repository.new.release .target{min-width:500px}.repository.new.release .target #tag-name{margin-top:-4px}.repository.new.release .target .at{margin-left:-5px;margin-right:5px}.repository.new.release .target .dropdown.icon{margin:0;padding-top:3px}.repository.new.release .target .selection.dropdown{padding-top:10px;padding-bottom:10px}.repository.new.release .prerelease.field{margin-bottom:0}@media only screen and (max-width:438px){.repository.new.release .field button,.repository.new.release .field input{width:100%}}@media only screen and (max-width:768px){.repository.new.release .field button{margin-bottom:1em}}.repository.forks .list{margin-top:0}.repository.forks .list .item{padding-top:10px;padding-bottom:10px;border-bottom:1px solid #DDD}.repository.forks .list .item .ui.avatar{float:left;margin-right:5px}.repository.forks .list .item .link{padding-top:5px}.repository.wiki.start .ui.segment{padding-top:70px;padding-bottom:100px}.repository.wiki.start .ui.segment .mega-octicon{font-size:48px}.repository.wiki.new .CodeMirror .CodeMirror-code{font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.repository.wiki.new .CodeMirror .CodeMirror-code .cm-comment{background:inherit}.repository.wiki.new .editor-preview{background-color:#fff}.repository.wiki.view .choose.page{margin-top:-5px}.repository.wiki.view .ui.sub.header{text-transform:none}.repository.wiki.view>.markdown{padding:15px 30px}.repository.wiki.view>.markdown h1:first-of-type,.repository.wiki.view>.markdown h2:first-of-type,.repository.wiki.view>.markdown h3:first-of-type,.repository.wiki.view>.markdown h4:first-of-type,.repository.wiki.view>.markdown h5:first-of-type,.repository.wiki.view>.markdown h6:first-of-type{margin-top:0}@media only screen and (max-width:767px){.repository.wiki .dividing.header .stackable.grid .button{margin-top:2px;margin-bottom:2px}}.repository.settings.collaboration .collaborator.list{padding:0}.repository.settings.collaboration .collaborator.list>.item{margin:0;line-height:2em}.repository.settings.collaboration .collaborator.list>.item:not(:last-child){border-bottom:1px solid #DDD}.repository.settings.collaboration #repo-collab-form #search-user-box .results{left:7px}.repository.settings.collaboration #repo-collab-form .ui.button{margin-left:5px;margin-top:-3px}.repository.settings.branches .protected-branches .selection.dropdown{width:300px}.repository.settings.branches .protected-branches .item{border:1px solid #eaeaea;padding:10px 15px}.repository.settings.branches .protected-branches .item:not(:last-child){border-bottom:0}.repository.settings.branches .branch-protection .help{margin-left:26px;padding-top:0}.repository.settings.branches .branch-protection .fields{margin-left:20px;display:block}.repository.settings.branches .branch-protection .whitelist{margin-left:26px}.repository.settings.branches .branch-protection .whitelist .dropdown img{display:inline-block}.repository.settings.webhook .events .column{padding-bottom:0}.repository.settings.webhook .events .help{font-size:13px;margin-left:26px;padding-top:0}.repository .ui.attached.isSigned.isVerified:not(.positive){border-left:1px solid #A3C293;border-right:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified.top:not(.positive){border-top:1px solid #A3C293}.repository .ui.attached.isSigned.isVerified:not(.positive):last-child{border-bottom:1px solid #A3C293}.repository .ui.segment.sub-menu{padding:7px;line-height:0}.repository .ui.segment.sub-menu .list{width:100%;display:flex}.repository .ui.segment.sub-menu .list .item{width:100%;border-radius:3px}.repository .ui.segment.sub-menu .list .item a{color:#000}.repository .ui.segment.sub-menu .list .item a:hover{color:#666}.repository .ui.segment.sub-menu .list .item.active{background:rgba(0,0,0,.05)}.repository .segment.reactions.dropdown .menu,.repository .select-reaction.dropdown .menu{right:0!important;left:auto!important}.repository .segment.reactions.dropdown .menu>.header,.repository .select-reaction.dropdown .menu>.header{margin:.75rem 0 .5rem}.repository .segment.reactions.dropdown .menu>.item,.repository .select-reaction.dropdown .menu>.item{float:left;padding:.5rem .5rem!important}.repository .segment.reactions.dropdown .menu>.item img.emoji,.repository .select-reaction.dropdown .menu>.item img.emoji{margin-right:0}.repository .segment.reactions{padding:.3em 1em}.repository .segment.reactions .ui.label{padding:.4em}.repository .segment.reactions .ui.label.disabled{cursor:default}.repository .segment.reactions .ui.label>img{height:1.5em!important}.repository .segment.reactions .select-reaction{float:none}.repository .segment.reactions .select-reaction:not(.active) a{display:none}.repository .segment.reactions:hover .select-reaction a{display:block}.user-cards .list{padding:0}.user-cards .list .item{list-style:none;width:32%;margin:10px 10px 10px 0;padding-bottom:14px;float:left}.user-cards .list .item .avatar{width:48px;height:48px;float:left;display:block;margin-right:10px}.user-cards .list .item .name{margin-top:0;margin-bottom:0;font-weight:400}.user-cards .list .item .meta{margin-top:5px}#search-repo-box .results .result .image,#search-user-box .results .result .image{float:left;margin-right:8px;width:2em;height:2em}#search-repo-box .results .result .content,#search-user-box .results .result .content{margin:6px 0}#issue-filters.hide{display:none}#issue-actions{margin-top:-1rem!important}#issue-actions.hide{display:none}.ui.checkbox.issue-checkbox{vertical-align:middle}.issue.list{list-style:none}.issue.list>.item{padding-top:15px;padding-bottom:10px;border-bottom:1px dashed #AAA}.issue.list>.item .title{color:#444;font-size:15px;font-weight:700;margin:0 6px}.issue.list>.item .title:hover{color:#000}.issue.list>.item .comment{padding-right:10px;color:#666}.issue.list>.item .desc{padding-top:5px;color:#999}.issue.list>.item .desc .checklist{padding-left:5px}.issue.list>.item .desc .checklist .progress-bar{margin-left:2px;width:80px;height:6px;display:inline-block;background-color:#eee;overflow:hidden;border-radius:3px;vertical-align:2px!important}.issue.list>.item .desc .checklist .progress-bar .progress{background-color:#ccc;display:block;height:100%}.issue.list>.item .desc a.milestone{padding-left:5px;color:#999!important}.issue.list>.item .desc a.milestone:hover{color:#000!important}.issue.list>.item .desc .assignee{margin-top:-5px;margin-right:5px}.issue.list>.item .desc .overdue{color:red}.page.buttons{padding-top:15px}.ui.form .dropzone{width:100%;margin-bottom:10px;border:2px dashed #0087F7;box-shadow:none!important}.ui.form .dropzone .dz-error-message{top:140px}.settings .content{margin-top:2px}.settings .content .segment,.settings .content>.header{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.settings .list>.item .green{color:#21BA45}.settings .list>.item:not(:first-child){border-top:1px solid #eaeaea;padding:1rem;margin:15px -1rem -1rem -1rem}.settings .list>.item>.mega-octicon{display:table-cell}.settings .list>.item>.mega-octicon+.content{display:table-cell;padding:0 0 0 .5em;vertical-align:top}.settings .list>.item .info{margin-top:10px}.settings .list>.item .info .tab.segment{border:none;padding:10px 0 0}.settings .list.key .meta{padding-top:5px;color:#666}.settings .list.email>.item:not(:first-child){min-height:60px}.settings .list.collaborator>.item{padding:0}.ui.vertical.menu .header.item{font-size:1.1em;background:#f0f0f0}.edit-label.modal .form .column,.new-label.segment .form .column{padding-right:0}.edit-label.modal .form .buttons,.new-label.segment .form .buttons{margin-left:auto;padding-top:15px}.edit-label.modal .form .color.picker.column,.new-label.segment .form .color.picker.column{width:auto}.edit-label.modal .form .color.picker.column .color-picker,.new-label.segment .form .color.picker.column .color-picker{height:35px;width:auto;padding-left:30px}.edit-label.modal .form .minicolors-swatch.minicolors-sprite,.new-label.segment .form .minicolors-swatch.minicolors-sprite{top:10px;left:10px;width:15px;height:15px}.edit-label.modal .form .precolors,.new-label.segment .form .precolors{padding-left:0;padding-right:0;margin:3px 10px auto 10px;width:120px}.edit-label.modal .form .precolors .color,.new-label.segment .form .precolors .color{float:left;width:15px;height:15px}#avatar-arrow:after,#avatar-arrow:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}#avatar-arrow:before{border-right-color:#D4D4D5;border-width:9px;margin-top:-9px}#avatar-arrow:after{border-right-color:#f7f7f7;border-width:8px;margin-top:-8px}#delete-repo-modal .ui.message,#transfer-repo-modal .ui.message{width:100%!important}.tab-size-1{-moz-tab-size:1!important;-o-tab-size:1!important;tab-size:1!important}.tab-size-2{-moz-tab-size:2!important;-o-tab-size:2!important;tab-size:2!important}.tab-size-3{-moz-tab-size:3!important;-o-tab-size:3!important;tab-size:3!important}.tab-size-4{-moz-tab-size:4!important;-o-tab-size:4!important;tab-size:4!important}.tab-size-5{-moz-tab-size:5!important;-o-tab-size:5!important;tab-size:5!important}.tab-size-6{-moz-tab-size:6!important;-o-tab-size:6!important;tab-size:6!important}.tab-size-7{-moz-tab-size:7!important;-o-tab-size:7!important;tab-size:7!important}.tab-size-8{-moz-tab-size:8!important;-o-tab-size:8!important;tab-size:8!important}.tab-size-9{-moz-tab-size:9!important;-o-tab-size:9!important;tab-size:9!important}.tab-size-10{-moz-tab-size:10!important;-o-tab-size:10!important;tab-size:10!important}.tab-size-11{-moz-tab-size:11!important;-o-tab-size:11!important;tab-size:11!important}.tab-size-12{-moz-tab-size:12!important;-o-tab-size:12!important;tab-size:12!important}.tab-size-13{-moz-tab-size:13!important;-o-tab-size:13!important;tab-size:13!important}.tab-size-14{-moz-tab-size:14!important;-o-tab-size:14!important;tab-size:14!important}.tab-size-15{-moz-tab-size:15!important;-o-tab-size:15!important;tab-size:15!important}.tab-size-16{-moz-tab-size:16!important;-o-tab-size:16!important;tab-size:16!important}.stats-table{display:table;width:100%}.stats-table .table-cell{display:table-cell}.stats-table .table-cell.tiny{height:.5em}tbody.commit-list{vertical-align:baseline}.commit-body{white-space:pre-wrap}@media only screen and (max-width:767px){.ui.stackable.menu.mobile--margin-between-items>.item{margin-top:5px;margin-bottom:5px}.ui.stackable.menu.mobile--no-negative-margins{margin-left:0;margin-right:0}}#topic_edit{margin-top:5px}#repo-topics{margin-top:5px}.repo-topic{cursor:pointer}#new-dependency-drop-list.ui.selection.dropdown{min-width:0;width:100%;border-radius:4px 0 0 4px;border-right:0;white-space:nowrap}#new-dependency-drop-list .text{width:100%;overflow:hidden}#manage_topic{font-size:12px}.label+#manage_topic{margin-left:5px}.repo-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap}.repo-header .repo-buttons{display:flex;align-items:center}.repo-buttons .disabled-repo-button .label{opacity:.5}.repo-buttons .disabled-repo-button a.button{opacity:.5;cursor:not-allowed}.repo-buttons .disabled-repo-button a.button:hover{background:0 0!important;color:rgba(0,0,0,.6)!important;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset!important}.repo-buttons .ui.labeled.button>.label{border-left:none!important;margin:0!important}.CodeMirror{font:14px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.CodeMirror.cm-s-default{border-radius:3px;padding:0!important}.CodeMirror .cm-comment{background:inherit!important}.repository.file.editor .tab[data-tab=write]{padding:0!important}.repository.file.editor .tab[data-tab=write] .editor-toolbar{border:none!important}.repository.file.editor .tab[data-tab=write] .CodeMirror{border-left:none;border-right:none;border-bottom:none}.organization{padding-top:15px}.organization .head .ui.header .text{vertical-align:middle;font-size:1.6rem;margin-left:15px}.organization .head .ui.header .ui.right{margin-top:5px}.organization.new.org form{margin:auto}.organization.new.org form .ui.message{text-align:center}@media only screen and (min-width:768px){.organization.new.org form{width:800px!important}.organization.new.org form .header{padding-left:280px!important}.organization.new.org form .inline.field>label{text-align:right;width:250px!important;word-wrap:break-word}.organization.new.org form .help{margin-left:265px!important}.organization.new.org form .optional .title{margin-left:250px!important}.organization.new.org form input,.organization.new.org form textarea{width:50%!important}}@media only screen and (max-width:767px){.organization.new.org form .optional .title{margin-left:15px}.organization.new.org form .inline.field>label{display:block}}.organization.new.org form .header{padding-left:0!important;text-align:center}.organization.options input{min-width:300px}.organization.profile #org-avatar{width:100px;height:100px;margin-right:15px}.organization.profile #org-info .ui.header{font-size:36px;margin-bottom:0}.organization.profile #org-info .desc{font-size:16px;margin-bottom:10px}.organization.profile #org-info .meta .item{display:inline-block;margin-right:10px}.organization.profile #org-info .meta .item .icon{margin-right:5px}.organization.profile .ui.top.header .ui.right{margin-top:0}.organization.profile .teams .item{padding:10px 15px}.organization.profile .members .ui.avatar,.organization.teams .members .ui.avatar{width:48px;height:48px;margin-right:5px}.organization.invite #invite-box{margin:auto;margin-top:50px;width:500px!important}.organization.invite #invite-box #search-user-box input{margin-left:0;width:300px}.organization.invite #invite-box .ui.button{margin-left:5px;margin-top:-3px}.organization.members .list .item{margin-left:0;margin-right:0;border-bottom:1px solid #eee}.organization.members .list .item .ui.avatar{width:48px;height:48px}.organization.members .list .item .meta{line-height:24px}.organization.teams .detail .item{padding:10px 15px}.organization.teams .detail .item:not(:last-child){border-bottom:1px solid #eee}.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #DDD}.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}.user:not(.icon){padding-top:15px}.user.profile .ui.card .username{display:block}.user.profile .ui.card .extra.content{padding:0}.user.profile .ui.card .extra.content ul{margin:0;padding:0}.user.profile .ui.card .extra.content ul li{padding:10px;list-style:none}.user.profile .ui.card .extra.content ul li:not(:last-child){border-bottom:1px solid #eaeaea}.user.profile .ui.card .extra.content ul li .octicon{margin-left:1px;margin-right:5px}.user.profile .ui.card .extra.content ul li.follow .ui.button{width:100%}@media only screen and (max-width:768px){.user.profile .ui.card #profile-avatar{height:250px;overflow:hidden}.user.profile .ui.card #profile-avatar img{max-height:768px;max-width:768px}}@media only screen and (max-width:768px){.user.profile .ui.card{width:100%}}.user.profile .ui.repository.list{margin-top:25px}.user.profile #loading-heatmap{margin-bottom:1em}.user.followers .header.name{font-size:20px;line-height:24px;vertical-align:middle}.user.followers .follow .ui.button{padding:8px 15px}.user.notification .octicon{float:left;font-size:2em}.user.notification .content{float:left;margin-left:7px}.user.notification table form{display:inline-block}.user.notification table button{padding:3px 3px 3px 5px}.user.notification table tr{cursor:pointer}.user.notification .octicon.green{color:#21ba45}.user.notification .octicon.red{color:#d01919}.user.notification .octicon.purple{color:#a333c8}.user.notification .octicon.blue{color:#2185d0}.user.link-account:not(.icon){padding-top:15px;padding-bottom:5px}.user.settings .iconFloat{float:left}.dashboard{padding-top:15px}.dashboard.feeds .context.user.menu,.dashboard.issues .context.user.menu{z-index:101;min-width:200px}.dashboard.feeds .context.user.menu .ui.header,.dashboard.issues .context.user.menu .ui.header{font-size:1rem;text-transform:none}.dashboard.feeds .filter.menu .item,.dashboard.issues .filter.menu .item{text-align:left}.dashboard.feeds .filter.menu .item .text,.dashboard.issues .filter.menu .item .text{height:16px;vertical-align:middle}.dashboard.feeds .filter.menu .item .text.truncate,.dashboard.issues .filter.menu .item .text.truncate{width:85%}.dashboard.feeds .filter.menu .item .floating.label,.dashboard.issues .filter.menu .item .floating.label{top:7px;left:90%;width:15%}@media only screen and (max-width:768px){.dashboard.feeds .filter.menu .item .floating.label,.dashboard.issues .filter.menu .item .floating.label{top:10px;left:auto;width:auto;right:13px}}.dashboard.feeds .filter.menu .jump.item,.dashboard.issues .filter.menu .jump.item{margin:1px;padding-right:0}.dashboard.feeds .filter.menu .menu,.dashboard.issues .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}@media only screen and (max-width:768px){.dashboard.feeds .filter.menu,.dashboard.issues .filter.menu{width:100%}}.dashboard.feeds .right.stackable.menu>.item.active,.dashboard.issues .right.stackable.menu>.item.active{color:#d9453d}.dashboard .dashboard-repos{margin:0 1px}.dashboard .dashboard-navbar{width:100vw;padding:0 .5rem}.feeds .news>.ui.grid{margin-left:auto;margin-right:auto}.feeds .news .ui.avatar{margin-top:13px}.feeds .news .time-since{font-size:13px}.feeds .news .issue.title{width:80%}.feeds .news .push.news .content ul{font-size:13px;list-style:none;padding-left:10px}.feeds .news .push.news .content ul img{margin-bottom:-2px}.feeds .news .push.news .content ul .text.truncate{width:80%;margin-bottom:-5px}.feeds .news .commit-id{font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}.feeds .news code{padding:1px;font-size:85%;background-color:rgba(0,0,0,.04);border-radius:3px;word-break:break-all}.feeds .list .header .ui.label{margin-top:-4px;padding:4px 5px;font-weight:400}.feeds .list .header .plus.icon{margin-top:5px}.feeds .list ul{list-style:none;margin:0;padding-left:0}.feeds .list ul li:not(:last-child){border-bottom:1px solid #EAEAEA}.feeds .list ul li.private{background-color:#fcf8e9}.feeds .list ul li a{padding:6px 1.2em;display:block}.feeds .list ul li a .octicon{color:#888}.feeds .list ul li a .octicon.rear{font-size:15px}.feeds .list ul li a .star-num{font-size:12px}.feeds .list .repo-owner-name-list .item-name{max-width:70%;margin-bottom:-4px}.feeds .list #collaborative-repo-list .owner-and-repo{max-width:80%;margin-bottom:-5px}.feeds .list #collaborative-repo-list .owner-name{max-width:120px;margin-bottom:-5px}.admin{padding-top:15px}.admin .table.segment{padding:0;font-size:13px}.admin .table.segment:not(.striped){padding-top:5px}.admin .table.segment:not(.striped) thead th:last-child{padding-right:5px!important}.admin .table.segment th{padding-top:5px;padding-bottom:5px}.admin .table.segment:not(.select) td:first-of-type,.admin .table.segment:not(.select) th:first-of-type{padding-left:15px!important}.admin .ui.header,.admin .ui.segment{box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.admin.user .email{max-width:200px}.admin dl.admin-dl-horizontal{padding:20px;margin:0}.admin dl.admin-dl-horizontal dd{margin-left:275px}.admin dl.admin-dl-horizontal dt{font-weight:bolder;float:left;width:285px;clear:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.admin.config #test-mail-btn{margin-left:5px}.explore{padding-top:15px}.explore .navbar{justify-content:center;padding-top:15px!important;margin-top:-15px!important;margin-bottom:15px!important;background-color:#FAFAFA!important;border-width:1px!important}.explore .navbar .octicon{width:16px;text-align:center;margin-right:5px}.ui.repository.list .item{padding-bottom:25px}.ui.repository.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.repository.list .item .ui.header{font-size:1.5rem;padding-bottom:10px}.ui.repository.list .item .ui.header .name{word-break:break-all}.ui.repository.list .item .ui.header .metas{color:#888;font-size:14px;font-weight:400}.ui.repository.list .item .ui.header .metas span:not(:last-child){margin-right:5px}.ui.repository.list .item .time{font-size:12px;color:grey}.ui.repository.list .item .ui.tags{margin-bottom:1em}.ui.repository.branches .time{font-size:12px;color:grey}.ui.user.list .item{padding-bottom:25px}.ui.user.list .item:not(:first-child){border-top:1px solid #eee;padding-top:25px}.ui.user.list .item .ui.avatar.image{width:40px;height:40px}.ui.user.list .item .description{margin-top:5px}.ui.user.list .item .description .octicon:not(:first-child){margin-left:5px}.ui.user.list .item .description a{color:#333}.ui.user.list .item .description a:hover{text-decoration:underline}.ui.button.add-code-comment{font-size:14px;height:16px;padding:2px 0 0;position:relative;width:16px;z-index:5;float:left;margin:-2px -10px -2px -20px;opacity:0;transition:transform .1s ease-in-out;transform:scale(1,1)}.ui.button.add-code-comment:hover{transform:scale(1.2,1.2)}.focus-lines-new .ui.button.add-code-comment.add-code-comment-right,.focus-lines-old .ui.button.add-code-comment.add-code-comment-left{opacity:1}.comment-code-cloud{padding:4px;margin:0 auto;position:relative;border:1px solid #f1f1f1;margin-top:13px;margin-right:10px;margin-bottom:5px}.comment-code-cloud:before{content:" ";width:0;height:0;border-left:13px solid transparent;border-right:13px solid transparent;border-bottom:13px solid #f1f1f1;left:20px;position:absolute;top:-13px}.comment-code-cloud .attached.tab{border:none;padding:0;margin:0}.comment-code-cloud .attached.tab.markdown{padding:1em;min-height:168px}.comment-code-cloud .attached.header{padding:.1rem 1rem}.comment-code-cloud .right.menu.options .item{padding:.85714286em .442857em;cursor:pointer}.comment-code-cloud .ui.form textarea{border:0}.comment-code-cloud .ui.attached.tabular.menu{background:#f7f7f7;border:1px solid #d4d4d5;padding-top:5px;padding-left:5px;margin-top:0}.comment-code-cloud .footer{border-top:1px solid #f1f1f1;margin-top:10px}.comment-code-cloud .footer .markdown-info{display:inline-block;margin:5px 0;font-size:12px;color:rgba(0,0,0,.6)}.comment-code-cloud .footer .ui.right.floated{padding-top:6px}.comment-code-cloud .footer:after{clear:both;content:"";display:block}.comment-code-cloud button.comment-form-reply{margin:.5em .5em .5em 4.5em}.comment-code-cloud form.comment-form-reply{margin:0 0 0 4em}.file-comment{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.87)} \ No newline at end of file diff --git a/public/less/_repository.less b/public/less/_repository.less index 5eb14ef059..c746f5bbcf 100644 --- a/public/less/_repository.less +++ b/public/less/_repository.less @@ -2244,9 +2244,18 @@ tbody.commit-list { cursor: pointer; } -@media only screen and (max-width: 768px) { - .new-dependency-drop-list { +#new-dependency-drop-list { + &.ui.selection.dropdown { + min-width: 0; + width: 100%; + border-radius: 4px 0 0 4px; + border-right: 0; + white-space: nowrap; + } + + .text { width: 100%; + overflow: hidden; } } diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 4db5628faa..853f681474 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -321,7 +321,7 @@
{{$.CsrfTokenHtml}}
-