Compare commits

..

38 Commits

Author SHA1 Message Date
6543
2ccd92407a Changelog for 1.10.2 (#9585)
* Changelog for 1.10.2

* imprufe

* fix misspell

* Update CHANGELOG.md
2020-01-02 19:04:19 +08:00
6543
17c691f8aa [Backport] [API] Allow only specific Colums to be updated on Issue (#9539) (#9580)
* Fix #9189 - API Allow only specific Colums to be updated on Issue (#9539)

* dont insert "-1" in any case to issue.poster_id

* Make sure API cant override importand fields

* code format

* add Test for IssueEdit

* load milestone and return it on IssueEdit via API

* extend Test for TestAPIEditIssue

* fix TEST

* make sure Poster is loaded

* keep code format on backport as it is
2020-01-02 02:38:42 -05:00
6543
cb3fe4cbf1 [Backport] Fix bug when migrate from API (#8631) (#9563)
* Fix bug when migrate from API (#8631)

* fix bug when migrate from API

* fix test

* fix test

* improve

* fix error message

* Update routers/api/v1/repo/repo.go

* remove unrelated

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-01-02 01:36:00 +02:00
6543
c63a80138a backport part of #9550 (#9564)
* add ErrReactionAlreadyExist
 * extend FindReactionsOptions
 * createReaction check if exit before create
2019-12-31 19:00:17 +02:00
6543
b4b8c9679f fix 500 error for ghost avatar (#9537) 2019-12-30 18:55:51 +08:00
Lunny Xiao
c0bb5ebc15 Fix repository issues pagination bug when there are more than one label filter (#9512) (#9528)
* Fix repository issues pagination bug when there are more than one label filter (#9512)

* fix merge
2019-12-28 23:54:24 +00:00
Lunny Xiao
9409ac9030 Fix deleted branch isn't removed when push the branch again (#9516) (#9524) 2019-12-28 12:41:41 +08:00
Lunny Xiao
a3928fd820 Fix missing repository status when migrating repository via API (#9511)
* Fix API migration wrong repository status

* Force push for ci
2019-12-27 14:34:28 +02:00
Brad Albright
f0bda12c49 when branch is deleted after a pull request is merged, trigger webhook (#9510) 2019-12-27 14:36:52 +08:00
Brad Albright
58c38ab4b6 backport fix: fixed bug in GitTreeBySHA where pulling items from a page other than page 1 would fail because the wrong var was used to set the entries to return (#9482) 2019-12-24 07:01:02 +00:00
John Olheiser
a276aaf61e Fix NewCommitStatus (#9434) (#9435)
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-12-19 23:49:47 +01:00
mrsdizzie
e03934f035 Use OriginalURL insead of CloneAddr in migration logging (#9418) (#9420)
CloneAddr will contain username and password credentials and they will
get stored in system notices about failed migrations (and logs if trace
is set). Replace with OriginalURL that doesn't have those.
2019-12-18 21:02:47 -05:00
Cornel
3a14a69e8a Fix Slack webhook payload title generation to work with Mattermost (#9378) (#9404) 2019-12-18 20:56:35 +08:00
zeripath
f0f48e0fff DefaultBranch needs to be prefixed by BranchPrefix (#9356) (#9359) 2019-12-15 11:08:19 +00:00
Lunny Xiao
1aeeaa8e89 fix issue indexer not triggered when migrating a repository (#9333) 2019-12-13 13:07:11 +08:00
Lunny Xiao
eb8d5f6aff Fix bug that release attachment files not deleted when deleting repository (#9322) (#9329)
* Fix bug that release attachment files not deleted when deleting repository

* improve code

* add quote

* improve code
2019-12-12 15:05:44 +08:00
John Olheiser
9ef148abeb Fix migration releases (#9319) (#9326) (#9328)
* Only sync tags after all batches (#9319)

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* Add SyncTags to uploader interface (#9326)

* Add sync tags to interface

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* Fix revive

Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-12-11 23:38:58 -05:00
6543
f11df80058 File Edit: Author/Committer interchanged [BugFix] (#9300) 2019-12-09 21:12:36 +08:00
6543
59913f405c Changelog 1.10.1 (#9256)
* Changelog for 1.10.1

* format ...

* format

* add @techknowlogick s suggestion
2019-12-05 13:23:51 -05:00
6543
42dae399eb Fix max length check and limit in multiple repo forms (#9148) (#9204)
* Fix input field max length for release, label and milestone forms

* Add max length for isseu and PR title
2019-11-29 14:00:19 +08:00
6543
f36efe0b54 Properly fix displaying virtual session provider in admin panel (#9137) (#9203)
* Properly fix #7147

Although #7300 properly shadows the password from the virtual session
provider, the template displaying the provider config still presumed
that the config was JSON.

This PR updates the template and properly hides the Virtual Session
provider.

Fixes #7147

* update per @silverwind's suggestion
2019-11-29 00:28:38 -05:00
Lunny Xiao
38664d7f39 upgrade levelqueue to 0.1.0 (#9192) (#9199) 2019-11-28 17:40:49 +01:00
Lunny Xiao
c4e52d232e Fix panic when diff (#9187) (#9193)
* fix panic when diff

* improve code
2019-11-28 10:07:30 -05:00
zeripath
2b257a91de Fix #9151 - smtp logger configuration sendTos should be an arra… (#9157)
* Fix #9151 - sendTos should be an array

* trimspace from the addresses
2019-11-25 16:18:16 +01:00
Benno
c01afd584d backport v1.10 - Always show Password field on link account sign in page (#9150) 2019-11-24 18:38:05 -05:00
guillep2k
1270e2ad85 Create PR on Current Repository by Default (#8670) (#9141)
* 'update'

* Send push tag event when release created

* send tag create event while release created in UI

* update to go v1.13

* fix gofmt error

* fix #8576 create pull request on current repository by default
2019-11-24 03:08:40 +01:00
Lunny Xiao
29fa3a0f68 fix race on indexer (#9136) (#9139) 2019-11-23 20:08:49 +02:00
Koichi MATSUMOTO
65a573f3c7 Fix reCAPTCHA URL (#9119)
Fix reCAPTCHA issue
2019-11-22 14:48:58 +00:00
Jordan
ade5ec5aa7 Backport: Hide given credentials for migrated repos. (#9098)
CloneAddr was being used as OriginalURL.
Now passing OriginalURL through from the form and saving it.
2019-11-20 17:23:12 -05:00
techknowlogick
38ce87a61a backport: update golang.org/x/crypto vendor to use acme v2 (#9056) (#9085) 2019-11-20 02:12:47 -05:00
guillep2k
261b19ced7 Backport: Fix password checks on admin create/edit user (#9076) (#9081)
* Fix password checks on admin create/edit user

* Remove incorrect trimspace
2019-11-20 00:08:53 +00:00
Antoine GIRARD
6ef0ab4d96 fix: add search to reserved usernames (#9063) (#9065)
* fix: add search to reserved usernames

* Update integrations/user_test.go

Co-Authored-By: 6543 <24977596+6543@users.noreply.github.com>
2019-11-18 22:40:14 +00:00
guillep2k
ecdb4c1750 Fix permission checks for close/reopen from commit (#8875) (#9033)
* Fix checks for close/reopen from commit

* Fix permission order
2019-11-16 00:11:40 +02:00
zeripath
a0e76de75a Ensure Written is set in GZIP ProxyResponseWriter (#9018) (#9025)
Fix #9001

The GZIP ProxyReponseWriter doesn't currently respond correctly
to requests about its Written status - leading to #9001.

This PR properly reimplements these methods.
2019-11-15 15:57:57 +00:00
6543
880f26c7f0 Fix broken link to branch from issue list (#9003) (#9021)
* Fix broken link to branch from issue list

* Update templates/repo/issue/list.tmpl
2019-11-15 14:10:28 +00:00
Lunny Xiao
fd461ca555 fix wrong system notice when repository is empty (#9020) 2019-11-15 20:13:58 +08:00
zeripath
3487fb66a1 Shadow password correctly for session config (#8984) (#9002)
Fix #8718

This PR shadows passwords in session config correctly by detecting
the VirtualProvider, unmarshalling the original config and then
shadowing config within that.
2019-11-14 22:34:47 +00:00
John Olheiser
1122230d0e Update security (#8990)
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-11-14 16:56:17 +00:00
149 changed files with 4958 additions and 3253 deletions
+43 -2
View File
@@ -4,6 +4,47 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
* BUGFIXES
* Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
* Add ErrReactionAlreadyExist error (#9550) (#9564)
* Fix bug when migrate from API (#8631) (#9563)
* Use default avatar for ghost user (#9536) (#9537)
* Fix repository issues pagination bug when there are more than one label filter (#9512) (#9528)
* Fix deleted branch not removed when push the branch again (#9516) (#9524)
* Fix missing repository status when migrating repository via API (#9511)
* Trigger webhook when deleting a branch after merging a PR (#9510)
* Fix paging on /repos/{owner}/{repo}/git/trees/{sha} API endpoint (#9482)
* Fix NewCommitStatus (#9434) (#9435)
* Use OriginalURL instead of CloneAddr in migration logging (#9418) (#9420)
* Fix Slack webhook payload title generation to work with Mattermost (#9404)
* DefaultBranch needs to be prefixed by BranchPrefix (#9356) (#9359)
* Fix issue indexer not triggered when migrating a repository (#9333)
* Fix bug that release attachment files not deleted when deleting repository (#9322) (#9329)
* Fix migration releases (#9319) (#9326) (#9328)
* Fix File Edit: Author/Committer interchanged (#9297) (#9300)
## [1.10.1](https://github.com/go-gitea/gitea/releases/tag/v1.10.1) - 2019-12-05
* BUGFIXES
* Fix max length check and limit in multiple repo forms (#9148) (#9204)
* Properly fix displaying virtual session provider in admin panel (#9137) (#9203)
* Upgrade levelqueue to 0.1.0 (#9192) (#9199)
* Fix panic when diff (#9187) (#9193)
* Smtp logger configuration sendTos should be an array (#9154) (#9157)
* Always Show Password Field on Link Account Sign-in Page (#9150)
* Create PR on Current Repository by Default (#8670) (#9141)
* Fix race on indexer (#9136) (#9139)
* Fix reCAPTCHA URL (#9119)
* Hide migrated credentials (#9098)
* Update golang.org/x/crypto vendor to use acme v2 (#9056) (#9085)
* Fix password checks on admin create/edit user (#9076) (#9081)
* Fix add search as a reserved username (#9063) (#9065)
* Fix permission checks for close/reopen from commit (#8875) (#9033)
* Ensure Written is set in GZIP ProxyResponseWriter (#9018) (#9025)
* Fix broken link to branch from issue list (#9003) (#9021)
* Fix wrong system notice when repository is empty (#9020)
* Shadow password correctly for session config (#8984) (#9002)
## [1.10.0](https://github.com/go-gitea/gitea/releases/tag/v1.10.0) - 2019-11-13
* BREAKING
* Fix deadline on update issue or PR via API (#8698)
@@ -13,8 +54,10 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Add pagination for admin api get orgs and fix only list public orgs bug (#7742)
* Implement the ability to change the ssh port to match what is in the gitea config (#7286)
* SECURITY
* Fix issue with user.fullname (#8903)
* Ignore mentions for users with no access (#8395)
* Be more strict with git arguments (#7715)
* Extract the username and password from the mirror url (#7651)
* reserve .well-known username (#7637)
* FEATURE
* Org/Members: display 2FA members states + optimize sql requests (#7621)
@@ -37,7 +80,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Fix require external registration password (#8885) (#8890)
* Fix password complexity check on registration (#8887) (#8888)
* Update Github Migration Tests (#8896) (#8938) (#8945)
* Fix issue with user.fullname (#8903)
* Enable punctuations ending mentions (#8889) (#8894)
* Add Close() method to gogitRepository (#8901) (#8956)
* Hotfix for review actions and notifications (#8965)
@@ -288,7 +330,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Fix global search result CSS, misc CSS tweaks (#7789)
* Tweak label border CSS (#7739)
* Fix create menu item widths (#7708)
* Extract the username and password from the mirror url (#7651)
* [Branch View] Delete duplicate protection symbol (#7624)
* [Branch View] Delete Table Header (#7622)
* [Branch View] icons to buttons (#7602)
+2 -2
View File
@@ -4,6 +4,7 @@ go 1.13
require (
cloud.google.com/go v0.45.0 // indirect
gitea.com/lunny/levelqueue v0.1.0
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b
gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76
gitea.com/macaron/captcha v0.0.0-20190822015246-daa973478bae
@@ -67,7 +68,6 @@ require (
github.com/lafriks/xormstore v1.3.1
github.com/lib/pq v1.2.0
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e
github.com/mailru/easyjson v0.7.0 // indirect
github.com/markbates/goth v1.56.0
github.com/mattn/go-isatty v0.0.7
@@ -104,7 +104,7 @@ require (
github.com/urfave/cli v1.20.0
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 // indirect
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b
+4 -2
View File
@@ -12,6 +12,8 @@ cloud.google.com/go v0.45.0 h1:bALuGBSgE+BD4rxsopAYlqjcwqcQtye6pWG4bC3N/k0=
cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
gitea.com/lunny/levelqueue v0.1.0 h1:7wMk0VH6mvKN6vZEZCy9nUDgRmdPLgeNrm1NkW8EHNk=
gitea.com/lunny/levelqueue v0.1.0/go.mod h1:G7hVb908t0Bl0uk7zGSg14fyzNtxgtD9Shf04wkMK7s=
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b h1:vXt85uYV17KURaUlhU7v4GbCShkqRZDSfo0TkC0YCjQ=
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b/go.mod h1:Cxadig6POWpPYYSfg23E7jo35Yf0yvsdC1lifoKWmPo=
gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76 h1:mMsMEg90c5KXQgRWsH8D6GHXfZIW1RAe5S9VYIb12lM=
@@ -391,8 +393,6 @@ github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY=
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e h1:GSprKUrG9wNgwQgROvjPGXmcZrg4OLslOuZGB0uJjx8=
github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e/go.mod h1:rQZVENnBOiVakCs97XvclbwJRTAv77CRFWcYVNDkVf8=
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de h1:nyxwRdWHAVxpFcDThedEgQ07DbcRc5xgNObtbTp76fk=
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af h1:UaWHNBdukWrSG3DRvHFR/hyfg681fceqQDYVTBncKfQ=
@@ -627,6 +627,8 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49N
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba h1:9bFeDpN3gTqNanMVqNcoR/pJQuP5uroC3t1D7eXozTE=
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
+59
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"net/http"
"testing"
"time"
"code.gitea.io/gitea/models"
api "code.gitea.io/gitea/modules/structs"
@@ -62,3 +63,61 @@ func TestAPICreateIssue(t *testing.T) {
Title: title,
})
}
func TestAPIEditIssue(t *testing.T) {
prepareTestEnv(t)
issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 9}).(*models.Issue)
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
assert.NoError(t, issueBefore.LoadAttributes())
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
assert.Equal(t, api.StateOpen, issueBefore.State())
session := loginUser(t, owner.Name)
token := getTokenForLoggedInUser(t, session)
// update values of issue
issueState := "closed"
removeDeadline := time.Unix(0, 0)
milestone := int64(4)
body := "new content!"
title := "new title from api set"
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repo.Name, issueBefore.Index, token)
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
State: &issueState,
Deadline: &removeDeadline,
Milestone: &milestone,
Body: &body,
Title: title,
// ToDo change more
})
resp := session.MakeRequest(t, req, http.StatusCreated)
var apiIssue api.Issue
DecodeJSON(t, resp, &apiIssue)
issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 9}).(*models.Issue)
// check deleted user
assert.Equal(t, int64(500), issueAfter.PosterID)
assert.NoError(t, issueAfter.LoadAttributes())
assert.Equal(t, int64(-1), issueAfter.PosterID)
assert.Equal(t, int64(-1), issueBefore.PosterID)
assert.Equal(t, int64(-1), apiIssue.Poster.ID)
// API response
assert.Equal(t, api.StateClosed, apiIssue.State)
assert.Equal(t, milestone, apiIssue.Milestone.ID)
assert.Equal(t, body, apiIssue.Body)
assert.True(t, apiIssue.Deadline == nil)
assert.Equal(t, title, apiIssue.Title)
// in database
assert.Equal(t, api.StateClosed, issueAfter.State())
assert.Equal(t, milestone, issueAfter.MilestoneID)
assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix))
assert.Equal(t, body, issueAfter.Content)
assert.Equal(t, title, issueAfter.Title)
}
+6 -6
View File
@@ -30,12 +30,12 @@ func getCreateFileOptions() api.CreateFileOptions {
NewBranchName: "master",
Message: "Making this new file new/file.txt",
Author: api.Identity{
Name: "John Doe",
Email: "johndoe@example.com",
Name: "Anne Doe",
Email: "annedoe@example.com",
},
Committer: api.Identity{
Name: "Jane Doe",
Email: "janedoe@example.com",
Name: "John Doe",
Email: "johndoe@example.com",
},
},
Content: contentEncoded,
@@ -77,8 +77,8 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
Author: &api.CommitUser{
Identity: api.Identity{
Name: "Jane Doe",
Email: "janedoe@example.com",
Name: "Anne Doe",
Email: "annedoe@example.com",
},
},
Committer: &api.CommitUser{
+6 -6
View File
@@ -35,8 +35,8 @@ func getUpdateFileOptions() *api.UpdateFileOptions {
Email: "johndoe@example.com",
},
Committer: api.Identity{
Name: "Jane Doe",
Email: "janedoe@example.com",
Name: "Anne Doe",
Email: "annedoe@example.com",
},
},
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
@@ -80,14 +80,14 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
Author: &api.CommitUser{
Identity: api.Identity{
Name: "Jane Doe",
Email: "janedoe@example.com",
Name: "John Doe",
Email: "johndoe@example.com",
},
},
Committer: &api.CommitUser{
Identity: api.Identity{
Name: "John Doe",
Email: "johndoe@example.com",
Name: "Anne Doe",
Email: "annedoe@example.com",
},
},
Message: "My update of README.md\n",
+1 -1
View File
@@ -334,7 +334,7 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) {
resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
respJSON := map[string]string{}
DecodeJSON(t, resp, &respJSON)
assert.Equal(t, respJSON["message"], "The repository with the same name already exists.")
assert.Equal(t, "The repository with the same name already exists.", respJSON["message"])
})
}
+1
View File
@@ -90,6 +90,7 @@ func TestRenameReservedUsername(t *testing.T) {
"repo",
"template",
"user",
"search",
}
session := loginUser(t, "user2")
+30 -17
View File
@@ -533,31 +533,44 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra
}
refMarked[key] = true
// only create comments for issues if user has permission for it
if perm.IsAdmin() || perm.IsOwner() || perm.CanWrite(UnitTypeIssues) {
message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, html.EscapeString(c.Message))
if err = CreateRefComment(doer, refRepo, refIssue, message, c.Sha1); err != nil {
return err
}
// FIXME: this kind of condition is all over the code, it should be consolidated in a single place
canclose := perm.IsAdmin() || perm.IsOwner() || perm.CanWrite(UnitTypeIssues) || refIssue.PosterID == doer.ID
cancomment := canclose || perm.CanRead(UnitTypeIssues)
// Don't proceed if the user can't comment
if !cancomment {
continue
}
// Process closing/reopening keywords
message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, html.EscapeString(c.Message))
if err = CreateRefComment(doer, refRepo, refIssue, message, c.Sha1); err != nil {
return err
}
// Only issues can be closed/reopened this way, and user needs the correct permissions
if refIssue.IsPull || !canclose {
continue
}
// Only process closing/reopening keywords
if ref.Action != references.XRefActionCloses && ref.Action != references.XRefActionReopens {
continue
}
// Change issue status only if the commit has been pushed to the default branch.
// and if the repo is configured to allow only that
// FIXME: we should be using Issue.ref if set instead of repo.DefaultBranch
if repo.DefaultBranch != branchName && !repo.CloseIssuesViaCommitInAnyBranch {
continue
if !repo.CloseIssuesViaCommitInAnyBranch {
// If the issue was specified to be in a particular branch, don't allow commits in other branches to close it
if refIssue.Ref != "" {
if branchName != refIssue.Ref {
continue
}
// Otherwise, only process commits to the default branch
} else if branchName != repo.DefaultBranch {
continue
}
}
// only close issues in another repo if user has push access
if perm.IsAdmin() || perm.IsOwner() || perm.CanWrite(UnitTypeCode) {
if err := changeIssueStatus(refRepo, refIssue, doer, ref.Action == references.XRefActionCloses); err != nil {
return err
}
if err := changeIssueStatus(refRepo, refIssue, doer, ref.Action == references.XRefActionCloses); err != nil {
return err
}
}
}
+2 -2
View File
@@ -219,7 +219,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
PosterID: user.ID,
IssueID: 1,
}
issueBean := &Issue{RepoID: repo.ID, Index: 2}
issueBean := &Issue{RepoID: repo.ID, Index: 4}
AssertNotExistsBean(t, commentBean)
AssertNotExistsBean(t, &Issue{RepoID: repo.ID, Index: 2}, "is_closed=1")
@@ -273,7 +273,7 @@ func TestUpdateIssuesCommit_Colon(t *testing.T) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
repo.Owner = user
issueBean := &Issue{RepoID: repo.ID, Index: 2}
issueBean := &Issue{RepoID: repo.ID, Index: 4}
AssertNotExistsBean(t, &Issue{RepoID: repo.ID, Index: 2}, "is_closed=1")
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
+6
View File
@@ -480,6 +480,12 @@ func (deletedBranch *DeletedBranch) LoadUser() {
deletedBranch.DeletedBy = user
}
// RemoveDeletedBranch removes all deleted branches
func RemoveDeletedBranch(repoID int64, branch string) error {
_, err := x.Where("repo_id=? AND name=?", repoID, branch).Delete(new(DeletedBranch))
return err
}
// RemoveOldDeletedBranches removes old deleted branches
func RemoveOldDeletedBranches() {
log.Trace("Doing: DeletedBranchesCleanup")
+15
View File
@@ -1104,6 +1104,21 @@ func (err ErrNewIssueInsert) Error() string {
return err.OriginalError.Error()
}
// ErrReactionAlreadyExist is used when a existing reaction was try to created
type ErrReactionAlreadyExist struct {
Reaction string
}
// IsErrReactionAlreadyExist checks if an error is a ErrReactionAlreadyExist.
func IsErrReactionAlreadyExist(err error) bool {
_, ok := err.(ErrReactionAlreadyExist)
return ok
}
func (err ErrReactionAlreadyExist) Error() string {
return fmt.Sprintf("reaction '%s' already exists", err.Reaction)
}
// __________ .__ .__ __________ __
// \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
+14 -1
View File
@@ -96,4 +96,17 @@
is_closed: false
is_pull: true
created_unix: 946684820
updated_unix: 978307180
updated_unix: 978307180
-
id: 9
repo_id: 42
index: 1
poster_id: 500
name: issue from deleted account
content: content from deleted account
is_closed: false
is_pull: false
created_unix: 946684830
updated_unix: 999307200
deadline_unix: 1019307200
+8
View File
@@ -21,3 +21,11 @@
content: content3
is_closed: true
num_issues: 0
-
id: 4
repo_id: 42
name: milestone of repo42
content: content random
is_closed: false
num_issues: 0
+2 -1
View File
@@ -547,7 +547,8 @@
is_private: false
num_stars: 0
num_forks: 0
num_issues: 0
num_issues: 1
num_milestones: 1
is_mirror: false
-
+24 -16
View File
@@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@@ -238,6 +239,16 @@ func (issue *Issue) loadReactions(e Engine) (err error) {
return nil
}
func (issue *Issue) loadMilestone(e Engine) (err error) {
if issue.Milestone == nil && issue.MilestoneID > 0 {
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
if err != nil && !IsErrMilestoneNotExist(err) {
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
}
}
return nil
}
func (issue *Issue) loadAttributes(e Engine) (err error) {
if err = issue.loadRepo(e); err != nil {
return
@@ -251,11 +262,8 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
return
}
if issue.Milestone == nil && issue.MilestoneID > 0 {
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
if err != nil && !IsErrMilestoneNotExist(err) {
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
}
if err = issue.loadMilestone(e); err != nil {
return
}
if err = issue.loadAssignees(e); err != nil {
@@ -295,6 +303,11 @@ func (issue *Issue) LoadAttributes() error {
return issue.loadAttributes(x)
}
// LoadMilestone load milestone of this issue.
func (issue *Issue) LoadMilestone() error {
return issue.loadMilestone(x)
}
// GetIsRead load the `IsRead` field of the issue
func (issue *Issue) GetIsRead(userID int64) error {
issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
@@ -1730,22 +1743,17 @@ func SearchIssueIDsByKeyword(kw string, repoID int64, limit, start int) (int64,
return total, ids, nil
}
func updateIssue(e Engine, issue *Issue) error {
_, err := e.ID(issue.ID).AllCols().Update(issue)
if err != nil {
return err
}
return nil
}
// UpdateIssue updates all fields of given issue.
func UpdateIssue(issue *Issue) error {
// UpdateIssueByAPI updates all allowed fields of given issue.
func UpdateIssueByAPI(issue *Issue) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if err := updateIssue(sess, issue); err != nil {
if _, err := sess.ID(issue.ID).Cols(
"name", "is_closed", "content", "milestone_id", "priority",
"deadline_unix", "updated_unix", "closed_unix", "is_locked").
Update(issue); err != nil {
return err
}
if err := issue.neuterCrossReferences(sess); err != nil {
+30 -5
View File
@@ -30,6 +30,8 @@ type Reaction struct {
type FindReactionsOptions struct {
IssueID int64
CommentID int64
UserID int64
Reaction string
}
func (opts *FindReactionsOptions) toConds() builder.Cond {
@@ -40,6 +42,13 @@ func (opts *FindReactionsOptions) toConds() builder.Cond {
if opts.CommentID > 0 {
cond = cond.And(builder.Eq{"reaction.comment_id": opts.CommentID})
}
if opts.UserID > 0 {
cond = cond.And(builder.Eq{"reaction.user_id": opts.UserID})
}
if opts.Reaction != "" {
cond = cond.And(builder.Eq{"reaction.type": opts.Reaction})
}
return cond
}
@@ -57,9 +66,25 @@ func createReaction(e *xorm.Session, opts *ReactionOptions) (*Reaction, error) {
UserID: opts.Doer.ID,
IssueID: opts.Issue.ID,
}
findOpts := FindReactionsOptions{
IssueID: opts.Issue.ID,
CommentID: -1, // reaction to issue only
Reaction: opts.Type,
UserID: opts.Doer.ID,
}
if opts.Comment != nil {
reaction.CommentID = opts.Comment.ID
findOpts.CommentID = opts.Comment.ID
}
existingR, err := findReactions(e, findOpts)
if err != nil {
return nil, err
}
if len(existingR) > 0 {
return existingR[0], ErrReactionAlreadyExist{Reaction: opts.Type}
}
if _, err := e.Insert(reaction); err != nil {
return nil, err
}
@@ -76,19 +101,19 @@ type ReactionOptions struct {
}
// CreateReaction creates reaction for issue or comment.
func CreateReaction(opts *ReactionOptions) (reaction *Reaction, err error) {
func CreateReaction(opts *ReactionOptions) (*Reaction, error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
if err := sess.Begin(); err != nil {
return nil, err
}
reaction, err = createReaction(sess, opts)
reaction, err := createReaction(sess, opts)
if err != nil {
return nil, err
return reaction, err
}
if err = sess.Commit(); err != nil {
if err := sess.Commit(); err != nil {
return nil, err
}
return reaction, nil
+6 -7
View File
@@ -50,9 +50,10 @@ func TestIssueAddDuplicateReaction(t *testing.T) {
Type: "heart",
})
assert.Error(t, err)
assert.Nil(t, reaction)
assert.Equal(t, ErrReactionAlreadyExist{Reaction: "heart"}, err)
AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID})
existingR := AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID}).(*Reaction)
assert.Equal(t, existingR.ID, reaction.ID)
}
func TestIssueDeleteReaction(t *testing.T) {
@@ -129,7 +130,6 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
user3 := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
ghost := NewGhostUser()
issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
@@ -139,14 +139,13 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
addReaction(t, user2, issue1, comment1, "heart")
addReaction(t, user3, issue1, comment1, "heart")
addReaction(t, user4, issue1, comment1, "+1")
addReaction(t, ghost, issue1, comment1, "heart")
err := comment1.LoadReactions()
assert.NoError(t, err)
assert.Len(t, comment1.Reactions, 5)
assert.Len(t, comment1.Reactions, 4)
reactions := comment1.Reactions.GroupByType()
assert.Len(t, reactions["heart"], 4)
assert.Len(t, reactions["heart"], 3)
assert.Len(t, reactions["+1"], 1)
}
@@ -160,7 +159,7 @@ func TestIssueCommentReactionCount(t *testing.T) {
comment1 := AssertExistsAndLoadBean(t, &Comment{ID: 1}).(*Comment)
addReaction(t, user1, issue1, comment1, "heart")
DeleteCommentReaction(user1, issue1, comment1, "heart")
assert.NoError(t, DeleteCommentReaction(user1, issue1, comment1, "heart"))
AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID, CommentID: comment1.ID})
}
+40 -7
View File
@@ -1781,6 +1781,12 @@ func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) {
return sess.Commit()
}
// UpdateRepositoryStatus updates a repository's status
func UpdateRepositoryStatus(repoID int64, status RepositoryStatus) error {
_, err := x.Exec("UPDATE repository SET status = ? WHERE id = ?", status, repoID)
return err
}
// UpdateRepositoryUpdatedTime updates a repository's updated time
func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error {
_, err := x.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", updateTime.Unix(), repoID)
@@ -1860,6 +1866,17 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
}
}
attachments := make([]*Attachment, 0, 20)
if err = sess.Join("INNER", "`release`", "`release`.id = `attachment`.release_id").
Where("`release`.repo_id = ?", repoID).
Find(&attachments); err != nil {
return err
}
releaseAttachments := make([]string, 0, len(attachments))
for i := 0; i < len(attachments); i++ {
releaseAttachments = append(releaseAttachments, attachments[i].LocalPath())
}
if err = deleteBeans(sess,
&Access{RepoID: repo.ID},
&Action{RepoID: repo.ID},
@@ -1910,13 +1927,13 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return err
}
attachmentPaths := make([]string, 0, 20)
attachments := make([]*Attachment, 0, len(attachmentPaths))
attachments = attachments[:0]
if err = sess.Join("INNER", "issue", "issue.id = attachment.issue_id").
Where("issue.repo_id = ?", repoID).
Find(&attachments); err != nil {
return err
}
attachmentPaths := make([]string, 0, len(attachments))
for j := range attachments {
attachmentPaths = append(attachmentPaths, attachments[j].LocalPath())
}
@@ -1953,11 +1970,6 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return err
}
// Remove attachment files.
for i := range attachmentPaths {
removeAllWithNotice(sess, "Delete attachment", attachmentPaths[i])
}
// Remove LFS objects
var lfsObjects []*LFSMetaObject
if err = sess.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil {
@@ -1997,6 +2009,8 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return fmt.Errorf("Commit: %v", err)
}
sess.Close()
if org.IsOrganization() {
if err = PrepareWebhooks(repo, HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoDeleted,
@@ -2009,6 +2023,19 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
go HookQueue.Add(repo.ID)
}
// We should always delete the files after the database transaction succeed. If
// we delete the file but the database rollback, the repository will be borken.
// Remove issue attachment files.
for i := range attachmentPaths {
removeAllWithNotice(x, "Delete issue attachment", attachmentPaths[i])
}
// Remove release attachment files.
for i := range releaseAttachments {
removeAllWithNotice(x, "Delete release attachment", releaseAttachments[i])
}
if len(repo.Avatar) > 0 {
avatarPath := repo.CustomAvatarPath()
if com.IsExist(avatarPath) {
@@ -2784,3 +2811,9 @@ func (repo *Repository) GetOriginalURLHostname() string {
return u.Host
}
// UpdateRepositoryCols updates repository's columns
func UpdateRepositoryCols(repo *Repository, cols ...string) error {
_, err := x.ID(repo.ID).Cols(cols...).Update(repo)
return err
}
+13 -8
View File
@@ -58,7 +58,7 @@ func (repo *Repository) updateIndexerStatus(sha string) error {
}
type repoIndexerOperation struct {
repo *Repository
repoID int64
deleted bool
watchers []chan<- error
}
@@ -122,7 +122,7 @@ func populateRepoIndexer(maxRepoID int64) {
}
for _, repo := range repos {
repoIndexerOperationQueue <- repoIndexerOperation{
repo: repo,
repoID: repo.ID,
deleted: false,
}
maxRepoID = repo.ID - 1
@@ -131,7 +131,12 @@ func populateRepoIndexer(maxRepoID int64) {
log.Info("Done populating the repo indexer with existing repositories")
}
func updateRepoIndexer(repo *Repository) error {
func updateRepoIndexer(repoID int64) error {
repo, err := getRepositoryByID(x, repoID)
if err != nil {
return err
}
sha, err := getDefaultBranchSha(repo)
if err != nil {
return err
@@ -172,7 +177,7 @@ type fileUpdate struct {
}
func getDefaultBranchSha(repo *Repository) (string, error) {
stdout, err := git.NewCommand("show-ref", "-s", repo.DefaultBranch).RunInDir(repo.RepoPath())
stdout, err := git.NewCommand("show-ref", "-s", git.BranchPrefix+repo.DefaultBranch).RunInDir(repo.RepoPath())
if err != nil {
return "", err
}
@@ -339,11 +344,11 @@ func processRepoIndexerOperationQueue() {
op := <-repoIndexerOperationQueue
var err error
if op.deleted {
if err = indexer.DeleteRepoFromIndexer(op.repo.ID); err != nil {
if err = indexer.DeleteRepoFromIndexer(op.repoID); err != nil {
log.Error("DeleteRepoFromIndexer: %v", err)
}
} else {
if err = updateRepoIndexer(op.repo); err != nil {
if err = updateRepoIndexer(op.repoID); err != nil {
log.Error("updateRepoIndexer: %v", err)
}
}
@@ -355,12 +360,12 @@ func processRepoIndexerOperationQueue() {
// DeleteRepoFromIndexer remove all of a repository's entries from the indexer
func DeleteRepoFromIndexer(repo *Repository, watchers ...chan<- error) {
addOperationToQueue(repoIndexerOperation{repo: repo, deleted: true, watchers: watchers})
addOperationToQueue(repoIndexerOperation{repoID: repo.ID, deleted: true, watchers: watchers})
}
// UpdateRepoIndexer update a repository's entries in the indexer
func UpdateRepoIndexer(repo *Repository, watchers ...chan<- error) {
addOperationToQueue(repoIndexerOperation{repo: repo, deleted: false, watchers: watchers})
addOperationToQueue(repoIndexerOperation{repoID: repo.ID, deleted: false, watchers: watchers})
}
func addOperationToQueue(op repoIndexerOperation) {
+1 -1
View File
@@ -196,7 +196,7 @@ func CreateMigrateTask(doer, u *User, opts base.MigrateOptions) (*Task, error) {
repo, err := CreateRepository(doer, u, CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
OriginalURL: opts.CloneAddr,
OriginalURL: opts.OriginalURL,
IsPrivate: opts.Private,
IsMirror: opts.Mirror,
Status: RepositoryBeingMigrated,
+1
View File
@@ -822,6 +822,7 @@ var (
".",
"..",
".well-known",
"search",
}
reservedUserPatterns = []string{"*.keys", "*.gpg"}
)
+93 -76
View File
@@ -36,10 +36,11 @@ type SlackPayload struct {
// SlackAttachment contains the slack message
type SlackAttachment struct {
Fallback string `json:"fallback"`
Color string `json:"color"`
Title string `json:"title"`
Text string `json:"text"`
Fallback string `json:"fallback"`
Color string `json:"color"`
Title string `json:"title"`
TitleLink string `json:"title_link"`
Text string `json:"text"`
}
// SetSecret sets the slack secret
@@ -133,70 +134,78 @@ func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, e
func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
var text, title, attachmentText string
title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
titleLink := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index)
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text, attachmentText string
switch p.Action {
case api.HookIssueOpened:
text = fmt.Sprintf("[%s] Issue submitted by %s", p.Repository.FullName, senderLink)
title = titleLink
text = fmt.Sprintf("[%s] Issue opened by %s", repoLink, senderLink)
attachmentText = SlackTextFormatter(p.Issue.Body)
case api.HookIssueClosed:
text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Issue closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueReOpened:
text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Issue re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueEdited:
text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Issue edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
attachmentText = SlackTextFormatter(p.Issue.Body)
case api.HookIssueAssigned:
text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName,
text = fmt.Sprintf("[%s] Issue assigned to %s: [%s](%s) by %s", repoLink,
SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
titleLink, senderLink)
title, titleLink, senderLink)
case api.HookIssueUnassigned:
text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Issue unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueLabelUpdated:
text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Issue labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueLabelCleared:
text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Issue labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueSynchronized:
text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Issue synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueMilestoned:
text = fmt.Sprintf("[%s] Issue milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
text = fmt.Sprintf("[%s] Issue milestoned to [%s](%s): [%s](%s) by %s", repoLink,
p.Issue.Milestone.Title, mileStoneLink, title, titleLink, senderLink)
case api.HookIssueDemilestoned:
text = fmt.Sprintf("[%s] Issue milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Issue milestone cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
}
return &SlackPayload{
pl := &SlackPayload{
Channel: slack.Channel,
Text: text,
Username: slack.Username,
IconURL: slack.IconURL,
Attachments: []SlackAttachment{{
Color: slack.Color,
Title: title,
Text: attachmentText,
}},
}, nil
}
if attachmentText != "" {
pl.Attachments = []SlackAttachment{{
Color: slack.Color,
Title: title,
TitleLink: titleLink,
Text: attachmentText,
}}
}
return pl, nil
}
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)),
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
var text, title, attachmentText string
title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text, titleLink, attachmentText string
switch p.Action {
case api.HookIssueCommentCreated:
text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
title = titleLink
text = fmt.Sprintf("[%s] New comment created by %s", repoLink, senderLink)
titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
attachmentText = SlackTextFormatter(p.Comment.Body)
case api.HookIssueCommentEdited:
text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
title = titleLink
text = fmt.Sprintf("[%s] Comment edited by %s", repoLink, senderLink)
titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
attachmentText = SlackTextFormatter(p.Comment.Body)
case api.HookIssueCommentDeleted:
text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink)
title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
text = fmt.Sprintf("[%s] Comment deleted by %s", repoLink, senderLink)
titleLink = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
attachmentText = SlackTextFormatter(p.Comment.Body)
}
@@ -206,9 +215,10 @@ func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (
Username: slack.Username,
IconURL: slack.IconURL,
Attachments: []SlackAttachment{{
Color: slack.Color,
Title: title,
Text: attachmentText,
Color: slack.Color,
Title: title,
TitleLink: titleLink,
Text: attachmentText,
}},
}, nil
}
@@ -281,65 +291,75 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
var text, title, attachmentText string
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text, attachmentText string
switch p.Action {
case api.HookIssueOpened:
text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink)
title = titleLink
text = fmt.Sprintf("[%s] Pull request opened by %s", repoLink, senderLink)
attachmentText = SlackTextFormatter(p.PullRequest.Body)
case api.HookIssueClosed:
if p.PullRequest.HasMerged {
text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request merged: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
} else {
text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
}
case api.HookIssueReOpened:
text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueEdited:
text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
attachmentText = SlackTextFormatter(p.PullRequest.Body)
case api.HookIssueAssigned:
list := make([]string, len(p.PullRequest.Assignees))
for i, user := range p.PullRequest.Assignees {
list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName)
}
text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
text = fmt.Sprintf("[%s] Pull request assigned to %s: [%s](%s) by %s", repoLink,
strings.Join(list, ", "),
titleLink, senderLink)
title, titleLink, senderLink)
case api.HookIssueUnassigned:
text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueLabelUpdated:
text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueLabelCleared:
text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueSynchronized:
text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
case api.HookIssueMilestoned:
text = fmt.Sprintf("[%s] Pull request milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
text = fmt.Sprintf("[%s] Pull request milestoned to [%s](%s): [%s](%s) %s", repoLink,
p.PullRequest.Milestone.Title, mileStoneLink, title, titleLink, senderLink)
case api.HookIssueDemilestoned:
text = fmt.Sprintf("[%s] Pull request milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request milestone cleared: [%s](%s) %s", repoLink, title, titleLink, senderLink)
}
return &SlackPayload{
pl := &SlackPayload{
Channel: slack.Channel,
Text: text,
Username: slack.Username,
IconURL: slack.IconURL,
Attachments: []SlackAttachment{{
Color: slack.Color,
Title: title,
Text: attachmentText,
}},
}, nil
}
if attachmentText != "" {
pl.Attachments = []SlackAttachment{{
Color: slack.Color,
Title: title,
TitleLink: titleLink,
Text: attachmentText,
}}
}
return pl, nil
}
func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event HookEventType) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
var text, title, attachmentText string
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text string
switch p.Action {
case api.HookIssueSynchronized:
action, err := parseHookPullRequestEventType(event)
@@ -347,7 +367,7 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
return nil, err
}
text = fmt.Sprintf("[%s] Pull request review %s : %s by %s", p.Repository.FullName, action, titleLink, senderLink)
text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink)
}
return &SlackPayload{
@@ -355,17 +375,13 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
Text: text,
Username: slack.Username,
IconURL: slack.IconURL,
Attachments: []SlackAttachment{{
Color: slack.Color,
Title: title,
Text: attachmentText,
}},
}, nil
}
func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
var text, title, attachmentText string
switch p.Action {
case api.HookRepoCreated:
text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
@@ -380,9 +396,10 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla
Username: slack.Username,
IconURL: slack.IconURL,
Attachments: []SlackAttachment{{
Color: slack.Color,
Title: title,
Text: attachmentText,
Color: slack.Color,
Title: title,
TitleLink: title,
Text: attachmentText,
}},
}, nil
}
+4 -4
View File
@@ -499,9 +499,9 @@ func (f SubmitReviewForm) HasEmptyContent() bool {
// NewReleaseForm form for creating release
type NewReleaseForm struct {
TagName string `binding:"Required;GitRefName"`
Target string `form:"tag_target" binding:"Required"`
Title string `binding:"Required"`
TagName string `binding:"Required;GitRefName;MaxSize(255)"`
Target string `form:"tag_target" binding:"Required;MaxSize(255)"`
Title string `binding:"Required;MaxSize(255)"`
Content string
Draft string
Prerelease bool
@@ -515,7 +515,7 @@ func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) bin
// EditReleaseForm form for changing release
type EditReleaseForm struct {
Title string `form:"title" binding:"Required"`
Title string `form:"title" binding:"Required;MaxSize(255)"`
Content string `form:"content"`
Draft string `form:"draft"`
Prerelease bool `form:"prerelease"`
+43 -10
View File
@@ -123,7 +123,7 @@ func Middleware(options ...Options) macaron.Handler {
// OK we should proxy the response writer
// We are still not necessarily going to compress...
proxyWriter := &ProxyResponseWriter{
ResponseWriter: ctx.Resp,
internal: ctx.Resp,
}
defer proxyWriter.Close()
@@ -137,19 +137,52 @@ func Middleware(options ...Options) macaron.Handler {
}
ctx.Next()
ctx.Resp = proxyWriter.internal
}
}
// ProxyResponseWriter is a wrapped macaron ResponseWriter that may compress its contents
type ProxyResponseWriter struct {
writer io.WriteCloser
macaron.ResponseWriter
stopped bool
writer io.WriteCloser
internal macaron.ResponseWriter
stopped bool
code int
buf []byte
}
// Header returns the header map
func (proxy *ProxyResponseWriter) Header() http.Header {
return proxy.internal.Header()
}
// Status returns the status code of the response or 0 if the response has not been written.
func (proxy *ProxyResponseWriter) Status() int {
if proxy.code != 0 {
return proxy.code
}
return proxy.internal.Status()
}
// Written returns whether or not the ResponseWriter has been written.
func (proxy *ProxyResponseWriter) Written() bool {
if proxy.code != 0 {
return true
}
return proxy.internal.Written()
}
// Size returns the size of the response body.
func (proxy *ProxyResponseWriter) Size() int {
return proxy.internal.Size()
}
// Before allows for a function to be called before the ResponseWriter has been written to. This is
// useful for setting headers or any other operations that must happen before a response has been written.
func (proxy *ProxyResponseWriter) Before(before macaron.BeforeFunc) {
proxy.internal.Before(before)
}
// Write appends data to the proxied gzip writer.
func (proxy *ProxyResponseWriter) Write(b []byte) (int, error) {
// if writer is initialized, use the writer
@@ -210,7 +243,7 @@ func (proxy *ProxyResponseWriter) startGzip() error {
// Write the header to gzip response.
if proxy.code != 0 {
proxy.ResponseWriter.WriteHeader(proxy.code)
proxy.internal.WriteHeader(proxy.code)
// Ensure that no other WriteHeader's happen
proxy.code = 0
}
@@ -220,7 +253,7 @@ func (proxy *ProxyResponseWriter) startGzip() error {
// write the gzip header even if nothing was ever written.
if len(proxy.buf) > 0 {
// Initialize the GZIP response.
proxy.writer = writerPool.Get(proxy.ResponseWriter)
proxy.writer = writerPool.Get(proxy.internal)
return proxy.writeBuf()
}
@@ -229,11 +262,11 @@ func (proxy *ProxyResponseWriter) startGzip() error {
func (proxy *ProxyResponseWriter) startPlain() error {
if proxy.code != 0 {
proxy.ResponseWriter.WriteHeader(proxy.code)
proxy.internal.WriteHeader(proxy.code)
proxy.code = 0
}
proxy.stopped = true
proxy.writer = noopCloser{proxy.ResponseWriter}
proxy.writer = noopCloser{proxy.internal}
return proxy.writeBuf()
}
@@ -295,13 +328,13 @@ func (proxy *ProxyResponseWriter) Flush() {
gw.Flush()
}
proxy.ResponseWriter.Flush()
proxy.internal.Flush()
}
// Hijack implements http.Hijacker. If the underlying ResponseWriter is a
// Hijacker, its Hijack method is returned. Otherwise an error is returned.
func (proxy *ProxyResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hijacker, ok := proxy.ResponseWriter.(http.Hijacker)
hijacker, ok := proxy.internal.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
}
+21 -16
View File
@@ -138,26 +138,31 @@ func populateIssueIndexer() {
}
for _, repo := range repos {
is, err := models.Issues(&models.IssuesOptions{
RepoIDs: []int64{repo.ID},
IsClosed: util.OptionalBoolNone,
IsPull: util.OptionalBoolNone,
})
if err != nil {
log.Error("Issues: %v", err)
continue
}
if err = models.IssueList(is).LoadDiscussComments(); err != nil {
log.Error("LoadComments: %v", err)
continue
}
for _, issue := range is {
UpdateIssueIndexer(issue)
}
UpdateRepoIndexer(repo)
}
}
}
// UpdateRepoIndexer add/update all issues of the repositories
func UpdateRepoIndexer(repo *models.Repository) {
is, err := models.Issues(&models.IssuesOptions{
RepoIDs: []int64{repo.ID},
IsClosed: util.OptionalBoolNone,
IsPull: util.OptionalBoolNone,
})
if err != nil {
log.Error("Issues: %v", err)
return
}
if err = models.IssueList(is).LoadDiscussComments(); err != nil {
log.Error("LoadComments: %v", err)
return
}
for _, issue := range is {
UpdateIssueIndexer(issue)
}
}
// UpdateIssueIndexer add/update an issue to the issue indexer
func UpdateIssueIndexer(issue *models.Issue) {
var comments []string
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"code.gitea.io/gitea/modules/log"
"github.com/lunny/levelqueue"
"gitea.com/lunny/levelqueue"
)
var (
+1
View File
@@ -12,6 +12,7 @@ type Uploader interface {
CreateTopics(topic ...string) error
CreateMilestones(milestones ...*Milestone) error
CreateReleases(releases ...*Release) error
SyncTags() error
CreateLabels(labels ...*Label) error
CreateIssues(issues ...*Issue) error
CreateComments(comments ...*Comment) error
+5 -4
View File
@@ -288,11 +288,12 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
rels = append(rels, &rel)
}
if err := models.InsertReleases(rels...); err != nil {
return err
}
// sync tags to releases in database
return models.InsertReleases(rels...)
}
// SyncTags syncs releases with tags in the database
func (g *GiteaLocalUploader) SyncTags() error {
return models.SyncReleasesWithTags(g.repo, g.gitRepo)
}
+7 -2
View File
@@ -57,7 +57,7 @@ func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOpt
opts.PullRequests = false
opts.GitServiceType = structs.PlainGitService
downloader = NewPlainGitDownloader(ownerName, opts.RepoName, opts.CloneAddr)
log.Trace("Will migrate from git: %s", opts.CloneAddr)
log.Trace("Will migrate from git: %s", opts.OriginalURL)
} else if opts.GitServiceType == structs.NotMigrated {
opts.GitServiceType = theFactory.GitServiceType()
}
@@ -68,7 +68,7 @@ func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOpt
log.Error("rollback failed: %v", err1)
}
if err2 := models.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.CloneAddr, err)); err2 != nil {
if err2 := models.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
log.Error("create respotiry notice failed: ", err2)
}
return nil, err
@@ -165,6 +165,11 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts
}
releases = releases[relBatchSize:]
}
// Once all releases (if any) are inserted, sync any remaining non-release tags
if err := uploader.SyncTags(); err != nil {
return err
}
}
var commentBatchSize = uploader.MaxBatchInsertSize("comment")
+5
View File
@@ -107,3 +107,8 @@ func (r *indexerNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
func (r *indexerNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
issue_indexer.UpdateIssueIndexer(issue)
}
func (r *indexerNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
issue_indexer.UpdateRepoIndexer(repo)
models.UpdateRepoIndexer(repo)
}
+1 -1
View File
@@ -24,7 +24,7 @@ type Response struct {
ErrorCodes []string `json:"error-codes"`
}
const apiURL = "/api/siteverify"
const apiURL = "api/siteverify"
// Verify calls Google Recaptcha API to verify token
func Verify(response string) (bool, error) {
+1 -1
View File
@@ -69,7 +69,7 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo
message := strings.TrimSpace(opts.Message)
author, committer := GetAuthorAndCommitterUsers(opts.Committer, opts.Author, doer)
author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
t, err := NewTemporaryUploadRepository(repo)
if err != nil {
+1 -1
View File
@@ -80,7 +80,7 @@ func GetFileCommitResponse(repo *models.Repository, commit *git.Commit) (*api.Fi
}
// GetAuthorAndCommitterUsers Gets the author and committer user objects from the IdentityOptions
func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models.User) (committerUser, authorUser *models.User) {
func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models.User) (authorUser, committerUser *models.User) {
// Committer and author are optional. If they are not the doer (not same email address)
// then we use bogus User objects for them to store their FullName and Email.
// If only one of the two are provided, we set both of them to it.
+5 -5
View File
@@ -79,11 +79,11 @@ func GetTreeBySHA(repo *models.Repository, sha string, page, perPage int, recurs
for e := rangeStart; e < rangeEnd; e++ {
i := e - rangeStart
tree.Entries[e].Path = entries[e].Name()
tree.Entries[e].Mode = fmt.Sprintf("%06o", entries[e].Mode())
tree.Entries[e].Type = entries[e].Type()
tree.Entries[e].Size = entries[e].Size()
tree.Entries[e].SHA = entries[e].ID.String()
tree.Entries[i].Path = entries[e].Name()
tree.Entries[i].Mode = fmt.Sprintf("%06o", entries[e].Mode())
tree.Entries[i].Type = entries[e].Type()
tree.Entries[i].Size = entries[e].Size()
tree.Entries[i].SHA = entries[e].ID.String()
if entries[e].IsDir() {
copy(treeURL[copyPos:], entries[e].ID.String())
+7 -2
View File
@@ -167,7 +167,7 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
message := strings.TrimSpace(opts.Message)
author, committer := GetAuthorAndCommitterUsers(opts.Committer, opts.Author, doer)
author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
t, err := NewTemporaryUploadRepository(repo)
if err != nil {
@@ -453,10 +453,15 @@ func PushUpdate(repo *models.Repository, branch string, opts models.PushUpdateOp
}
}
} else if !isDelRef {
branchName := opts.RefFullName[len(git.BranchPrefix):]
if err = models.RemoveDeletedBranch(repo.ID, branchName); err != nil {
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, branchName, err)
}
// If is branch reference
// Clear cache for branch commit count
cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
cache.Remove(repo.GetCommitsCountCacheKey(branchName, true))
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
if err != nil {
+5 -1
View File
@@ -128,7 +128,11 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions
logConfig["username"] = sec.Key("USER").MustString("example@example.com")
logConfig["password"] = sec.Key("PASSWD").MustString("******")
logConfig["host"] = sec.Key("HOST").MustString("127.0.0.1:25")
logConfig["sendTos"] = sec.Key("RECEIVERS").MustString("[]")
sendTos := strings.Split(sec.Key("RECEIVERS").MustString(""), ",")
for i, address := range sendTos {
sendTos[i] = strings.TrimSpace(address)
}
logConfig["sendTos"] = sendTos
logConfig["subject"] = sec.Key("SUBJECT").MustString("Diagnostic message from Gitea")
}
-2
View File
@@ -97,8 +97,6 @@ func runMigrateTask(t *models.Task) (err error) {
opts.MigrateToRepoID = t.RepoID
repo, err := migrations.MigrateRepository(t.Doer, t.Owner.Name, *opts)
if err == nil {
notification.NotifyMigrateRepository(t.Doer, t.Owner, repo)
log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
return nil
}
+8
View File
@@ -239,6 +239,14 @@ func NewFuncMap() []template.FuncMap {
"MirrorFullAddress": mirror_service.AddressNoCredentials,
"MirrorUserName": mirror_service.Username,
"MirrorPassword": mirror_service.Password,
"contain": func(s []int64, id int64) bool {
for i := 0; i < len(s); i++ {
if s[i] == id {
return true
}
}
return false
},
}}
}
+19 -11
View File
@@ -6,6 +6,7 @@
package admin
import (
"encoding/json"
"fmt"
"net/url"
"os"
@@ -25,6 +26,7 @@ import (
"code.gitea.io/gitea/services/mailer"
"gitea.com/macaron/macaron"
"gitea.com/macaron/session"
"github.com/unknwon/com"
)
@@ -207,7 +209,7 @@ func SendTestMail(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/admin/config")
}
func shadownPasswordKV(cfgItem, splitter string) string {
func shadowPasswordKV(cfgItem, splitter string) string {
fields := strings.Split(cfgItem, splitter)
for i := 0; i < len(fields); i++ {
if strings.HasPrefix(fields[i], "password=") {
@@ -218,10 +220,10 @@ func shadownPasswordKV(cfgItem, splitter string) string {
return strings.Join(fields, splitter)
}
func shadownURL(provider, cfgItem string) string {
func shadowURL(provider, cfgItem string) string {
u, err := url.Parse(cfgItem)
if err != nil {
log.Error("shodowPassword %v failed: %v", provider, err)
log.Error("Shadowing Password for %v failed: %v", provider, err)
return cfgItem
}
if u.User != nil {
@@ -239,7 +241,7 @@ func shadownURL(provider, cfgItem string) string {
func shadowPassword(provider, cfgItem string) string {
switch provider {
case "redis":
return shadownPasswordKV(cfgItem, ",")
return shadowPasswordKV(cfgItem, ",")
case "mysql":
//root:@tcp(localhost:3306)/macaron?charset=utf8
atIdx := strings.Index(cfgItem, "@")
@@ -253,15 +255,15 @@ func shadowPassword(provider, cfgItem string) string {
case "postgres":
// user=jiahuachen dbname=macaron port=5432 sslmode=disable
if !strings.HasPrefix(cfgItem, "postgres://") {
return shadownPasswordKV(cfgItem, " ")
return shadowPasswordKV(cfgItem, " ")
}
fallthrough
case "couchbase":
return shadowURL(provider, cfgItem)
// postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full
// Notice: use shadwonURL
// Notice: use shadowURL
}
// "couchbase"
return shadownURL(provider, cfgItem)
return cfgItem
}
// Config show admin config page
@@ -306,8 +308,14 @@ func Config(ctx *context.Context) {
ctx.Data["CacheItemTTL"] = setting.CacheService.TTL
sessionCfg := setting.SessionConfig
if sessionCfg.Provider == "VirtualSession" {
var realSession session.Options
if err := json.Unmarshal([]byte(sessionCfg.ProviderConfig), &realSession); err != nil {
log.Error("Unable to unmarshall session config for virtualed provider config: %s\nError: %v", sessionCfg.ProviderConfig, err)
}
sessionCfg = realSession
}
sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig)
ctx.Data["SessionConfig"] = sessionCfg
ctx.Data["DisableGravatar"] = setting.DisableGravatar
+14 -3
View File
@@ -94,8 +94,14 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
u.LoginName = form.LoginName
}
}
if u.LoginType == models.LoginPlain {
if u.LoginType == models.LoginNoType || u.LoginType == models.LoginPlain {
if len(form.Password) < setting.MinPasswordLength {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserNew, &form)
return
}
if !password.IsComplexEnough(form.Password) {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserNew, &form)
return
}
@@ -203,14 +209,19 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
if len(form.Password) > 0 {
var err error
if u.Salt, err = models.GetUserSalt(); err != nil {
ctx.ServerError("UpdateUser", err)
if len(form.Password) < setting.MinPasswordLength {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserEdit, &form)
return
}
if !password.IsComplexEnough(form.Password) {
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserEdit, &form)
return
}
if u.Salt, err = models.GetUserSalt(); err != nil {
ctx.ServerError("UpdateUser", err)
return
}
u.HashPassword(form.Password)
}
+7 -3
View File
@@ -352,8 +352,8 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
}
}
if err = models.UpdateIssue(issue); err != nil {
ctx.Error(500, "UpdateIssue", err)
if err = models.UpdateIssueByAPI(issue); err != nil {
ctx.InternalServerError(err)
return
}
if form.State != nil {
@@ -372,7 +372,11 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
// Refetch from database to assign some automatic values
issue, err = models.GetIssueByID(issue.ID)
if err != nil {
ctx.Error(500, "GetIssueByID", err)
ctx.InternalServerError(err)
return
}
if err = issue.LoadMilestone(); err != nil {
ctx.InternalServerError(err)
return
}
ctx.JSON(201, issue.APIFormat())
+2 -2
View File
@@ -420,8 +420,8 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
}
}
if err = models.UpdateIssue(issue); err != nil {
ctx.Error(500, "UpdateIssue", err)
if err = models.UpdateIssueByAPI(issue); err != nil {
ctx.InternalServerError(err)
return
}
if form.State != nil {
+47 -7
View File
@@ -6,6 +6,8 @@
package repo
import (
"bytes"
"errors"
"fmt"
"net/http"
"net/url"
@@ -431,15 +433,53 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
opts.Releases = false
}
repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts)
if err == nil {
notification.NotifyCreateRepository(ctx.User, ctxUser, repo)
log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
OriginalURL: opts.CloneAddr,
IsPrivate: opts.Private,
IsMirror: opts.Mirror,
Status: models.RepositoryBeingMigrated,
})
if err != nil {
handleMigrateError(ctx, ctxUser, remoteAddr, err)
return
}
opts.MigrateToRepoID = repo.ID
defer func() {
if e := recover(); e != nil {
var buf bytes.Buffer
fmt.Fprintf(&buf, "Handler crashed with error: %v", log.Stack(2))
err = errors.New(buf.String())
}
if err == nil {
repo.Status = models.RepositoryReady
if err := models.UpdateRepositoryCols(repo, "status"); err == nil {
notification.NotifyMigrateRepository(ctx.User, ctxUser, repo)
return
}
}
if repo != nil {
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
log.Error("DeleteRepository: %v", errDelete)
}
}
}()
if _, err = migrations.MigrateRepository(ctx.User, ctxUser.Name, opts); err != nil {
handleMigrateError(ctx, ctxUser, remoteAddr, err)
return
}
log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
}
func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteAddr string, err error) {
switch {
case models.IsErrRepoAlreadyExist(err):
ctx.Error(409, "", "The repository with the same name already exists.")
@@ -448,7 +488,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
case migrations.IsTwoFactorAuthError(err):
ctx.Error(422, "", "Remote visit required two factors authentication.")
case models.IsErrReachLimitOfRepo(err):
ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit()))
ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
case models.IsErrNameReserved(err):
ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
case models.IsErrNamePatternNotAllowed(err):
+2 -2
View File
@@ -41,8 +41,8 @@ func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) {
// schema:
// "$ref": "#/definitions/CreateStatusOption"
// responses:
// "200":
// "$ref": "#/responses/StatusList"
// "201":
// "$ref": "#/responses/Status"
sha := ctx.Params("sha")
if len(sha) == 0 {
ctx.Error(400, "sha not given", nil)
+28
View File
@@ -346,6 +346,30 @@ func PrepareCompareDiff(
return false
}
// parseBaseRepoInfo parse base repository if current repo is forked.
// The "base" here means the repository where current repo forks from,
// not the repository fetch from current URL.
func parseBaseRepoInfo(ctx *context.Context, repo *models.Repository) error {
if !repo.IsFork {
return nil
}
if err := repo.GetBaseRepo(); err != nil {
return err
}
if err := repo.BaseRepo.GetOwnerName(); err != nil {
return err
}
baseGitRepo, err := git.OpenRepository(models.RepoPath(repo.BaseRepo.OwnerName, repo.BaseRepo.Name))
if err != nil {
return err
}
ctx.Data["BaseRepoBranches"], err = baseGitRepo.GetBranches()
if err != nil {
return err
}
return nil
}
// CompareDiff show different from one commit to another commit
func CompareDiff(ctx *context.Context) {
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := ParseCompareInfo(ctx)
@@ -353,6 +377,10 @@ func CompareDiff(ctx *context.Context) {
return
}
defer headGitRepo.Close()
if err := parseBaseRepoInfo(ctx, headRepo); err != nil {
ctx.ServerError("parseBaseRepoInfo", err)
return
}
nothingToCompare := PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch)
if ctx.Written() {
+2 -1
View File
@@ -261,7 +261,8 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
}
ctx.Data["IssueStats"] = issueStats
ctx.Data["SelectLabels"] = com.StrTo(selectLabels).MustInt64()
ctx.Data["SelLabelIDs"] = labelIDs
ctx.Data["SelectLabels"] = selectLabels
ctx.Data["ViewType"] = viewType
ctx.Data["SortType"] = sortType
ctx.Data["MilestoneID"] = milestoneID
+5
View File
@@ -10,6 +10,11 @@ import (
// SetEditorconfigIfExists set editor config as render variable
func SetEditorconfigIfExists(ctx *context.Context) {
if ctx.Repo.Repository.IsEmpty {
ctx.Data["Editorconfig"] = nil
return
}
ec, err := ctx.Repo.GetEditorconfig()
if err != nil && !git.IsErrNotExist(err) {
+16
View File
@@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/repofiles"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff"
@@ -928,6 +929,21 @@ func CleanUpPullRequest(ctx *context.Context) {
return
}
if err := repofiles.PushUpdate(
pr.HeadRepo,
pr.HeadBranch,
models.PushUpdateOptions{
RefFullName: git.BranchPrefix + pr.HeadBranch,
OldCommitID: branchCommitID,
NewCommitID: git.EmptySHA,
PusherID: ctx.User.ID,
PusherName: ctx.User.Name,
RepoUserName: pr.HeadRepo.Owner.Name,
RepoName: pr.HeadRepo.Name,
}); err != nil {
log.Error("Update: %v", err)
}
if err := models.AddDeletePRBranchComment(ctx.User, pr.BaseRepo, issue.ID, pr.HeadBranch); err != nil {
// Do not fail here as branch has already been deleted
log.Error("DeleteBranch: %v", err)
+1
View File
@@ -291,6 +291,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
}
var opts = migrations.MigrateOptions{
OriginalURL: form.CloneAddr,
CloneAddr: remoteAddr,
RepoName: form.RepoName,
Description: form.Description,
+1
View File
@@ -786,6 +786,7 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
u, err := models.UserSignIn(signInForm.UserName, signInForm.Password)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Data["user_exists"] = true
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplLinkAccount, &signInForm)
} else {
ctx.ServerError("UserLinkAccount", err)
+13 -7
View File
@@ -6,6 +6,7 @@ package user
import (
"strconv"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
@@ -23,14 +24,19 @@ func Avatar(ctx *context.Context) {
log.Debug("Asked avatar for user %v and size %v", userName, size)
user, err := models.GetUserByName(userName)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.ServerError("Requested avatar for invalid user", err)
} else {
ctx.ServerError("Retrieving user by name", err)
var user *models.User
if strings.ToLower(userName) != "ghost" {
user, err = models.GetUserByName(userName)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.ServerError("Requested avatar for invalid user", err)
} else {
ctx.ServerError("Retrieving user by name", err)
}
return
}
return
} else {
user = models.NewGhostUser()
}
ctx.Redirect(user.RealSizedAvatarLink(size))
+9 -8
View File
@@ -546,6 +546,15 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
// Get new file.
if strings.HasPrefix(line, cmdDiffHead) {
if len(diff.Files) >= maxFiles {
diff.IsIncomplete = true
_, err := io.Copy(ioutil.Discard, reader)
if err != nil {
return nil, fmt.Errorf("Copy: %v", err)
}
break
}
var middle int
// Note: In case file name is surrounded by double quotes (it happens only in git-shell).
@@ -590,14 +599,6 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
IsRenamed: a != b,
}
diff.Files = append(diff.Files, curFile)
if len(diff.Files) >= maxFiles {
diff.IsIncomplete = true
_, err := io.Copy(ioutil.Discard, reader)
if err != nil {
return nil, fmt.Errorf("Copy: %v", err)
}
break
}
curFileLinesCount = 0
curFileLFSPrefix = false
+14
View File
@@ -6,11 +6,13 @@
package gitdiff
import (
"fmt"
"html/template"
"strings"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
dmp "github.com/sergi/go-diff/diffmatchpatch"
@@ -195,3 +197,15 @@ func TestDiffLine_GetCommentSide(t *testing.T) {
assert.Equal(t, "previous", (&DiffLine{Comments: []*models.Comment{{Line: -3}}}).GetCommentSide())
assert.Equal(t, "proposed", (&DiffLine{Comments: []*models.Comment{{Line: 3}}}).GetCommentSide())
}
func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) {
git.Debug = true
for _, behavior := range []string{"-w", "--ignore-space-at-eol", "-b", ""} {
diffs, err := GetDiffRangeWithWhitespaceBehavior("./testdata/academic-module", "559c156f8e0178b71cb44355428f24001b08fc68", "bd7063cc7c04689c4d082183d32a604ed27a24f9",
setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles, behavior)
assert.NoError(t, err, fmt.Sprintf("Error when diff with %s", behavior))
for _, f := range diffs.Files {
assert.True(t, len(f.Sections) > 0, fmt.Sprintf("%s should have sections", f.Name))
}
}
}
+1
View File
@@ -0,0 +1 @@
ref: refs/heads/master
+10
View File
@@ -0,0 +1,10 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[branch "master"]
remote = origin
merge = refs/heads/master
+1
View File
@@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
#
# An example hook script to check the commit log message taken by
# applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit. The hook is
# allowed to edit the commit message file.
#
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:
+24
View File
@@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open2;
# An example hook script to integrate Watchman
# (https://facebook.github.io/watchman/) with git to speed up detecting
# new and modified files.
#
# The hook is passed a version (currently 1) and a time in nanoseconds
# formatted as a string and outputs to stdout all files that have been
# modified since the given time. Paths must be relative to the root of
# the working tree and separated by a single NUL.
#
# To enable this hook, rename this file to "query-watchman" and set
# 'git config core.fsmonitor .git/hooks/query-watchman'
#
my ($version, $time) = @ARGV;
# Check the hook interface version
if ($version == 1) {
# convert nanoseconds to seconds
$time = int $time / 1000000000;
} else {
die "Unsupported query-fsmonitor hook version '$version'.\n" .
"Falling back to scanning...\n";
}
my $git_work_tree;
if ($^O =~ 'msys' || $^O =~ 'cygwin') {
$git_work_tree = Win32::GetCwd();
$git_work_tree =~ tr/\\/\//;
} else {
require Cwd;
$git_work_tree = Cwd::cwd();
}
my $retry = 1;
launch_watchman();
sub launch_watchman {
my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
or die "open2() failed: $!\n" .
"Falling back to scanning...\n";
# In the query expression below we're asking for names of files that
# changed since $time but were not transient (ie created after
# $time but no longer exist).
#
# To accomplish this, we're using the "since" generator to use the
# recency index to select candidate nodes and "fields" to limit the
# output to file names only. Then we're using the "expression" term to
# further constrain the results.
#
# The category of transient files that we want to ignore will have a
# creation clock (cclock) newer than $time_t value and will also not
# currently exist.
my $query = <<" END";
["query", "$git_work_tree", {
"since": $time,
"fields": ["name"],
"expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]]
}]
END
print CHLD_IN $query;
close CHLD_IN;
my $response = do {local $/; <CHLD_OUT>};
die "Watchman: command returned no output.\n" .
"Falling back to scanning...\n" if $response eq "";
die "Watchman: command returned invalid output: $response\n" .
"Falling back to scanning...\n" unless $response =~ /^\{/;
my $json_pkg;
eval {
require JSON::XS;
$json_pkg = "JSON::XS";
1;
} or do {
require JSON::PP;
$json_pkg = "JSON::PP";
};
my $o = $json_pkg->new->utf8->decode($response);
if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) {
print STDERR "Adding '$git_work_tree' to watchman's watch list.\n";
$retry--;
qx/watchman watch "$git_work_tree"/;
die "Failed to make watchman watch '$git_work_tree'.\n" .
"Falling back to scanning...\n" if $? != 0;
# Watchman will always return all files on the first query so
# return the fast "everything is dirty" flag to git and do the
# Watchman query just to get it over with now so we won't pay
# the cost in git to look up each individual file.
print "/\0";
eval { launch_watchman() };
exit 0;
}
die "Watchman: $o->{error}.\n" .
"Falling back to scanning...\n" if $o->{error};
binmode STDOUT, ":utf8";
local $, = "\0";
print @{$o->{files}};
}
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".
exec git update-server-info
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed
# by applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
precommit="$(git rev-parse --git-path hooks/pre-commit)"
test -x "$precommit" && exec "$precommit" ${1+"$@"}
:
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --bool hooks.allownonascii)
# Redirect output to stderr.
exec 1>&2
# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
git config hooks.allownonascii true
EOF
exit 1
fi
# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --
+53
View File
@@ -0,0 +1,53 @@
#!/bin/sh
# An example hook script to verify what is about to be pushed. Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed. If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
# <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
remote="$1"
url="$2"
z40=0000000000000000000000000000000000000000
while read local_ref local_sha remote_ref remote_sha
do
if [ "$local_sha" = $z40 ]
then
# Handle delete
:
else
if [ "$remote_sha" = $z40 ]
then
# New branch, examine all commits
range="$local_sha"
else
# Update to existing branch, examine new commits
range="$remote_sha..$local_sha"
fi
# Check for WIP commit
commit=`git rev-list -n 1 --grep '^WIP' "$range"`
if [ -n "$commit" ]
then
echo >&2 "Found WIP commit in $local_ref, not pushing"
exit 1
fi
fi
done
exit 0
+169
View File
@@ -0,0 +1,169 @@
#!/bin/sh
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
# The hook is called with the following parameters:
#
# $1 -- the upstream the series was forked from.
# $2 -- the branch being rebased (or empty when rebasing the current branch).
#
# This sample shows how to prevent topic branches that are already
# merged to 'next' branch from getting rebased, because allowing it
# would result in rebasing already published history.
publish=next
basebranch="$1"
if test "$#" = 2
then
topic="refs/heads/$2"
else
topic=`git symbolic-ref HEAD` ||
exit 0 ;# we do not interrupt rebasing detached HEAD
fi
case "$topic" in
refs/heads/??/*)
;;
*)
exit 0 ;# we do not interrupt others.
;;
esac
# Now we are dealing with a topic branch being rebased
# on top of master. Is it OK to rebase it?
# Does the topic really exist?
git show-ref -q "$topic" || {
echo >&2 "No such branch $topic"
exit 1
}
# Is topic fully merged to master?
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
echo >&2 "$topic is fully merged to master; better remove it."
exit 1 ;# we could allow it, but there is no point.
fi
# Is topic ever merged to next? If so you should not be rebasing it.
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
only_next_2=`git rev-list ^master ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
not_in_topic=`git rev-list "^$topic" master`
if test -z "$not_in_topic"
then
echo >&2 "$topic is already up to date with master"
exit 1 ;# we could allow it, but there is no point.
else
exit 0
fi
else
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
/usr/bin/perl -e '
my $topic = $ARGV[0];
my $msg = "* $topic has commits already merged to public branch:\n";
my (%not_in_next) = map {
/^([0-9a-f]+) /;
($1 => 1);
} split(/\n/, $ARGV[1]);
for my $elem (map {
/^([0-9a-f]+) (.*)$/;
[$1 => $2];
} split(/\n/, $ARGV[2])) {
if (!exists $not_in_next{$elem->[0]}) {
if ($msg) {
print STDERR $msg;
undef $msg;
}
print STDERR " $elem->[1]\n";
}
}
' "$topic" "$not_in_next" "$not_in_master"
exit 1
fi
<<\DOC_END
This sample hook safeguards topic branches that have been
published from being rewound.
The workflow assumed here is:
* Once a topic branch forks from "master", "master" is never
merged into it again (either directly or indirectly).
* Once a topic branch is fully cooked and merged into "master",
it is deleted. If you need to build on top of it to correct
earlier mistakes, a new topic branch is created by forking at
the tip of the "master". This is not strictly necessary, but
it makes it easier to keep your history simple.
* Whenever you need to test or publish your changes to topic
branches, merge them into "next" branch.
The script, being an example, hardcodes the publish branch name
to be "next", but it is trivial to make it configurable via
$GIT_DIR/config mechanism.
With this workflow, you would want to know:
(1) ... if a topic branch has ever been merged to "next". Young
topic branches can have stupid mistakes you would rather
clean up before publishing, and things that have not been
merged into other branches can be easily rebased without
affecting other people. But once it is published, you would
not want to rewind it.
(2) ... if a topic branch has been fully merged to "master".
Then you can delete it. More importantly, you should not
build on top of it -- other people may already want to
change things related to the topic as patches against your
"master", so if you need further changes, it is better to
fork the topic (perhaps with the same name) afresh from the
tip of "master".
Let's look at this example:
o---o---o---o---o---o---o---o---o---o "next"
/ / / /
/ a---a---b A / /
/ / / /
/ / c---c---c---c B /
/ / / \ /
/ / / b---b C \ /
/ / / / \ /
---o---o---o---o---o---o---o---o---o---o---o "master"
A, B and C are topic branches.
* A has one fix since it was merged up to "next".
* B has finished. It has been fully merged up to "master" and "next",
and is ready to be deleted.
* C has not merged to "next" at all.
We would want to allow C to be rebased, refuse A, and encourage
B to be deleted.
To compute (1):
git rev-list ^master ^topic next
git rev-list ^master next
if these match, topic has not merged in next at all.
To compute (2):
git rev-list master..topic
if this is empty, it is fully merged to "master".
DOC_END
+24
View File
@@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to make use of push options.
# The example simply echoes all push options that start with 'echoback='
# and rejects all pushes when the "reject" push option is used.
#
# To enable this hook, rename this file to "pre-receive".
if test -n "$GIT_PUSH_OPTION_COUNT"
then
i=0
while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
do
eval "value=\$GIT_PUSH_OPTION_$i"
case "$value" in
echoback=*)
echo "echo from the pre-receive-hook: ${value#*=}" >&2
;;
reject)
exit 1
esac
i=$((i + 1))
done
fi
@@ -0,0 +1,42 @@
#!/bin/sh
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source. The hook's purpose is to edit the commit
# message file. If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".
# This hook includes three examples. The first one removes the
# "# Please enter the commit message..." help message.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output. It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited. This is rarely a good idea.
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
# case "$COMMIT_SOURCE,$SHA1" in
# ,|template,)
# /usr/bin/perl -i.bak -pe '
# print "\n" . `git diff --cached --name-status -r`
# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
# *) ;;
# esac
# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
# if test -z "$COMMIT_SOURCE"
# then
# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
# fi
+128
View File
@@ -0,0 +1,128 @@
#!/bin/sh
#
# An example hook script to block unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
# hooks.allowdeletetag
# This boolean sets whether deleting tags will be allowed in the
# repository. By default they won't be.
# hooks.allowmodifytag
# This boolean sets whether a tag may be modified after creation. By default
# it won't be.
# hooks.allowdeletebranch
# This boolean sets whether deleting branches will be allowed in the
# repository. By default they won't be.
# hooks.denycreatebranch
# This boolean sets whether remotely creating branches will be denied
# in the repository. By default this is allowed.
#
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
# --- Config
allowunannotated=$(git config --bool hooks.allowunannotated)
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
denycreatebranch=$(git config --bool hooks.denycreatebranch)
allowdeletetag=$(git config --bool hooks.allowdeletetag)
allowmodifytag=$(git config --bool hooks.allowmodifytag)
# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
echo "*** Project description file hasn't been set" >&2
exit 1
;;
esac
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero="0000000000000000000000000000000000000000"
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
newrev_type=$(git cat-file -t $newrev)
fi
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,delete)
# delete tag
if [ "$allowdeletetag" != "true" ]; then
echo "*** Deleting a tag is not allowed in this repository" >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
then
echo "*** Tag '$refname' already exists." >&2
echo "*** Modifying a tag is not allowed in this repository." >&2
exit 1
fi
;;
refs/heads/*,commit)
# branch
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
echo "*** Creating a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/heads/*,delete)
# delete branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/remotes/*,commit)
# tracking branch
;;
refs/remotes/*,delete)
# delete tracking branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
exit 1
fi
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
# --- Finished
exit 0
Binary file not shown.
@@ -0,0 +1,6 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
+1
View File
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module
+2
View File
@@ -0,0 +1,2 @@
# pack-refs with: peeled fully-peeled sorted
bd7063cc7c04689c4d082183d32a604ed27a24f9 refs/remotes/origin/master
@@ -0,0 +1 @@
bd7063cc7c04689c4d082183d32a604ed27a24f9
@@ -0,0 +1 @@
ref: refs/remotes/origin/master
+1 -1
View File
@@ -270,7 +270,7 @@
<dt>{{.i18n.Tr "admin.config.session_provider"}}</dt>
<dd>{{.SessionConfig.Provider}}</dd>
<dt>{{.i18n.Tr "admin.config.provider_config"}}</dt>
<dd><pre>{{if .SessionConfig.ProviderConfig}}{{.SessionConfig.ProviderConfig | JsonPrettyPrint}}{{else}}-{{end}}</pre></dd>
<dd><code>{{if .SessionConfig.ProviderConfig}}{{.SessionConfig.ProviderConfig}}{{else}}-{{end}}</code></dd>
<dt>{{.i18n.Tr "admin.config.cookie_name"}}</dt>
<dd>{{.SessionConfig.CookieName}}</dd>
<dt>{{.i18n.Tr "admin.config.gc_interval_time"}}</dt>
+6 -1
View File
@@ -28,6 +28,11 @@
{{range .Branches}}
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{EscapePound .}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}:{{end}}{{EscapePound $.HeadBranch}}">{{$.BaseName}}:{{.}}</div>
{{end}}
{{if .Repository.IsFork}}
{{range .BaseRepoBranches}}
<div class="item" data-url="{{$.PullRequestCtx.BaseRepo.Link}}/compare/{{EscapePound .}}...{{$.HeadUser.Name}}:{{EscapePound $.HeadBranch}}">{{$.PullRequestCtx.BaseRepo.OwnerName}}:{{.}}</div>
{{end}}
{{end}}
</div>
</div>
</div>
@@ -54,7 +59,7 @@
{{if .IsNothingToCompare}}
<div class="ui segment">{{.i18n.Tr "repo.pulls.nothing_to_compare"}}</div>
{{else if .PageIsComparePull}}
{{else if .PageIsComparePull}}
{{if .HasPullRequest}}
<div class="ui segment">
{{.i18n.Tr "repo.pulls.has_pull_request" $.RepoLink $.RepoRelPath .PullRequest.Index | Safe}}
+6 -6
View File
@@ -17,16 +17,16 @@
<div class="ui grid">
<div class="three wide column">
<div class="ui small input">
<input class="new-label-input emoji-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required>
<input class="new-label-input emoji-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50">
</div>
</div>
<div class="five wide column">
<div class="ui small fluid input">
<input class="new-label-desc-input" name="description" placeholder="{{.i18n.Tr "repo.issues.new_label_desc_placeholder"}}">
<input class="new-label-desc-input" name="description" placeholder="{{.i18n.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200">
</div>
</div>
<div class="color picker column">
<input class="color-picker" name="color" value="#70c24a" required>
<input class="color-picker" name="color" value="#70c24a" required maxlength="7">
</div>
<div class="column precolors">
{{template "repo/issue/label_precolors"}}
@@ -151,16 +151,16 @@
<div class="ui grid">
<div class="three wide column">
<div class="ui small input">
<input class="new-label-input emoji-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required>
<input class="new-label-input emoji-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required maxlength="50">
</div>
</div>
<div class="five wide column">
<div class="ui small fluid input">
<input class="new-label-desc-input" name="description" placeholder="{{.i18n.Tr "repo.issues.new_label_desc_placeholder"}}">
<input class="new-label-desc-input" name="description" placeholder="{{.i18n.Tr "repo.issues.new_label_desc_placeholder"}}" maxlength="200">
</div>
</div>
<div class="color picker column">
<input class="color-picker" name="color" value="#70c24a" required>
<input class="color-picker" name="color" value="#70c24a" required maxlength="7">
</div>
<div class="column precolors">
{{template "repo/issue/label_precolors"}}
+3 -3
View File
@@ -14,7 +14,7 @@
{{if .PageIsIssueList}}
<a class="ui green button" href="{{.RepoLink}}/issues/new">{{.i18n.Tr "repo.issues.new"}}</a>
{{else}}
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.PullRequestCtx.BaseRepo.Link}}/compare/{{.PullRequestCtx.BaseRepo.DefaultBranch | EscapePound}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{.Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | EscapePound}}{{end}}">{{.i18n.Tr "repo.pulls.new"}}</a>
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.Repository.Link}}/compare/{{.Repository.DefaultBranch | EscapePound}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{.Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | EscapePound}}{{end}}">{{.i18n.Tr "repo.pulls.new"}}</a>
{{end}}
</div>
{{else}}
@@ -154,7 +154,7 @@
<div class="menu">
{{range .Labels}}
<div class="item issue-action has-emoji" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
<span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
<span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
</div>
{{end}}
</div>
@@ -245,7 +245,7 @@
</a>
{{end}}
{{if .Ref}}
<a class="ref" href="{{$.RepoLink}}/src/branch/{{.Ref}}">
<a class="ref" href="{{$.RepoLink}}/src/branch/{{.Ref | PathEscapeSegments}}">
<span class="octicon octicon-git-branch"></span> {{.Ref}}
</a>
{{end}}
+2 -2
View File
@@ -60,7 +60,7 @@
<div class="menu">
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a>
{{range .Labels}}
<a class="item has-emoji" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.ID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
<a class="item has-emoji" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.ID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
{{end}}
</div>
</div>
@@ -148,7 +148,7 @@
<div class="menu">
{{range .Labels}}
<div class="item issue-action has-emoji" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
<span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
<span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
</div>
{{end}}
</div>
+1 -1
View File
@@ -26,7 +26,7 @@
<div class="eleven wide column">
<div class="field {{if .Err_Title}}error{{end}}">
<label>{{.i18n.Tr "repo.milestones.title"}}</label>
<input name="title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" autofocus required>
<input name="title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" autofocus required maxlength="50">
</div>
<div class="field">
<label>{{.i18n.Tr "repo.milestones.desc"}}</label>
+1 -1
View File
@@ -13,7 +13,7 @@
</a>
<div class="ui segment content">
<div class="field">
<input name="title" id="issue_title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" tabindex="3" autofocus required>
<input name="title" id="issue_title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" tabindex="3" autofocus required maxlength="255">
{{if .PageIsComparePull}}
<span class="title_wip_desc">{{.i18n.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</span>
{{end}}
+1 -1
View File
@@ -3,7 +3,7 @@
<h1 class="twelve wide column">
<span class="index">#{{.Issue.Index}}</span> <span id="issue-title" class="has-emoji">{{.Issue.Title}}</span>
<div id="edit-title-input" class="ui input" style="display: none">
<input value="{{.Issue.Title}}">
<input value="{{.Issue.Title}}" maxlength="255">
</div>
</h1>
{{if and (or .IsIssueWriter .IsIssuePoster) (not .Repository.IsArchived)}}
+2 -2
View File
@@ -19,7 +19,7 @@
{{if .PageIsEditRelease}}
<b>{{.tag_name}}</b><span class="at">@</span><strong>{{.tag_target}}</strong>
{{else}}
<input id="tag-name" name="tag_name" value="{{.tag_name}}" placeholder="{{.i18n.Tr "repo.release.tag_name"}}" autofocus required>
<input id="tag-name" name="tag_name" value="{{.tag_name}}" placeholder="{{.i18n.Tr "repo.release.tag_name"}}" autofocus required maxlength="255">
<span class="at">@</span>
<div class="ui selection dropdown">
<input type="hidden" name="tag_target" value="{{.tag_target}}"/>
@@ -42,7 +42,7 @@
<div class="eleven wide column">
<div class="field {{if .Err_Title}}error{{end}}">
<label>{{.i18n.Tr "repo.release.title"}}</label>
<input name="title" placeholder="{{.i18n.Tr "repo.release.title"}}" value="{{.title}}" autofocus required>
<input name="title" placeholder="{{.i18n.Tr "repo.release.title"}}" value="{{.title}}" autofocus required maxlength="255">
</div>
<div class="field">
<label>{{.i18n.Tr "repo.release.content"}}</label>
+2 -2
View File
@@ -5284,8 +5284,8 @@
}
],
"responses": {
"200": {
"$ref": "#/responses/StatusList"
"201": {
"$ref": "#/responses/Status"
}
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
</div>
{{if not .DisablePassword}}
{{if or (not .DisablePassword) .LinkAccountMode}}
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
<label for="password">{{.i18n.Tr "password"}}</label>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
+23
View File
@@ -0,0 +1,23 @@
---
kind: pipeline
name: test
workspace:
base: /go
path: src/gitea.com/lunny/levelqueue
steps:
- name: test
pull: default
image: golang:1.13
commands:
- go get -t -d -v ./...
- go build -v
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
environment:
GO111MODULE: "on"
GOPROXY: https://goproxy.cn
when:
event:
- push
- pull_request
+28
View File
@@ -0,0 +1,28 @@
# levelqueue
Level queue is a simple queue golang library base on go-leveldb.
[![Build Status](https://drone.gitea.com/api/badges/lunny/levelqueue/status.svg)](https://drone.gitea.com/lunny/levelqueue) [![](http://gocover.io/_badge/gitea.com/lunny/levelqueue)](http://gocover.io/gitea.com/lunny/levelqueue)
[![](https://goreportcard.com/badge/gitea.com/lunny/levelqueue)](https://goreportcard.com/report/gitea.com/lunny/levelqueue)
## Installation
```
go get gitea.com/lunny/levelqueue
```
## Usage
```Go
queue, err := levelqueue.Open("./queue")
err = queue.RPush([]byte("test"))
// pop an element from left of the queue
data, err = queue.LPop()
// if handle success, element will be pop, otherwise it will be keep
queue.LHandle(func(dt []byte) error{
return nil
})
```
+8
View File
@@ -0,0 +1,8 @@
module gitea.com/lunny/levelqueue
require (
github.com/stretchr/testify v1.3.0
github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66
)
go 1.13
@@ -1,18 +1,38 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66 h1:AwmkkZT+TucFotNCL+aNJ/0KCMsRtlXN9fs8uoOMSRk=
github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -140,25 +140,25 @@ func (queue *Queue) RPush(data []byte) error {
// LPush pushes a data from left of queue
func (queue *Queue) LPush(data []byte) error {
queue.highLock.Lock()
queue.lowLock.Lock()
id, err := queue.lowdecrement()
if err != nil {
queue.highLock.Unlock()
queue.lowLock.Unlock()
return err
}
err = queue.db.Put(id2bytes(id), data, nil)
queue.highLock.Unlock()
queue.lowLock.Unlock()
return err
}
// RPop pop a data from right of queue
func (queue *Queue) RPop() ([]byte, error) {
queue.highLock.Lock()
defer queue.highLock.Unlock()
currentID := queue.high
res, err := queue.db.Get(id2bytes(currentID), nil)
if err != nil {
queue.highLock.Unlock()
if err == leveldb.ErrNotFound {
return nil, ErrNotFound
}
@@ -167,26 +167,50 @@ func (queue *Queue) RPop() ([]byte, error) {
_, err = queue.highdecrement()
if err != nil {
queue.highLock.Unlock()
return nil, err
}
err = queue.db.Delete(id2bytes(currentID), nil)
queue.highLock.Unlock()
if err != nil {
return nil, err
}
return res, nil
}
// RHandle receives a user callback function to handle the right element of the queue, if function return nil, then delete the element, otherwise keep the element.
func (queue *Queue) RHandle(h func([]byte) error) error {
queue.highLock.Lock()
defer queue.highLock.Unlock()
currentID := queue.high
res, err := queue.db.Get(id2bytes(currentID), nil)
if err != nil {
if err == leveldb.ErrNotFound {
return ErrNotFound
}
return err
}
if err = h(res); err != nil {
return err
}
_, err = queue.highdecrement()
if err != nil {
return err
}
return queue.db.Delete(id2bytes(currentID), nil)
}
// LPop pop a data from left of queue
func (queue *Queue) LPop() ([]byte, error) {
queue.lowLock.Lock()
defer queue.lowLock.Unlock()
currentID := queue.low
res, err := queue.db.Get(id2bytes(currentID), nil)
if err != nil {
queue.lowLock.Unlock()
if err == leveldb.ErrNotFound {
return nil, ErrNotFound
}
@@ -199,13 +223,38 @@ func (queue *Queue) LPop() ([]byte, error) {
}
err = queue.db.Delete(id2bytes(currentID), nil)
queue.lowLock.Unlock()
if err != nil {
return nil, err
}
return res, nil
}
// LHandle receives a user callback function to handle the left element of the queue, if function return nil, then delete the element, otherwise keep the element.
func (queue *Queue) LHandle(h func([]byte) error) error {
queue.lowLock.Lock()
defer queue.lowLock.Unlock()
currentID := queue.low
res, err := queue.db.Get(id2bytes(currentID), nil)
if err != nil {
if err == leveldb.ErrNotFound {
return ErrNotFound
}
return err
}
if err = h(res); err != nil {
return err
}
_, err = queue.lowincrement()
if err != nil {
return err
}
return queue.db.Delete(id2bytes(currentID), nil)
}
// Close closes the queue
func (queue *Queue) Close() error {
err := queue.db.Close()
-23
View File
@@ -1,23 +0,0 @@
# levelqueue
Level queue is a simple queue golang library base on go-leveldb.
[![CircleCI](https://circleci.com/gh/lunny/levelqueue.svg?style=shield)](https://circleci.com/gh/lunny/levelqueue)
[![codecov](https://codecov.io/gh/lunny/levelqueue/branch/master/graph/badge.svg)](https://codecov.io/gh/lunny/levelqueue)
[![](https://goreportcard.com/badge/github.com/lunny/levelqueue)](https://goreportcard.com/report/github.com/lunny/levelqueue)
## Installation
```
go get github.com/lunny/levelqueue
```
## Usage
```Go
queue, err := levelqueue.Open("./queue")
err = queue.RPush([]byte("test"))
data, err = queue.LPop()
```
-3
View File
@@ -1,3 +0,0 @@
module github.com/lunny/levelqueue
require github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66
+101 -67
View File
@@ -5,7 +5,7 @@
// Package acme provides an implementation of the
// Automatic Certificate Management Environment (ACME) spec.
// The intial implementation was based on ACME draft-02 and
// is now being extended to comply with RFC8555.
// is now being extended to comply with RFC 8555.
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02
// and https://tools.ietf.org/html/rfc8555 for details.
//
@@ -44,7 +44,7 @@ import (
const (
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
LetsEncryptURL = "https://acme-v02.api.letsencrypt.org/directory"
// ALPNProto is the ALPN protocol name used by a CA server when validating
// tls-alpn-01 challenges.
@@ -60,7 +60,10 @@ var idPeACMEIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
const (
maxChainLen = 5 // max depth and breadth of a certificate chain
maxCertSize = 1 << 20 // max size of a certificate, in bytes
maxCertSize = 1 << 20 // max size of a certificate, in DER bytes
// Used for decoding certs from application/pem-certificate-chain response,
// the default when in RFC mode.
maxCertChainSize = maxCertSize * maxChainLen
// Max number of collected nonces kept in memory.
// Expect usual peak of 1 or 2.
@@ -139,8 +142,7 @@ type Client struct {
func (c *Client) accountKID(ctx context.Context) keyID {
c.cacheMu.Lock()
defer c.cacheMu.Unlock()
if c.dir.OrderURL == "" {
// Assume legacy CA.
if !c.dir.rfcCompliant() {
return noKeyID
}
if c.kid != noKeyID {
@@ -233,6 +235,9 @@ func (c *Client) directoryURL() string {
}
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
// It is incompatible with RFC 8555. Callers should use CreateOrderCert when interfacing
// with an RFC-compliant CA.
//
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
// with a different duration.
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
@@ -284,12 +289,22 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
// It retries the request until the certificate is successfully retrieved,
// context is cancelled by the caller or an error response is received.
//
// The returned value will also contain the CA (issuer) certificate if the bundle argument is true.
// If the bundle argument is true, the returned value also contains the CA (issuer)
// certificate chain.
//
// FetchCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid
// and has expected features.
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
if dir.rfcCompliant() {
return c.fetchCertRFC(ctx, url, bundle)
}
// Legacy non-authenticated GET request.
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
if err != nil {
return nil, err
@@ -304,10 +319,15 @@ func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]by
// For instance, the key pair of the certificate may be authorized.
// If the key is nil, c.Key is used instead.
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
if _, err := c.Discover(ctx); err != nil {
dir, err := c.Discover(ctx)
if err != nil {
return err
}
if dir.rfcCompliant() {
return c.revokeCertRFC(ctx, key, cert, reason)
}
// Legacy CA.
body := &struct {
Resource string `json:"resource"`
Cert string `json:"certificate"`
@@ -317,7 +337,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
Cert: base64.RawURLEncoding.EncodeToString(cert),
Reason: int(reason),
}
res, err := c.post(ctx, key, c.dir.RevokeURL, body, wantStatus(http.StatusOK))
res, err := c.post(ctx, key, dir.RevokeURL, body, wantStatus(http.StatusOK))
if err != nil {
return err
}
@@ -337,7 +357,7 @@ func AcceptTOS(tosURL string) bool { return true }
// Register calls prompt with a TOS URL provided by the CA. Prompt should report
// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
//
// When interfacing with RFC compliant CA, non-RFC8555 compliant fields of acct are ignored
// When interfacing with an RFC-compliant CA, non-RFC 8555 fields of acct are ignored
// and prompt is called if Directory's Terms field is non-zero.
// Also see Error's Instance field for when a CA requires already registered accounts to agree
// to an updated Terms of Service.
@@ -346,9 +366,7 @@ func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL
if err != nil {
return nil, err
}
// RFC8555 compliant account registration.
if dir.OrderURL != "" {
if dir.rfcCompliant() {
return c.registerRFC(ctx, acct, prompt)
}
@@ -370,16 +388,14 @@ func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL
// GetReg retrieves an existing account associated with c.Key.
//
// The url argument is an Account URI used with pre-RFC8555 CAs.
// It is ignored when interfacing with an RFC compliant CA.
// The url argument is an Account URI used with pre-RFC 8555 CAs.
// It is ignored when interfacing with an RFC-compliant CA.
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
// Assume RFC8555 compliant CA.
if dir.OrderURL != "" {
if dir.rfcCompliant() {
return c.getRegRFC(ctx)
}
@@ -395,16 +411,14 @@ func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
// UpdateReg updates an existing registration.
// It returns an updated account copy. The provided account is not modified.
//
// When interfacing with RFC compliant CAs, a.URI is ignored and the account URL
// When interfacing with RFC-compliant CAs, a.URI is ignored and the account URL
// associated with c.Key is used instead.
func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error) {
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
// Assume RFC8555 compliant CA.
if dir.OrderURL != "" {
if dir.rfcCompliant() {
return c.updateRegRFC(ctx, acct)
}
@@ -418,13 +432,21 @@ func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error)
return a, nil
}
// Authorize performs the initial step in an authorization flow.
// Authorize performs the initial step in the pre-authorization flow,
// as opposed to order-based flow.
// The caller will then need to choose from and perform a set of returned
// challenges using c.Accept in order to successfully complete authorization.
//
// Once complete, the caller can use AuthorizeOrder which the CA
// should provision with the already satisfied authorization.
// For pre-RFC CAs, the caller can proceed directly to requesting a certificate
// using CreateCert method.
//
// If an authorization has been previously granted, the CA may return
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
// need not fulfill any challenge and can proceed to requesting a certificate.
// a valid authorization which has its Status field set to StatusValid.
//
// More about pre-authorization can be found at
// https://tools.ietf.org/html/rfc8555#section-7.4.1.
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
return c.authorize(ctx, "dns", domain)
}
@@ -476,7 +498,17 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization
// If a caller needs to poll an authorization until its status is final,
// see the WaitAuthorization method.
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
var res *http.Response
if dir.rfcCompliant() {
res, err = c.postAsGet(ctx, url, wantStatus(http.StatusOK))
} else {
res, err = c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
}
if err != nil {
return nil, err
}
@@ -493,8 +525,8 @@ func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorizati
// The url argument is an Authorization.URI value.
//
// If successful, the caller will be required to obtain a new authorization
// using the Authorize method before being able to request a new certificate
// for the domain associated with the authorization.
// using the Authorize or AuthorizeOrder methods before being able to request
// a new certificate for the domain associated with the authorization.
//
// It does not revoke existing certificates.
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
@@ -528,8 +560,18 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
// In all other cases WaitAuthorization returns an error.
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
// Required for c.accountKID() when in RFC mode.
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
getfn := c.postAsGet
if !dir.rfcCompliant() {
getfn = c.get
}
for {
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
if err != nil {
return nil, err
}
@@ -572,10 +614,21 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
//
// A client typically polls a challenge status using this method.
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
// Required for c.accountKID() when in RFC mode.
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
getfn := c.postAsGet
if !dir.rfcCompliant() {
getfn = c.get
}
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
if err != nil {
return nil, err
}
defer res.Body.Close()
v := wireChallenge{URI: url}
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
@@ -590,23 +643,26 @@ func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, erro
// The server will then perform the validation asynchronously.
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
// Required for c.accountKID() when in RFC mode.
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
auth, err := keyAuth(c.Key.Public(), chal.Token)
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
req := struct {
Resource string `json:"resource"`
Type string `json:"type"`
Auth string `json:"keyAuthorization"`
}{
Resource: "challenge",
Type: chal.Type,
Auth: auth,
var req interface{} = json.RawMessage("{}") // RFC-compliant CA
if !dir.rfcCompliant() {
auth, err := keyAuth(c.Key.Public(), chal.Token)
if err != nil {
return nil, err
}
req = struct {
Resource string `json:"resource"`
Type string `json:"type"`
Auth string `json:"keyAuthorization"`
}{
Resource: "challenge",
Type: chal.Type,
Auth: auth,
}
}
res, err := c.post(ctx, nil, chal.URI, req, wantStatus(
http.StatusOK, // according to the spec
@@ -658,21 +714,8 @@ func (c *Client) HTTP01ChallengePath(token string) string {
}
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name.
//
// The implementation is incomplete in that the returned value is a single certificate,
// computed only for Z0 of the key authorization. ACME CAs are expected to update
// their implementations to use the newer version, TLS-SNI-02.
// For more details on TLS-SNI-01 see https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3.
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name of the TLS ClientHello matches exactly the returned name value.
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
ka, err := keyAuth(c.Key.Public(), token)
if err != nil {
@@ -689,17 +732,8 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
}
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name. For more details on TLS-SNI-02 see
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3.
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name in the TLS ClientHello matches exactly the returned name value.
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
b := sha256.Sum256([]byte(token))
h := hex.EncodeToString(b[:])
@@ -766,7 +800,7 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
return tlsChallengeCert([]string{domain}, newOpt)
}
// doReg sends all types of registration requests.
// doReg sends all types of registration requests the old way (pre-RFC world).
// The type of request is identified by typ argument, which is a "resource"
// in the ACME spec terms.
//

Some files were not shown because too many files have changed in this diff Show More