Merge branch 'master' into fix-6409
This commit is contained in:
commit
8f297caf26
1152 changed files with 117647 additions and 29965 deletions
1047
.drone.yml
1047
.drone.yml
File diff suppressed because it is too large
Load diff
25
.eslintrc
Normal file
25
.eslintrc
Normal file
|
@ -0,0 +1,25 @@
|
|||
root: true
|
||||
|
||||
extends:
|
||||
- eslint:recommended
|
||||
|
||||
parserOptions:
|
||||
ecmaVersion: 2015
|
||||
|
||||
env:
|
||||
browser: true
|
||||
jquery: true
|
||||
es6: true
|
||||
|
||||
globals:
|
||||
Clipboard: false
|
||||
CodeMirror: false
|
||||
emojify: false
|
||||
SimpleMDE: false
|
||||
Vue: false
|
||||
Dropzone: false
|
||||
u2fApi: false
|
||||
hljs: false
|
||||
|
||||
rules:
|
||||
no-unused-vars: [error, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, ignoreRestSiblings: true}]
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
open_collective: gitea
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -67,6 +67,7 @@ coverage.all
|
|||
/node_modules
|
||||
/modules/indexer/issues/indexers
|
||||
routers/repo/authorized_keys
|
||||
/yarn.lock
|
||||
|
||||
# Snapcraft
|
||||
snap/.snapcraft/
|
||||
|
|
97
.golangci.yml
Normal file
97
.golangci.yml
Normal file
|
@ -0,0 +1,97 @@
|
|||
linters:
|
||||
enable:
|
||||
- gosimple
|
||||
- deadcode
|
||||
- typecheck
|
||||
- govet
|
||||
- errcheck
|
||||
- staticcheck
|
||||
- unused
|
||||
- structcheck
|
||||
- varcheck
|
||||
- golint
|
||||
- dupl
|
||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||
- gofmt
|
||||
- misspell
|
||||
- gocritic
|
||||
enable-all: false
|
||||
disable-all: true
|
||||
fast: false
|
||||
|
||||
linters-settings:
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
- singleCaseSwitch # Every time this occured in the code, there was no other way.
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocyclo
|
||||
- errcheck
|
||||
- dupl
|
||||
- gosec
|
||||
- unparam
|
||||
- staticcheck
|
||||
- path: models/migrations/v
|
||||
linters:
|
||||
- gocyclo
|
||||
- errcheck
|
||||
- dupl
|
||||
- gosec
|
||||
- linters:
|
||||
- dupl
|
||||
text: "webhook"
|
||||
- linters:
|
||||
- gocritic
|
||||
text: "`ID' should not be capitalized"
|
||||
- path: modules/templates/helper.go
|
||||
linters:
|
||||
- gocritic
|
||||
- linters:
|
||||
- unused
|
||||
- deadcode
|
||||
text: "swagger"
|
||||
- path: contrib/pr/checkout.go
|
||||
linters:
|
||||
- errcheck
|
||||
- path: models/issue.go
|
||||
linters:
|
||||
- errcheck
|
||||
- path: models/migrations/
|
||||
linters:
|
||||
- errcheck
|
||||
- path: modules/log/
|
||||
linters:
|
||||
- errcheck
|
||||
- path: routers/routes/routes.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: routers/repo/view.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: models/migrations/
|
||||
linters:
|
||||
- unused
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "argument x is overwritten before first use"
|
||||
- path: modules/httplib/httplib.go
|
||||
linters:
|
||||
- staticcheck
|
||||
# Enabling this would require refactoring the methods and how they are called.
|
||||
- path: models/issue_comment_list.go
|
||||
linters:
|
||||
- dupl
|
||||
# "Destroy" is misspelled in github.com/go-macaron/session/session.go:213 so it's not our responsability to fix it
|
||||
- path: modules/session/virtual.go
|
||||
linters:
|
||||
- misspell
|
||||
text: '`Destory` is a misspelling of `Destroy`'
|
||||
- path: modules/session/memory.go
|
||||
linters:
|
||||
- misspell
|
||||
text: '`Destory` is a misspelling of `Destroy`'
|
1
.npmrc
Normal file
1
.npmrc
Normal file
|
@ -0,0 +1 @@
|
|||
save-exact=true
|
11
.stylelintrc
Normal file
11
.stylelintrc
Normal file
|
@ -0,0 +1,11 @@
|
|||
extends: stylelint-config-standard
|
||||
|
||||
rules:
|
||||
block-closing-brace-empty-line-before: null
|
||||
color-hex-length: null
|
||||
comment-empty-line-before: null
|
||||
declaration-empty-line-before: null
|
||||
indentation: 4
|
||||
no-descending-specificity: null
|
||||
rule-empty-line-before: null
|
||||
selector-pseudo-element-colon-notation: null
|
343
CHANGELOG.md
343
CHANGELOG.md
|
@ -4,6 +4,349 @@ 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.9.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.9.0-rc1) - 2019-07-06
|
||||
* BREAKING
|
||||
* Better logging (#6038) (#6095)
|
||||
* FEATURE
|
||||
* Content API for Creating, Updating, Deleting Files (#6314)
|
||||
* Enable tls-alpn-01: Use certmanager provided TLSConfig for LetsEncrypt (#7229)
|
||||
* Add command to convert mysql database from utf8 to utf8mb4 (#7144)
|
||||
* Fixes #2738 - Adds the /git/tags API endpoint (#7138)
|
||||
* Compare branches, commits and tags with each other (#6991)
|
||||
* Show Pull Request button or status of latest PR in branch list (#6990)
|
||||
* Repository avatars (#6986)
|
||||
* Show git-notes (#6984)
|
||||
* Add commit statuses reports on pull request view (#6845)
|
||||
* Number of commits ahead/behind in branch overview (#6695)
|
||||
* Add CLI commands to manage LDAP authentication source (#6681)
|
||||
* Add support for MS Teams webhooks (#6632)
|
||||
* OAuth2 Grant UI (#6625)
|
||||
* Add SUBJECT_PREFIX mailer config option (#6605)
|
||||
* Include custom configuration file in dump (#6516)
|
||||
* Add API for manipulating Git hooks (#6436)
|
||||
* Improve migrations to support migrating milestones/labels/issues/comments/pullrequests (#6290)
|
||||
* Add option to blame files (#5721)
|
||||
* Implement Default Webhooks (#4299)
|
||||
* Telegram webhook (#4227)
|
||||
* BUGFIXES
|
||||
* Correctly adjust mirror url (#6593)
|
||||
* Handle early git version's lack of get-url (#7065)
|
||||
* Fix icon position in issue view (#7354)
|
||||
* Cut timeline length with last element on issue view (#7355)
|
||||
* Fix mirror repository webhooks (#7366)
|
||||
* Fix api route for hooks (#7346)
|
||||
* Fix bug conflict between SyncReleasesWithTags and InsertReleases (#7337)
|
||||
* Fix pull view ui merge section (#7335)
|
||||
* Fix 7303 - remove unnessesary buttons on archived repos (#7326)
|
||||
* Fix topic bar to allow prefixes (#7325)
|
||||
* Fixes #7152 - Allow create/update/delete message to be empty, use default message (#7324)
|
||||
* Fixes #7238 - Annotated tag commit ID incorrect (#7321)
|
||||
* Dark theme fixes (#7319)
|
||||
* Gitea own dark codemirror theme (#7317)
|
||||
* Fixes #7292 - API File Contents bug (#7301)
|
||||
* Fix API link header (#7298)
|
||||
* Fix extra newlines when copying from diff in Firefox (#7288)
|
||||
* Make diff line-marker non-selectable (#7279)
|
||||
* Fix Submodule dection in subdir (#7275)
|
||||
* Fix error log when loading issues caused by a xorm bug (#7271)
|
||||
* Add .fa icon margin like .octicon (#7258)
|
||||
* Fix hljs unintenionally highlighting commit links (#7244)
|
||||
* Only check and config git on web subcommand but not others (#7236)
|
||||
* Fix migration panic when Head.User is not exist (#7226)
|
||||
* Only warn on errors in deleting LFS orphaned files during repo deletion (#7213)
|
||||
* Fix duplicated file on pull request conflicted files (#7211)
|
||||
* Allow colon between fixing word and issue (#7207)
|
||||
* Fix overflow issues in repo (#7190)
|
||||
* API error cleanup (#7186)
|
||||
* Add error for fork already existing (#7185)
|
||||
* Fixes diff on merged pull requests (#7171)
|
||||
* If milestone id is zero don't get it from database (#7169)
|
||||
* Fix pusher name via ssh push (#7167)
|
||||
* Fix database lock when use random repository fallback image (#7166)
|
||||
* Various fixes for issue mail notifications (#7165)
|
||||
* Allow archived repos to be (un)starred and (un)watched (#7163)
|
||||
* Fix GCArgs load from ini (#7156)
|
||||
* Detect noreply email address as user (#7133)
|
||||
* Avoid arbitrary format strings upon calling fail() function (#7112)
|
||||
* Validate External Tracker URL Format (#7089)
|
||||
* Repository avatar fallback configuration (#7087)
|
||||
* Fix #732: Add LFS objects to base repository on merging (#7082)
|
||||
* Install page - Handle invalid administrator username better (#7060)
|
||||
* Workaround for posting single comments in split diff view (#7052)
|
||||
* Fix possbile mysql invalid connnection error (#7051)
|
||||
* Fix charset was not saved after installation finished (#7048)
|
||||
* Handle insecure and ports in go get (#7041)
|
||||
* Avoid bad database state after failed migration (#7040)
|
||||
* Fix wrong init dependency on markup extensions (#7038)
|
||||
* Fix default for allowing new organization creation for new users (#7017)
|
||||
* Fix content download and /verify LFS handler expecting wrong content-type (#7015)
|
||||
* Fix missing repo description when migrating (#7000)
|
||||
* Fix LFS Locks over SSH (#6999)
|
||||
* Do not attempt to return blob on submodule (#6996)
|
||||
* Fix U2F for Chrome >= 74 (#6980)
|
||||
* Fix index produces problem when issues/pulls deleted (#6973)
|
||||
* Allow collaborators to view repo owned by private org (#6965)
|
||||
* Stop running hooks on pr merge (#6963)
|
||||
* Run hooks on merge/edit and cope with protected branches (#6961)
|
||||
* Webhook Logs show proper HTTP Method, and allow change HTTP method in form (#6953)
|
||||
* Stop colorizing log files by default (#6949)
|
||||
* Rotate serv.log, http.log and hook logs and stop stacktracing in these (#6935)
|
||||
* Fix plain text overflow line wrap (#6915)
|
||||
* Fix input size for dependency select (#6913)
|
||||
* Change drone token name to let users know to use oauth2 (#6912)
|
||||
* Fix syntax highlight in blame view #6895 (#6909)
|
||||
* Use AppURL for Oauth user link (#6894)
|
||||
* Fixes #6881 - API users search fix (#6882)
|
||||
* Fix 404 when send pull request some situation (#6871)
|
||||
* Enforce osusergo build tag for releases (#6862)
|
||||
* Fix 500 when reviewer is deleted with integration tests (#6856)
|
||||
* Fix v85.go (#6851)
|
||||
* Make dropTableColumns drop columns on sqlite and constraints on all (#6849)
|
||||
* Fix double-generation of scratch token (#6832) (#6833)
|
||||
* When mirroring we should set the remote to mirror (#6824)
|
||||
* Fix the v78 migration "Drop is_bare" on MSSQL #6707 (#6823)
|
||||
* Change verbose flag in dump command to avoid colliding with global version flag (#6822)
|
||||
* Fix #6813: Allow git.GetTree to take both commit and tree names (#6816)
|
||||
* Remove `seen` map from `getLastCommitForPaths` (#6807)
|
||||
* Show scrollbar only when needed (#6802)
|
||||
* Restore IsWindows variable assignment (#6722) (#6790)
|
||||
* Service worker js is a missing comma (#6788)
|
||||
* Fix team edit API panic (#6780)
|
||||
* Set user search base field optional in LDAP (simple auth) edit page (#6779)
|
||||
* Ignore already existing public keys after ldap sync (#6766)
|
||||
* Fix pulls broken when fork repository deleted (#6754)
|
||||
* Fix missing return (#6751)
|
||||
* Fix new team 500 (#6749)
|
||||
* OAuth2 token can be used in basic auth (#6747)
|
||||
* Fix org visibility bug when git cloning (#6743)
|
||||
* Fix bug when sort repos on org home page login with non-admin (#6741)
|
||||
* Stricter domain name pattern in email regex (#6739)
|
||||
* Fix admin template error (#6737)
|
||||
* Drop is_bare IDX only when it exists for MySQL and MariaDB (#6736)
|
||||
* UI: Detect and restore encoding and BOM in content (#6727)
|
||||
* Load issue attributes when editing an issue with API (#6723)
|
||||
* Fix team members API (#6714)
|
||||
* Unfortunately MemProvider Init does not actually Init properly (#6692)
|
||||
* Fix partial reversion of #6657 caused by #6314 (#6685)
|
||||
* Prevent creating empty sessions (#6677)
|
||||
* Fixes #6659 - Swagger schemes selection default to page's protocol (#6660)
|
||||
* Update highlight.js to 9.15.6 (#6658)
|
||||
* Properly escape on the redirect from the web editor (#6657)
|
||||
* Fix #6655 - Don't EscapePound .Link as it is already escaped (#6656)
|
||||
* Use ctx.metas for SHA hash links (#6645)
|
||||
* Fix wrong GPG expire date (#6643)
|
||||
* upgrade version of lib/pq to v1.1.0 (#6640)
|
||||
* Fix forking an empty repository (#6637)
|
||||
* Fix issuer of OTP URI should be URI-encoded. (#6634)
|
||||
* Return a UserList from /api/v1/admin/users (#6629)
|
||||
* Add json tags for oauth2 form (#6627)
|
||||
* Remove extra slash from twitter card (#6619)
|
||||
* remove bash requirement in makefile (#6617)
|
||||
* Fix Open Graph og:image link (#6612)
|
||||
* Fix cross-compile builds (#6609)
|
||||
* Change commit summary to full message in API (#6591)
|
||||
* Fix bug user search API pagesize didn't obey ExplorePagingNum (#6579)
|
||||
* Prevent server 500 on compare branches with no common history (#6555)
|
||||
* Properly escape release attachment URL (#6512)
|
||||
* Delete local branch when repo branch is deleted (#6497)
|
||||
* Fix bug when user login and want to resend register confirmation email (#6482)
|
||||
* Fix upload attachments (#6481)
|
||||
* Avoid multi-clicks in oauth2 login (#6467)
|
||||
* Hacky fix for alignment of the create-organization dialog (#6455)
|
||||
* Change order that PostProcess Processors are run (#6445)
|
||||
* Clean up ref name rules (#6437)
|
||||
* Fix Hook & HookList in Swagger (#6432)
|
||||
* Fixed unitTypeCode not being used in accessLevelUnit (#6419)
|
||||
* Display correct error for invalid mirror interval (#6414)
|
||||
* Don't Unescape redirect_to cookie value (#6399)
|
||||
* Fix dump table name error and add some test for dump database (#6394)
|
||||
* Fix migrations 82 to ignore unsynced tags between database and git data and missing is_archived on repository table (#6387)
|
||||
* Make sure units of a team are returned (#6379)
|
||||
* Fix bug manifest.json will not request with cookie so that session will created every request (#6372)
|
||||
* Disable benchmarking during tag events on DroneIO (#6365)
|
||||
* Comments list performance optimization (#5305)
|
||||
* ENHANCEMENT
|
||||
* Add API Endpoint for Repo Edit (#7006)
|
||||
* Add state param to milestone listing API (#7131)
|
||||
* Make captcha and password optional for external accounts (#6606)
|
||||
* Detect migrating batch size (#7353)
|
||||
* Fix 7255 - wrap long texts on user profile info (#7333)
|
||||
* Use commit graph files for listing pages (#7314)
|
||||
* Add git command line commitgraph support global default true when git version >= 2.18 (#7313)
|
||||
* Add LFS_START_SERVER option to control git-lfs support (#7281)
|
||||
* Dark theme markdown fixes (#7260)
|
||||
* Update go-git to v4.12.0 (#7249)
|
||||
* Show lfs config on admin panel (#7220)
|
||||
* Disable same user check for internal SSH (#7215)
|
||||
* Add LastLogin to the User API (#7196)
|
||||
* Add missing description of label on API (#7159)
|
||||
* Use go method to calculate ssh key fingerprint (#7128)
|
||||
* Enable Rust highlighting (#7125)
|
||||
* Refactor submodule URL parsing (#7100)
|
||||
* Change issue mail title. (#7064)
|
||||
* Use batch insert on migrating repository to make the process faster (#7050)
|
||||
* Improve github downloader on migrations (#7049)
|
||||
* When git version >= 2.18, git command could run with git wire protocol version 2 param if enabled (#7047)
|
||||
* Fix Erlang and Elixir highlight mappings (#7044)
|
||||
* API Org Visibility (#7028)
|
||||
* Improve handling of non-square avatars (#7025)
|
||||
* Bugfix: Align comment label and actions to the right (#7024)
|
||||
* Change UpdateRepoIndex api to include watchers (#7012)
|
||||
* Move serv hook functionality & drop GitLogger (#6993)
|
||||
* Add support of utf8mb4 for mysql (#6992)
|
||||
* Make webhook http connections resuable (#6976)
|
||||
* Move xorm logger bridge from log to models so that log module could be a standalone package (#6944)
|
||||
* Refactor models.NewRepoContext to extract git related codes to modules/git (#6941)
|
||||
* Remove macaron dependent on models (#6940)
|
||||
* Add less linter via npx (#6936)
|
||||
* Remove macaron dependent on modules/log (#6933)
|
||||
* Remove macaron dependent on models/mail.go (#6931)
|
||||
* Clean less files (#6921)
|
||||
* Fix code overflow (#6914)
|
||||
* Style orgs list in user profile (#6911)
|
||||
* Improve description of branch protection (fix #6886) (#6906)
|
||||
* Move sdk structs to modules/structs (#6905)
|
||||
* update sdk to latest (#6903)
|
||||
* Escape the commit message on issues update and title in telegram hook (#6901)
|
||||
* SearchRepositoryByName improvements and unification (#6897)
|
||||
* Change the color of issues/pulls list, merged is purple and closed is red (#6874)
|
||||
* Refactor table width to have more info shown in file list (#6867)
|
||||
* Monitor all git commands; move blame to git package and replace git as a variable (#6864)
|
||||
* Fix config ui error about cache ttl (#6861)
|
||||
* Improve localization of git activity stats (#6848)
|
||||
* Generate access token in admin cli (#6847)
|
||||
* Update github.com/urfave/cli to version 1.2.0 (#6838)
|
||||
* Rename LFS_JWT_SECRET cli option to include OAUTH2 as well (#6826)
|
||||
* internal/ssh: ignore env command totally (#6825)
|
||||
* Allow Recaptcha service url to be configured (#6820)
|
||||
* update github.com/mcuadros/go-version to v0.0.0-20190308113854-92cdf37c5b75 (#6815)
|
||||
* Use modules/git for git commands (#6775)
|
||||
* Add GET requests to webhook (#6771)
|
||||
* Move PushUpdate dependency from models to repofiles (#6763)
|
||||
* Tweak tab text and icon colors (#6760)
|
||||
* Ignore non-standard refs in git push (#6758)
|
||||
* Disable web preview for telegram webhook (#6719)
|
||||
* Show full name if DEFAULT_SHOW_FULL_NAME setting enabled (#6710)
|
||||
* Reorder file actions (#6706)
|
||||
* README WordPress the code is overflowing #6679 (#6696)
|
||||
* Improve issue reference on commit (#6694)
|
||||
* Handle redirects for git clone commands (#6688)
|
||||
* Fix one performance/correctness regression in #6478 found on Rails repository. (#6686)
|
||||
* API OTP Context (#6674)
|
||||
* Remove local clones & make hooks run on merge/edit/upload (#6672)
|
||||
* Bump github.com/stretchr/testify from 1.2.2 to 1.3.0 (#6663)
|
||||
* Bump gopkg.in/src-d/go-git.v4 from 4.8.0 to 4.10.0 (#6662)
|
||||
* Fix dropdown icon padding (#6651)
|
||||
* Add more title attributes on shortened names (#6647)
|
||||
* Update UI for topics labels on projects (#6639)
|
||||
* Trace Logging on Permission Denied & ColorFormat (#6618)
|
||||
* Add .gpg url (match github behaviour) (#6610)
|
||||
* Support for custom GITEA_CUSTOM env var in docker(#6608)
|
||||
* Show "delete branch" button on closed pull requests (#6570) (#6601)
|
||||
* Add option to disable refresh token invalidation (#6584)
|
||||
* Fix new repo dropdown alignment (#6583)
|
||||
* Fix mail notification when close/reopen issue (#6581)
|
||||
* Pre-calculate the absolute path of git (#6575)
|
||||
* Minor CSS cleanup for the navbar (#6553)
|
||||
* Render SHA1 links as code blocks (#6546)
|
||||
* Add username flag in create-user command (#6534)
|
||||
* Unifies pagination template usage (#6531) (#6533)
|
||||
* Fixes pagination width on mobile view (#5711) (#6532)
|
||||
* Improve SHA1 link detection (#6526)
|
||||
* Fixes #6446 - Sort team members and team's repositories (#6525)
|
||||
* Use stricter boundaries for auto-link detection (#6522)
|
||||
* Use regular line-height on frontpage entries (#6518)
|
||||
* Fixes #6514 - New Pull Request on files and pulls pages the same (#6515)
|
||||
* Make distinction between DisplayName and Username in email templates (#6495)
|
||||
* Add X-Auto-Response-Suppress header to outgoing messages (#6492)
|
||||
* Cleaned permission checks for API -> site admin can now do anything (#6483)
|
||||
* Support search operators for commits search (#6479)
|
||||
* Improve listing performance by using go-git (#6478)
|
||||
* Fix repo sub_menu font color in arc-green (#6477)
|
||||
* Show last commit status in pull request lists (#6465)
|
||||
* Add signatures to webhooks (#6428)
|
||||
* Optimize all images in public/img (#6427)
|
||||
* Add golangci (#6418)
|
||||
* Make "Ghost" not link to 404 page (#6410)
|
||||
* Include more variables on admin/config page (#6378)
|
||||
* Markdown: enable some more extensions (#6362)
|
||||
* Include repo name in page title tag (#6343)
|
||||
* Show locale string on timestamp (#6324)
|
||||
* Handle CORS requests (#6289)
|
||||
* Improve issue autolinks (#6273)
|
||||
* Migration Tweaks (#6260)
|
||||
* Add title attributes to all items in the repo list viewer (#6258)
|
||||
* Issue indexer queue redis support (#6218)
|
||||
* Add bio field for user (#6113)
|
||||
* Make the version within makefile overwriteable (#6080)
|
||||
* Updates to API 404 responses (#6077)
|
||||
* Use Go1.11 module (#5743)
|
||||
* UX + Security current user password reset (#5042)
|
||||
* Refactor: append, build variable and type switch (#4940)
|
||||
* Git statistics in Activity tab (#4724)
|
||||
* Drop the bits argument when generating an ed25519 key (#6504)
|
||||
* SECURITY
|
||||
* Shadow the password on cache and session config on admin panel (#7300)
|
||||
* TESTING
|
||||
* Exclude pull_request from fetch-tags step, fixes #7108 (#7120)
|
||||
* Refactor and improve git test (#7086)
|
||||
* Fix TestSearchRepo by waiting till indexing is done (#7004)
|
||||
* Add mssql migration tests (needs #6823) (#6852)
|
||||
* Add tests for Org API (#6731)
|
||||
* Context.ServerError and NotFound should log from their caller (#6550)
|
||||
* TRANSLATION
|
||||
* Add french specific rule for translating plural texts (#6846)
|
||||
* BUILD
|
||||
* Update mssql driver to last working version 20180314172330-6a30f4e59a44 (#7306)
|
||||
* Alpine 3.10 (#7256)
|
||||
* Use vfsgen instead of go-bindata (#7080)
|
||||
* remove and disable package-lock (#6969)
|
||||
* add make targets for js and css, add js linter (#6952)
|
||||
* Added tags pull step to drone config to show correct version hashes i… (#6836)
|
||||
* Make CustomPath, CustomConf and AppWorkPath configurable at build (#6631)
|
||||
* chore: update drone format to 1.0 (#6602)
|
||||
* Fix race in integration testlogger (#6556)
|
||||
* Quieter Integration Tests (#6513)
|
||||
* Drop the docker Makefile from the image (#6507)
|
||||
* Add make version on gitea version (#6485)
|
||||
* Fix #6468 - Uses space match and adds newline for all sed flavors (#6473)
|
||||
* Move code.gitea.io/git to code.gitea.io/gitea/modules/git (#6364)
|
||||
* Update npm dependencies and various tweaks (#7344)
|
||||
* Fix updated drone file (#7336)
|
||||
* Add 'npm' and 'npm-update' make targets and lockfile (#7246)
|
||||
* DOCS
|
||||
* Add work path CLI option (#6922)
|
||||
* Fix logging documentation (#6904)
|
||||
* Some logging documentation (#6498)
|
||||
* Fix link to Hacking on Gitea on From-Source doc page (#6471)
|
||||
* Fix typos in docs command-line examples (#6466)
|
||||
* Added docker example for backup (#5846)
|
||||
|
||||
## [1.8.3](https://github.com/go-gitea/gitea/releases/tag/v1.8.3) - 2019-06-17
|
||||
* BUGFIXES
|
||||
* Always set userID on LFS authentication (#7224) (Part of #6993)
|
||||
* Fix LFS Locks over SSH (#6999) (#7223)
|
||||
* Fix duplicated file on pull request conflicted files (#7211) (#7214)
|
||||
* Detect noreply email address as user (#7133) (#7195)
|
||||
* Don't get milestone from DB if ID is zero (#7169) (#7174)
|
||||
* Allow archived repos to be (un)starred and (un)watched (#7163) (#7168)
|
||||
* Fix GCArgs load from ini (#7156) (#7157)
|
||||
|
||||
## [1.8.2](https://github.com/go-gitea/gitea/releases/tag/v1.8.2) - 2019-05-29
|
||||
* BUGFIXES
|
||||
* Fix possbile mysql invalid connnection error (#7051) (#7071)
|
||||
* Handle invalid administrator username on install page (#7060) (#7063)
|
||||
* Disable arm7 builds (#7037) (#7042)
|
||||
* Fix default for allowing new organization creation for new users (#7017) (#7034)
|
||||
* SearchRepositoryByName improvements and unification (#6897) (#7002)
|
||||
* Fix u2f registrationlist ToRegistrations() method (#6980) (#6982)
|
||||
* Allow collaborators to view repo owned by private org (#6965) (#6968)
|
||||
* Use AppURL for Oauth user link (#6894) (#6925)
|
||||
* Escape the commit message on issues update (#6901) (#6902)
|
||||
* Fix regression for API users search (#6882) (#6885)
|
||||
* Handle early git version's lack of get-url (#7065) (#7076)
|
||||
* Fix wrong init dependency on markup extensions (#7038) (#7074)
|
||||
|
||||
## [1.8.1](https://github.com/go-gitea/gitea/releases/tag/v1.8.1) - 2019-05-08
|
||||
* BUGFIXES
|
||||
* Fix 404 when sending pull requests in some situations (#6871) (#6873)
|
||||
|
|
|
@ -65,17 +65,17 @@ high-level discussions.
|
|||
## Testing redux
|
||||
|
||||
Before submitting a pull request, run all the tests for the whole tree
|
||||
to make sure your changes don't cause regression elsewhere.
|
||||
to make sure your changes don't cause regression elsewhere.
|
||||
|
||||
Here's how to run the test suite:
|
||||
Here's how to run the test suite:
|
||||
|
||||
- Install the correct version of the drone-cli package. As of this
|
||||
writing, the correct drone-cli version is
|
||||
[0.8.6](https://0-8-0.docs.drone.io/cli-installation/).
|
||||
[1.1.0](https://docs.drone.io/cli/install/).
|
||||
- Ensure you have enough free disk space. You will need at least
|
||||
15-20 Gb of free disk space to hold all of the containers drone
|
||||
creates (a default AWS or GCE disk size won't work -- see
|
||||
[#6243](https://github.com/go-gitea/gitea/issues/6243)).
|
||||
[#6243](https://github.com/go-gitea/gitea/issues/6243)).
|
||||
- Change into the base directory of your copy of the gitea repository,
|
||||
and run `drone exec --local --build-event pull_request`.
|
||||
|
||||
|
@ -114,7 +114,7 @@ Generally, the go build tools are installed as-needed in the `Makefile`.
|
|||
An exception are the tools to build the CSS and images.
|
||||
|
||||
- To build CSS: Install [Node.js](https://nodejs.org/en/download/package-manager) at version 8.0 or above
|
||||
with `npm` and then run `npm install` and `make generate-stylesheets`.
|
||||
with `npm` and then run `npm install` and `make css`.
|
||||
- To build Images: ImageMagick, inkscape and zopflipng binaries must be
|
||||
available in your `PATH` to run `make generate-images`.
|
||||
|
||||
|
@ -214,7 +214,7 @@ to the maintainers team. If a maintainer is inactive for more than 3
|
|||
months and forgets to leave the maintainers team, the owners may move
|
||||
him or her from the maintainers team to the advisors team.
|
||||
For security reasons, Maintainers should use 2FA for their accounts and
|
||||
if possible provide gpg signed commits.
|
||||
if possible provide gpg signed commits.
|
||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
||||
https://help.github.com/articles/signing-commits-with-gpg/
|
||||
|
||||
|
@ -281,7 +281,7 @@ be reviewed by two maintainers and must pass the automatic tests.
|
|||
* Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`.
|
||||
* When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin`
|
||||
* If it is bugfix version create PR for changelog on branch `release/v$vmaj.$vmin` and wait till it is reviewed and merged.
|
||||
* Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`.
|
||||
* Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`.
|
||||
* And then push the tag as `git push origin v$vmaj.$vmin.$`. Drone CI will automatically created a release and upload all the compiled binary. (But currently it didn't add the release notes automatically. Maybe we should fix that.)
|
||||
* If needed send PR for changelog on branch `master`.
|
||||
* Send PR to [blog repository](https://github.com/go-gitea/blog) announcing the release.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
###################################
|
||||
#Build stage
|
||||
FROM golang:1.12-alpine3.9 AS build-env
|
||||
FROM golang:1.12-alpine3.10 AS build-env
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||
|
@ -18,7 +18,7 @@ WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
|||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||
&& make clean generate build
|
||||
|
||||
FROM alpine:3.9
|
||||
FROM alpine:3.10
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 22 3000
|
||||
|
|
|
@ -29,3 +29,4 @@ Andrew Thornton <art27@cantab.net> (@zeripath)
|
|||
John Olheiser <john.olheiser@gmail.com> (@jolheiser)
|
||||
Richard Mahn <rich.mahn@unfoldingword.org> (@richmahn)
|
||||
Mrsdizzie <info@mrsdizzie.com> (@mrsdizzie)
|
||||
silverwind <me@silverwind.io> (@silverwind)
|
||||
|
|
81
Makefile
81
Makefile
|
@ -97,15 +97,12 @@ vet:
|
|||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
@hash go-bindata > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/jteeuwen/go-bindata/go-bindata; \
|
||||
fi
|
||||
$(GO) generate $(PACKAGES)
|
||||
GO111MODULE=on $(GO) generate -mod=vendor $(PACKAGES)
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger:
|
||||
@hash swagger > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/go-swagger/go-swagger/cmd/swagger; \
|
||||
GO111MODULE="on" $(GO) get -u github.com/go-swagger/go-swagger/cmd/swagger@v0.19.0; \
|
||||
fi
|
||||
swagger generate spec -o './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
|
@ -138,6 +135,10 @@ errcheck:
|
|||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@echo 'make lint is depricated. Use "make revive" if you want to use the old lint tool, or "make golangci-lint" to run a complete code check.'
|
||||
|
||||
.PHONY: revive
|
||||
revive:
|
||||
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/mgechev/revive; \
|
||||
fi
|
||||
|
@ -335,7 +336,7 @@ release-linux:
|
|||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/*' -out gitea-$(VERSION) .
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
@ -365,32 +366,58 @@ release-compress:
|
|||
fi
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
|
||||
.PHONY: javascripts
|
||||
javascripts: public/js/index.js
|
||||
npm-check:
|
||||
@hash npm > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
echo "Please install Node.js 8.x or greater with npm"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
@hash npx > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
echo "Please install Node.js 8.x or greater with npm"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.IGNORE: public/js/index.js
|
||||
public/js/index.js: $(JAVASCRIPTS)
|
||||
cat $< >| $@
|
||||
.PHONY: npm
|
||||
npm: npm-check
|
||||
npm install --no-save
|
||||
|
||||
.PHONY: npm-update
|
||||
npm-update: npm-check
|
||||
npx updates -cu
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install --package-lock
|
||||
|
||||
.PHONY: js
|
||||
js: npm
|
||||
npx eslint public/js
|
||||
|
||||
.PHONY: css
|
||||
css: npm
|
||||
npx stylelint public/less
|
||||
npx lessc --clean-css="--s0 -b" public/less/index.less public/css/index.css
|
||||
$(foreach file, $(filter-out public/less/themes/_base.less, $(wildcard public/less/themes/*)),npx lessc --clean-css="--s0 -b" public/less/themes/$(notdir $(file)) > public/css/theme-$(notdir $(call strip-suffix,$(file))).css;)
|
||||
npx postcss --use autoprefixer --no-map --replace public/css/*
|
||||
|
||||
.PHONY: stylesheets-check
|
||||
stylesheets-check: generate-stylesheets
|
||||
@diff=$$(git diff public/css/*); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make generate-stylesheets' and commit the result:"; \
|
||||
if ([ -n "$$CI" ] && [ -n "$$diff" ]); then \
|
||||
echo "Generated files in public/css have changed, please commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: javascripts
|
||||
javascripts:
|
||||
echo "'make javascripts' is deprecated, please use 'make js'"
|
||||
$(MAKE) js
|
||||
|
||||
.PHONY: stylesheets-check
|
||||
stylesheets-check:
|
||||
echo "'make stylesheets-check' is deprecated, please use 'make css'"
|
||||
$(MAKE) css
|
||||
|
||||
.PHONY: generate-stylesheets
|
||||
generate-stylesheets:
|
||||
@hash npx > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
echo "Please install npm version 5.2+"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
$(eval BROWSERS := "> 1%, last 2 firefox versions, last 2 safari versions, ie 11")
|
||||
npx lessc --clean-css public/less/index.less public/css/index.css
|
||||
$(foreach file, $(filter-out public/less/themes/_base.less, $(wildcard public/less/themes/*)),npx lessc --clean-css public/less/themes/$(notdir $(file)) > public/css/theme-$(notdir $(call strip-suffix,$(file))).css;)
|
||||
$(foreach file, $(wildcard public/css/*),npx postcss --use autoprefixer --autoprefixer.browsers $(BROWSERS) -o $(file) $(file);)
|
||||
echo "'make generate-stylesheets' is deprecated, please use 'make css'"
|
||||
$(MAKE) css
|
||||
|
||||
.PHONY: swagger-ui
|
||||
swagger-ui:
|
||||
|
@ -441,3 +468,11 @@ generate-images:
|
|||
.PHONY: pr
|
||||
pr:
|
||||
$(GO) run contrib/pr/checkout.go $(PR)
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint:
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.16.0; \
|
||||
fi
|
||||
golangci-lint run
|
||||
|
|
|
@ -131,6 +131,10 @@ var (
|
|||
Subcommands: []cli.Command{
|
||||
microcmdAuthAddOauth,
|
||||
microcmdAuthUpdateOauth,
|
||||
cmdAuthAddLdapBindDn,
|
||||
cmdAuthUpdateLdapBindDn,
|
||||
cmdAuthAddLdapSimpleAuth,
|
||||
cmdAuthUpdateLdapSimpleAuth,
|
||||
microcmdAuthList,
|
||||
microcmdAuthDelete,
|
||||
},
|
||||
|
@ -144,7 +148,7 @@ var (
|
|||
|
||||
idFlag = cli.Int64Flag{
|
||||
Name: "id",
|
||||
Usage: "ID of OAuth authentication source",
|
||||
Usage: "ID of authentication source",
|
||||
}
|
||||
|
||||
microcmdAuthDelete = cli.Command{
|
||||
|
@ -481,7 +485,7 @@ func runUpdateOauth(c *cli.Context) error {
|
|||
}
|
||||
|
||||
// update custom URL mapping
|
||||
var customURLMapping *oauth2.CustomURLMapping
|
||||
var customURLMapping = &oauth2.CustomURLMapping{}
|
||||
|
||||
if oAuth2Config.CustomURLMapping != nil {
|
||||
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
|
||||
|
|
359
cmd/admin_auth_ldap.go
Normal file
359
cmd/admin_auth_ldap.go
Normal file
|
@ -0,0 +1,359 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth/ldap"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type (
|
||||
authService struct {
|
||||
initDB func() error
|
||||
createLoginSource func(loginSource *models.LoginSource) error
|
||||
updateLoginSource func(loginSource *models.LoginSource) error
|
||||
getLoginSourceByID func(id int64) (*models.LoginSource, error)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
commonLdapCLIFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "Authentication name.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "not-active",
|
||||
Usage: "Deactivate the authentication source.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "security-protocol",
|
||||
Usage: "Security protocol name.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-tls-verify",
|
||||
Usage: "Disable TLS verification.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host",
|
||||
Usage: "The address where the LDAP server can be reached.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port",
|
||||
Usage: "The port to use when connecting to the LDAP server.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "user-search-base",
|
||||
Usage: "The LDAP base at which user accounts will be searched for.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "user-filter",
|
||||
Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "admin-filter",
|
||||
Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "username-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user name.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "firstname-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s first name.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "surname-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s surname.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "email-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s email address.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "public-ssh-key-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
||||
},
|
||||
}
|
||||
|
||||
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
||||
cli.StringFlag{
|
||||
Name: "bind-dn",
|
||||
Usage: "The DN to bind to the LDAP server with when searching for the user.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "bind-password",
|
||||
Usage: "The password for the Bind DN, if any.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "attributes-in-bind",
|
||||
Usage: "Fetch attributes in bind DN context.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "synchronize-users",
|
||||
Usage: "Enable user synchronization.",
|
||||
},
|
||||
cli.UintFlag{
|
||||
Name: "page-size",
|
||||
Usage: "Search page size.",
|
||||
})
|
||||
|
||||
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
|
||||
cli.StringFlag{
|
||||
Name: "user-dn",
|
||||
Usage: "The user’s DN.",
|
||||
})
|
||||
|
||||
cmdAuthAddLdapBindDn = cli.Command{
|
||||
Name: "add-ldap",
|
||||
Usage: "Add new LDAP (via Bind DN) authentication source",
|
||||
Action: func(c *cli.Context) error {
|
||||
return newAuthService().addLdapBindDn(c)
|
||||
},
|
||||
Flags: ldapBindDnCLIFlags,
|
||||
}
|
||||
|
||||
cmdAuthUpdateLdapBindDn = cli.Command{
|
||||
Name: "update-ldap",
|
||||
Usage: "Update existing LDAP (via Bind DN) authentication source",
|
||||
Action: func(c *cli.Context) error {
|
||||
return newAuthService().updateLdapBindDn(c)
|
||||
},
|
||||
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
|
||||
}
|
||||
|
||||
cmdAuthAddLdapSimpleAuth = cli.Command{
|
||||
Name: "add-ldap-simple",
|
||||
Usage: "Add new LDAP (simple auth) authentication source",
|
||||
Action: func(c *cli.Context) error {
|
||||
return newAuthService().addLdapSimpleAuth(c)
|
||||
},
|
||||
Flags: ldapSimpleAuthCLIFlags,
|
||||
}
|
||||
|
||||
cmdAuthUpdateLdapSimpleAuth = cli.Command{
|
||||
Name: "update-ldap-simple",
|
||||
Usage: "Update existing LDAP (simple auth) authentication source",
|
||||
Action: func(c *cli.Context) error {
|
||||
return newAuthService().updateLdapSimpleAuth(c)
|
||||
},
|
||||
Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
|
||||
}
|
||||
)
|
||||
|
||||
// newAuthService creates a service with default functions.
|
||||
func newAuthService() *authService {
|
||||
return &authService{
|
||||
initDB: initDB,
|
||||
createLoginSource: models.CreateLoginSource,
|
||||
updateLoginSource: models.UpdateSource,
|
||||
getLoginSourceByID: models.GetLoginSourceByID,
|
||||
}
|
||||
}
|
||||
|
||||
// parseLoginSource assigns values on loginSource according to command line flags.
|
||||
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) {
|
||||
if c.IsSet("name") {
|
||||
loginSource.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("not-active") {
|
||||
loginSource.IsActived = !c.Bool("not-active")
|
||||
}
|
||||
if c.IsSet("synchronize-users") {
|
||||
loginSource.IsSyncEnabled = c.Bool("synchronize-users")
|
||||
}
|
||||
}
|
||||
|
||||
// parseLdapConfig assigns values on config according to command line flags.
|
||||
func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
|
||||
if c.IsSet("name") {
|
||||
config.Source.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("host") {
|
||||
config.Source.Host = c.String("host")
|
||||
}
|
||||
if c.IsSet("port") {
|
||||
config.Source.Port = c.Int("port")
|
||||
}
|
||||
if c.IsSet("security-protocol") {
|
||||
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
|
||||
}
|
||||
config.Source.SecurityProtocol = p
|
||||
}
|
||||
if c.IsSet("skip-tls-verify") {
|
||||
config.Source.SkipVerify = c.Bool("skip-tls-verify")
|
||||
}
|
||||
if c.IsSet("bind-dn") {
|
||||
config.Source.BindDN = c.String("bind-dn")
|
||||
}
|
||||
if c.IsSet("user-dn") {
|
||||
config.Source.UserDN = c.String("user-dn")
|
||||
}
|
||||
if c.IsSet("bind-password") {
|
||||
config.Source.BindPassword = c.String("bind-password")
|
||||
}
|
||||
if c.IsSet("user-search-base") {
|
||||
config.Source.UserBase = c.String("user-search-base")
|
||||
}
|
||||
if c.IsSet("username-attribute") {
|
||||
config.Source.AttributeUsername = c.String("username-attribute")
|
||||
}
|
||||
if c.IsSet("firstname-attribute") {
|
||||
config.Source.AttributeName = c.String("firstname-attribute")
|
||||
}
|
||||
if c.IsSet("surname-attribute") {
|
||||
config.Source.AttributeSurname = c.String("surname-attribute")
|
||||
}
|
||||
if c.IsSet("email-attribute") {
|
||||
config.Source.AttributeMail = c.String("email-attribute")
|
||||
}
|
||||
if c.IsSet("attributes-in-bind") {
|
||||
config.Source.AttributesInBind = c.Bool("attributes-in-bind")
|
||||
}
|
||||
if c.IsSet("public-ssh-key-attribute") {
|
||||
config.Source.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||
}
|
||||
if c.IsSet("page-size") {
|
||||
config.Source.SearchPageSize = uint32(c.Uint("page-size"))
|
||||
}
|
||||
if c.IsSet("user-filter") {
|
||||
config.Source.Filter = c.String("user-filter")
|
||||
}
|
||||
if c.IsSet("admin-filter") {
|
||||
config.Source.AdminFilter = c.String("admin-filter")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case.
|
||||
// It returns the value of the security protocol and if it was found.
|
||||
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
||||
for i, n := range models.SecurityProtocolNames {
|
||||
if strings.EqualFold(name, n) {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// getLoginSource gets the login source by its id defined in the command line flags.
|
||||
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
|
||||
func (a *authService) getLoginSource(c *cli.Context, loginType models.LoginType) (*models.LoginSource, error) {
|
||||
if err := argsSet(c, "id"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loginSource, err := a.getLoginSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if loginSource.Type != loginType {
|
||||
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", models.LoginNames[loginType], models.LoginNames[loginSource.Type])
|
||||
}
|
||||
|
||||
return loginSource, nil
|
||||
}
|
||||
|
||||
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
|
||||
func (a *authService) addLdapBindDn(c *cli.Context) error {
|
||||
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginSource := &models.LoginSource{
|
||||
Type: models.LoginLDAP,
|
||||
IsActived: true, // active by default
|
||||
Cfg: &models.LDAPConfig{
|
||||
Source: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.createLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
||||
func (a *authService) updateLdapBindDn(c *cli.Context) error {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginSource, err := a.getLoginSource(c, models.LoginLDAP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.updateLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
||||
func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
|
||||
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginSource := &models.LoginSource{
|
||||
Type: models.LoginDLDAP,
|
||||
IsActived: true, // active by default
|
||||
Cfg: &models.LDAPConfig{
|
||||
Source: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.createLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
|
||||
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginSource, err := a.getLoginSource(c, models.LoginDLDAP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.updateLoginSource(loginSource)
|
||||
}
|
1350
cmd/admin_auth_ldap_test.go
Normal file
1350
cmd/admin_auth_ldap_test.go
Normal file
File diff suppressed because it is too large
Load diff
21
cmd/cert.go
21
cmd/cert.go
|
@ -170,17 +170,28 @@ func runCert(c *cli.Context) error {
|
|||
if err != nil {
|
||||
log.Fatalf("Failed to open cert.pem for writing: %v", err)
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encode certificate: %v", err)
|
||||
}
|
||||
err = certOut.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write cert: %v", err)
|
||||
}
|
||||
log.Println("Written cert.pem")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||
}
|
||||
pem.Encode(keyOut, pemBlockForKey(priv))
|
||||
keyOut.Close()
|
||||
err = pem.Encode(keyOut, pemBlockForKey(priv))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encode key: %v", err)
|
||||
}
|
||||
err = keyOut.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write key: %v", err)
|
||||
}
|
||||
log.Println("Written key.pem")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
49
cmd/convert.go
Normal file
49
cmd/convert.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdConvert represents the available convert sub-command.
|
||||
var CmdConvert = cli.Command{
|
||||
Name: "convert",
|
||||
Usage: "Convert the database",
|
||||
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4",
|
||||
Action: runConvert,
|
||||
}
|
||||
|
||||
func runConvert(ctx *cli.Context) error {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("AppPath: %s", setting.AppPath)
|
||||
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Trace("Custom path: %s", setting.CustomPath)
|
||||
log.Trace("Log path: %s", setting.LogRootPath)
|
||||
models.LoadConfigs()
|
||||
|
||||
if models.DbCfg.Type != "mysql" {
|
||||
fmt.Println("This command can only be used with a MySQL database")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := models.ConvertUtf8ToUtf8mb4(); err != nil {
|
||||
log.Fatal("Failed to convert database from utf8 to utf8mb4: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Converted successfully, please confirm your database's character set is now utf8mb4")
|
||||
|
||||
return nil
|
||||
}
|
122
cmd/hook.go
122
cmd/hook.go
|
@ -8,15 +8,14 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -62,12 +61,11 @@ func runHookPreReceive(c *cli.Context) error {
|
|||
setup("hooks/pre-receive.log")
|
||||
|
||||
// the environment setted on serv command
|
||||
repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
|
||||
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||
username := os.Getenv(models.EnvRepoUsername)
|
||||
reponame := os.Getenv(models.EnvRepoName)
|
||||
userIDStr := os.Getenv(models.EnvPusherID)
|
||||
repoPath := models.RepoPath(username, reponame)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
@ -89,34 +87,22 @@ func runHookPreReceive(c *cli.Context) error {
|
|||
newCommitID := string(fields[1])
|
||||
refFullName := string(fields[2])
|
||||
|
||||
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||
protectBranch, err := private.GetProtectedBranchBy(repoID, branchName)
|
||||
if err != nil {
|
||||
fail("Internal error", fmt.Sprintf("retrieve protected branches information failed: %v", err))
|
||||
}
|
||||
|
||||
if protectBranch != nil && protectBranch.IsProtected() {
|
||||
// check and deletion
|
||||
if newCommitID == git.EmptySHA {
|
||||
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
|
||||
}
|
||||
|
||||
// detect force push
|
||||
if git.EmptySHA != oldCommitID {
|
||||
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDir(repoPath)
|
||||
if err != nil {
|
||||
fail("Internal error", "Fail to detect force push: %v", err)
|
||||
} else if len(output) > 0 {
|
||||
fail(fmt.Sprintf("branch %s is protected from force push", branchName), "")
|
||||
}
|
||||
}
|
||||
|
||||
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
|
||||
canPush, err := private.CanUserPush(protectBranch.ID, userID)
|
||||
if err != nil {
|
||||
fail("Internal error", "Fail to detect user can push: %v", err)
|
||||
} else if !canPush {
|
||||
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
|
||||
// If the ref is a branch, check if it's protected
|
||||
if strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||
statusCode, msg := private.HookPreReceive(username, reponame, private.HookOptions{
|
||||
OldCommitID: oldCommitID,
|
||||
NewCommitID: newCommitID,
|
||||
RefFullName: refFullName,
|
||||
UserID: userID,
|
||||
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||
ProtectedBranchID: prID,
|
||||
})
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("Internal Server Error", msg)
|
||||
case http.StatusForbidden:
|
||||
fail(msg, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +128,6 @@ func runHookPostReceive(c *cli.Context) error {
|
|||
setup("hooks/post-receive.log")
|
||||
|
||||
// the environment setted on serv command
|
||||
repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
|
||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||
repoName := os.Getenv(models.EnvRepoName)
|
||||
|
@ -169,58 +154,31 @@ func runHookPostReceive(c *cli.Context) error {
|
|||
newCommitID := string(fields[1])
|
||||
refFullName := string(fields[2])
|
||||
|
||||
if err := private.PushUpdate(models.PushUpdateOptions{
|
||||
RefFullName: refFullName,
|
||||
OldCommitID: oldCommitID,
|
||||
NewCommitID: newCommitID,
|
||||
PusherID: pusherID,
|
||||
PusherName: pusherName,
|
||||
RepoUserName: repoUser,
|
||||
RepoName: repoName,
|
||||
}); err != nil {
|
||||
log.GitLogger.Error("Update: %v", err)
|
||||
res, err := private.HookPostReceive(repoUser, repoName, private.HookOptions{
|
||||
OldCommitID: oldCommitID,
|
||||
NewCommitID: newCommitID,
|
||||
RefFullName: refFullName,
|
||||
UserID: pusherID,
|
||||
UserName: pusherName,
|
||||
})
|
||||
|
||||
if res == nil {
|
||||
fail("Internal Server Error", err)
|
||||
}
|
||||
|
||||
if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||
branch := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||
repo, pullRequestAllowed, err := private.GetRepository(repoID)
|
||||
if err != nil {
|
||||
log.GitLogger.Error("get repo: %v", err)
|
||||
break
|
||||
}
|
||||
if !pullRequestAllowed {
|
||||
break
|
||||
}
|
||||
|
||||
baseRepo := repo
|
||||
if repo.IsFork {
|
||||
baseRepo = repo.BaseRepo
|
||||
}
|
||||
|
||||
if !repo.IsFork && branch == baseRepo.DefaultBranch {
|
||||
break
|
||||
}
|
||||
|
||||
pr, err := private.ActivePullRequest(baseRepo.ID, repo.ID, baseRepo.DefaultBranch, branch)
|
||||
if err != nil {
|
||||
log.GitLogger.Error("get active pr: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if pr == nil {
|
||||
if repo.IsFork {
|
||||
branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", branch)
|
||||
fmt.Fprintf(os.Stderr, " %s/compare/%s...%s\n", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch))
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||
fmt.Fprintf(os.Stderr, " %s/pulls/%d\n", baseRepo.HTMLURL(), pr.Index)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if res["message"] == false {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if res["create"] == true {
|
||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res["branch"])
|
||||
fmt.Fprintf(os.Stderr, " %s\n", res["url"])
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||
fmt.Fprintf(os.Stderr, " %s\n", res["url"])
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
207
cmd/serv.go
207
cmd/serv.go
|
@ -8,14 +8,15 @@ package cmd
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/pprof"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
@ -23,12 +24,10 @@ import (
|
|||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
version "github.com/mcuadros/go-version"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
accessDenied = "Repository does not exist or you do not have access"
|
||||
lfsAuthenticateVerb = "git-lfs-authenticate"
|
||||
)
|
||||
|
||||
|
@ -45,30 +44,9 @@ var CmdServ = cli.Command{
|
|||
},
|
||||
}
|
||||
|
||||
func checkLFSVersion() {
|
||||
if setting.LFS.StartServer {
|
||||
//Disable LFS client hooks if installed for the current OS user
|
||||
//Needs at least git v2.1.2
|
||||
binVersion, err := git.BinVersion()
|
||||
if err != nil {
|
||||
fail(fmt.Sprintf("Error retrieving git version: %v", err), fmt.Sprintf("Error retrieving git version: %v", err))
|
||||
}
|
||||
|
||||
if !version.Compare(binVersion, "2.1.2", ">=") {
|
||||
setting.LFS.StartServer = false
|
||||
println("LFS server support needs at least Git v2.1.2, disabled")
|
||||
} else {
|
||||
git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "filter.lfs.required=",
|
||||
"-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setup(logPath string) {
|
||||
log.DelLogger("console")
|
||||
_ = log.DelLogger("console")
|
||||
setting.NewContext()
|
||||
checkLFSVersion()
|
||||
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
|
||||
}
|
||||
|
||||
func parseCmd(cmd string) (string, string) {
|
||||
|
@ -95,15 +73,14 @@ func fail(userMessage, logMessage string, args ...interface{}) {
|
|||
if !setting.ProdMode {
|
||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
}
|
||||
log.GitLogger.Fatal(logMessage, args...)
|
||||
return
|
||||
}
|
||||
|
||||
log.GitLogger.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func runServ(c *cli.Context) error {
|
||||
// FIXME: This needs to internationalised
|
||||
setup("serv.log")
|
||||
|
||||
if setting.SSH.Disabled {
|
||||
|
@ -112,13 +89,29 @@ func runServ(c *cli.Context) error {
|
|||
}
|
||||
|
||||
if len(c.Args()) < 1 {
|
||||
cli.ShowSubcommandHelp(c)
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
fmt.Printf("error showing subcommand help: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
keys := strings.Split(c.Args()[0], "-")
|
||||
if len(keys) != 2 || keys[0] != "key" {
|
||||
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||
}
|
||||
keyID := com.StrTo(keys[1]).MustInt64()
|
||||
|
||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||
if len(cmd) == 0 {
|
||||
println("Hi there, You've successfully authenticated, but Gitea does not provide shell access.")
|
||||
key, user, err := private.ServNoCommand(keyID)
|
||||
if err != nil {
|
||||
fail("Internal error", "Failed to check provided key: %v", err)
|
||||
}
|
||||
if key.Type == models.KeyTypeDeploy {
|
||||
println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
} else {
|
||||
println("Hi there: " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
}
|
||||
println("If this is unexpected, please log in with password and setup Gitea under another user.")
|
||||
return nil
|
||||
}
|
||||
|
@ -152,41 +145,19 @@ func runServ(c *cli.Context) error {
|
|||
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
||||
}
|
||||
|
||||
stopCPUProfiler := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
|
||||
stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
|
||||
if err != nil {
|
||||
fail("Internal Server Error", "Unable to start CPU profile: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
stopCPUProfiler()
|
||||
pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
|
||||
err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
|
||||
if err != nil {
|
||||
fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var (
|
||||
isWiki bool
|
||||
unitType = models.UnitTypeCode
|
||||
unitName = "code"
|
||||
)
|
||||
if strings.HasSuffix(reponame, ".wiki") {
|
||||
isWiki = true
|
||||
unitType = models.UnitTypeWiki
|
||||
unitName = "wiki"
|
||||
reponame = reponame[:len(reponame)-5]
|
||||
}
|
||||
|
||||
os.Setenv(models.EnvRepoUsername, username)
|
||||
if isWiki {
|
||||
os.Setenv(models.EnvRepoIsWiki, "true")
|
||||
} else {
|
||||
os.Setenv(models.EnvRepoIsWiki, "false")
|
||||
}
|
||||
os.Setenv(models.EnvRepoName, reponame)
|
||||
|
||||
repo, err := private.GetRepositoryByOwnerAndName(username, reponame)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "Failed to get repository: repository does not exist") {
|
||||
fail(accessDenied, "Repository does not exist: %s/%s", username, reponame)
|
||||
}
|
||||
fail("Internal error", "Failed to get repository: %v", err)
|
||||
}
|
||||
|
||||
requestedMode, has := allowedCommands[verb]
|
||||
if !has {
|
||||
fail("Unknown git command", "Unknown git command %s", verb)
|
||||
|
@ -202,97 +173,37 @@ func runServ(c *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Prohibit push to mirror repositories.
|
||||
if requestedMode > models.AccessModeRead && repo.IsMirror {
|
||||
fail("mirror repository is read-only", "")
|
||||
}
|
||||
|
||||
// Allow anonymous clone for public repositories.
|
||||
var (
|
||||
keyID int64
|
||||
user *models.User
|
||||
)
|
||||
if requestedMode == models.AccessModeWrite || repo.IsPrivate || setting.Service.RequireSignInView {
|
||||
keys := strings.Split(c.Args()[0], "-")
|
||||
if len(keys) != 2 {
|
||||
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||
}
|
||||
|
||||
key, err := private.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
|
||||
if err != nil {
|
||||
fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
|
||||
}
|
||||
keyID = key.ID
|
||||
|
||||
// Check deploy key or user key.
|
||||
if key.Type == models.KeyTypeDeploy {
|
||||
// Now we have to get the deploy key for this repo
|
||||
deployKey, err := private.GetDeployKey(key.ID, repo.ID)
|
||||
if err != nil {
|
||||
fail("Key access denied", "Failed to access internal api: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
if deployKey == nil {
|
||||
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
if deployKey.Mode < requestedMode {
|
||||
fail("Key permission denied", "Cannot push with read-only deployment key: %d to repo_id: %d", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
// Update deploy key activity.
|
||||
if err = private.UpdateDeployKeyUpdated(key.ID, repo.ID); err != nil {
|
||||
fail("Internal error", "UpdateDeployKey: %v", err)
|
||||
}
|
||||
|
||||
// FIXME: Deploy keys aren't really the owner of the repo pushing changes
|
||||
// however we don't have good way of representing deploy keys in hook.go
|
||||
// so for now use the owner
|
||||
os.Setenv(models.EnvPusherName, username)
|
||||
os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", repo.OwnerID))
|
||||
} else {
|
||||
user, err = private.GetUserByKeyID(key.ID)
|
||||
if err != nil {
|
||||
fail("internal error", "Failed to get user by key ID(%d): %v", keyID, err)
|
||||
}
|
||||
|
||||
if !user.IsActive || user.ProhibitLogin {
|
||||
fail("Your account is not active or has been disabled by Administrator",
|
||||
"User %s is disabled and have no access to repository %s",
|
||||
user.Name, repoPath)
|
||||
}
|
||||
|
||||
mode, err := private.CheckUnitUser(user.ID, repo.ID, user.IsAdmin, unitType)
|
||||
if err != nil {
|
||||
fail("Internal error", "Failed to check access: %v", err)
|
||||
} else if *mode < requestedMode {
|
||||
clientMessage := accessDenied
|
||||
if *mode >= models.AccessModeRead {
|
||||
clientMessage = "You do not have sufficient authorization for this action"
|
||||
}
|
||||
fail(clientMessage,
|
||||
"User %s does not have level %v access to repository %s's "+unitName,
|
||||
user.Name, requestedMode, repoPath)
|
||||
}
|
||||
|
||||
os.Setenv(models.EnvPusherName, user.Name)
|
||||
os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", user.ID))
|
||||
}
|
||||
results, err := private.ServCommand(keyID, username, reponame, requestedMode, verb, lfsVerb)
|
||||
if err != nil {
|
||||
if private.IsErrServCommand(err) {
|
||||
errServCommand := err.(private.ErrServCommand)
|
||||
if errServCommand.StatusCode != http.StatusInternalServerError {
|
||||
fail("Unauthorized", "%s", errServCommand.Error())
|
||||
} else {
|
||||
fail("Internal Server Error", "%s", errServCommand.Error())
|
||||
}
|
||||
}
|
||||
fail("Internal Server Error", "%s", err.Error())
|
||||
}
|
||||
os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
|
||||
os.Setenv(models.EnvRepoName, results.RepoName)
|
||||
os.Setenv(models.EnvRepoUsername, results.OwnerName)
|
||||
os.Setenv(models.EnvPusherName, results.UserName)
|
||||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||
os.Setenv(models.ProtectedBranchRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||
os.Setenv(models.ProtectedBranchPRID, fmt.Sprintf("%d", 0))
|
||||
|
||||
//LFS token authentication
|
||||
if verb == lfsAuthenticateVerb {
|
||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, username, repo.Name)
|
||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
||||
|
||||
now := time.Now()
|
||||
claims := jwt.MapClaims{
|
||||
"repo": repo.ID,
|
||||
"repo": results.RepoID,
|
||||
"op": lfsVerb,
|
||||
"exp": now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
||||
"nbf": now.Unix(),
|
||||
}
|
||||
if user != nil {
|
||||
claims["user"] = user.ID
|
||||
"user": results.UserID,
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
|
@ -313,7 +224,6 @@ func runServ(c *cli.Context) error {
|
|||
if err != nil {
|
||||
fail("Internal error", "Failed to encode LFS json response: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -329,13 +239,6 @@ func runServ(c *cli.Context) error {
|
|||
} else {
|
||||
gitcmd = exec.Command(verb, repoPath)
|
||||
}
|
||||
if isWiki {
|
||||
if err = private.InitWiki(repo.ID); err != nil {
|
||||
fail("Internal error", "Failed to init wiki repo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))
|
||||
|
||||
gitcmd.Dir = setting.RepoRootPath
|
||||
gitcmd.Stdout = os.Stdout
|
||||
|
@ -346,9 +249,9 @@ func runServ(c *cli.Context) error {
|
|||
}
|
||||
|
||||
// Update user key activity.
|
||||
if keyID > 0 {
|
||||
if err = private.UpdatePublicKeyUpdated(keyID); err != nil {
|
||||
fail("Internal error", "UpdatePublicKey: %v", err)
|
||||
if results.KeyID > 0 {
|
||||
if err = private.UpdatePublicKeyInRepo(results.KeyID, results.RepoID); err != nil {
|
||||
fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
21
cmd/web.go
21
cmd/web.go
|
@ -5,7 +5,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -15,7 +14,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup/external"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/routes"
|
||||
|
@ -83,11 +81,9 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
|
|||
}
|
||||
}()
|
||||
server := &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: m,
|
||||
TLSConfig: &tls.Config{
|
||||
GetCertificate: certManager.GetCertificate,
|
||||
},
|
||||
Addr: listenAddr,
|
||||
Handler: m,
|
||||
TLSConfig: certManager.TLSConfig(),
|
||||
}
|
||||
return server.ListenAndServeTLS("", "")
|
||||
}
|
||||
|
@ -111,8 +107,6 @@ func runWeb(ctx *cli.Context) error {
|
|||
|
||||
routers.GlobalInit()
|
||||
|
||||
external.RegisterParsers()
|
||||
|
||||
m := routes.NewMacaron()
|
||||
routes.RegisterRoutes(m)
|
||||
|
||||
|
@ -181,11 +175,16 @@ func runWeb(ctx *cli.Context) error {
|
|||
}
|
||||
err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||
case setting.FCGI:
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
var listener net.Listener
|
||||
listener, err = net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to bind %s: %v", listenAddr, err)
|
||||
}
|
||||
defer listener.Close()
|
||||
defer func() {
|
||||
if err := listener.Close(); err != nil {
|
||||
log.Fatal("Failed to stop server: %v", err)
|
||||
}
|
||||
}()
|
||||
err = fcgi.Serve(listener, context2.ClearHandler(m))
|
||||
case setting.UnixSocket:
|
||||
if err := os.Remove(listenAddr); err != nil && !os.IsNotExist(err) {
|
||||
|
|
42
contrib/fhs-compliant-script/gitea
Executable file
42
contrib/fhs-compliant-script/gitea
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/bin/bash
|
||||
|
||||
########################################################################
|
||||
# This script some defaults for gitea to run in a FHS compliant manner #
|
||||
########################################################################
|
||||
|
||||
# It assumes that you place this script as gitea in /usr/bin
|
||||
#
|
||||
# And place the original in /usr/lib/gitea with working files in /var/lib/gitea
|
||||
# and main configuration in /etc/gitea/app.ini
|
||||
GITEA="/usr/lib/gitea/gitea"
|
||||
WORK_DIR="/var/lib/gitea"
|
||||
APP_INI="/etc/gitea/app.ini"
|
||||
|
||||
APP_INI_SET=""
|
||||
for i in "$@"; do
|
||||
case "$i" in
|
||||
"-c")
|
||||
APP_INI_SET=1
|
||||
;;
|
||||
"-c="*)
|
||||
APP_INI_SET=1
|
||||
;;
|
||||
"--config")
|
||||
APP_INI_SET=1
|
||||
;;
|
||||
"--config="*)
|
||||
APP_INI_SET=1
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$APP_INI_SET" ]; then
|
||||
CONF_ARG="-c \"$APP_INI\""
|
||||
fi
|
||||
|
||||
# Provide FHS compliant defaults to
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" "$GITEA" $CONF_ARG "$@"
|
||||
|
||||
|
|
@ -20,9 +20,13 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/external"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/routes"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
context2 "github.com/gorilla/context"
|
||||
|
@ -30,9 +34,6 @@ import (
|
|||
"gopkg.in/src-d/go-git.v4/config"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/testfixtures.v2"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
var codeFilePath = "contrib/pr/checkout.go"
|
||||
|
@ -43,7 +44,7 @@ func runPR() {
|
|||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
setting.SetCustomPathAndConf("", "")
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.NewContext()
|
||||
|
||||
setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos")
|
||||
|
@ -90,8 +91,7 @@ func runPR() {
|
|||
routers.NewServices()
|
||||
//x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
||||
|
||||
var helper testfixtures.Helper
|
||||
helper = &testfixtures.SQLite{}
|
||||
var helper testfixtures.Helper = &testfixtures.SQLite{}
|
||||
models.NewEngine(func(_ *xorm.Engine) error {
|
||||
return nil
|
||||
})
|
||||
|
@ -113,6 +113,7 @@ func runPR() {
|
|||
log.Printf("[PR] Setting up router\n")
|
||||
//routers.GlobalInit()
|
||||
external.RegisterParsers()
|
||||
markup.Init()
|
||||
m := routes.NewMacaron()
|
||||
routes.RegisterRoutes(m)
|
||||
|
||||
|
|
|
@ -74,6 +74,23 @@ WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]
|
|||
; List of reasons why a Pull Request or Issue can be locked
|
||||
LOCK_REASONS=Too heated,Off-topic,Resolved,Spam
|
||||
|
||||
[cors]
|
||||
; More information about CORS can be found here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#The_HTTP_response_headers
|
||||
; enable cors headers (disabled by default)
|
||||
ENABLED=false
|
||||
; scheme of allowed requests
|
||||
SCHEME=http
|
||||
; list of requesting domains that are allowed
|
||||
ALLOW_DOMAIN=*
|
||||
; allow subdomains of headers listed above to request
|
||||
ALLOW_SUBDOMAIN=false
|
||||
; list of methods allowed to request
|
||||
METHODS=GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
|
||||
; max time to cache response
|
||||
MAX_AGE=10m
|
||||
; allow request with credentials
|
||||
ALLOW_CREDENTIALS=false
|
||||
|
||||
[ui]
|
||||
; Number of repositories that are displayed on one explore page
|
||||
EXPLORE_PAGING_NUM = 20
|
||||
|
@ -243,6 +260,9 @@ PASSWD =
|
|||
; For Postgres, either "disable" (default), "require", or "verify-full"
|
||||
; For MySQL, either "false" (default), "true", or "skip-verify"
|
||||
SSL_MODE = disable
|
||||
; For MySQL only, either "utf8" or "utf8mb4", default is "utf8".
|
||||
; NOTICE: for "utf8mb4" you must use MySQL InnoDB > 5.6. Gitea is unable to check this.
|
||||
CHARSET = utf8
|
||||
; For "sqlite3" and "tidb", use an absolute path when you start gitea as service
|
||||
PATH = data/gitea.db
|
||||
; For "sqlite3" only. Query timeout
|
||||
|
@ -299,6 +319,8 @@ MIN_PASSWORD_LENGTH = 6
|
|||
IMPORT_LOCAL_PATHS = false
|
||||
; Set to true to prevent all users (including admin) from creating custom git hooks
|
||||
DISABLE_GIT_HOOKS = false
|
||||
; Password Hash algorithm, either "pbkdf2", "argon2", "scrypt" or "bcrypt"
|
||||
PASSWORD_HASH_ALGO = pbkdf2
|
||||
|
||||
[openid]
|
||||
;
|
||||
|
@ -484,10 +506,18 @@ SESSION_LIFE_TIME = 86400
|
|||
|
||||
[picture]
|
||||
AVATAR_UPLOAD_PATH = data/avatars
|
||||
; Max Width and Height of uploaded avatars. This is to limit the amount of RAM
|
||||
; used when resizing the image.
|
||||
REPOSITORY_AVATAR_UPLOAD_PATH = data/repo-avatars
|
||||
; How Gitea deals with missing repository avatars
|
||||
; none = no avatar will be displayed; random = random avatar will be displayed; image = default image will be used
|
||||
REPOSITORY_AVATAR_FALLBACK = none
|
||||
REPOSITORY_AVATAR_FALLBACK_IMAGE = /img/repo_default.png
|
||||
; Max Width and Height of uploaded avatars.
|
||||
; This is to limit the amount of RAM used when resizing the image.
|
||||
AVATAR_MAX_WIDTH = 4096
|
||||
AVATAR_MAX_HEIGHT = 3072
|
||||
; Maximum alloved file size for uploaded avatars.
|
||||
; This is to limit the amount of RAM used when resizing the image.
|
||||
AVATAR_MAX_FILE_SIZE = 1048576
|
||||
; Chinese users can choose "duoshuo"
|
||||
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
||||
GRAVATAR_SOURCE = gravatar
|
||||
|
@ -640,6 +670,8 @@ SCHEDULE = @every 24h
|
|||
UPDATE_EXISTING = true
|
||||
|
||||
[git]
|
||||
; The path of git executable. If empty, Gitea searches through the PATH environment.
|
||||
PATH =
|
||||
; Disables highlight of added and removed changes
|
||||
DISABLE_DIFF_HIGHLIGHT = false
|
||||
; Max number of lines allowed in a single file in diff view
|
||||
|
@ -651,6 +683,8 @@ MAX_GIT_DIFF_FILES = 100
|
|||
; Arguments for command 'git gc', e.g. "--aggressive --auto"
|
||||
; see more on http://git-scm.com/docs/git-gc/
|
||||
GC_ARGS =
|
||||
; If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1
|
||||
EnableAutoGitWireProtocol = true
|
||||
|
||||
; Operation timeout in seconds
|
||||
[git.timeout]
|
||||
|
@ -681,7 +715,7 @@ DEFAULT_MAX_BLOB_SIZE = 10485760
|
|||
|
||||
[oauth2]
|
||||
; Enables OAuth2 provider
|
||||
ENABLED = true
|
||||
ENABLE = true
|
||||
; Lifetime of an OAuth2 access token in seconds
|
||||
ACCESS_TOKEN_EXPIRATION_TIME=3600
|
||||
; Lifetime of an OAuth2 access token in hours
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
export GITEA_CUSTOM=/data/gitea
|
|
@ -6,12 +6,16 @@ if [ ! -d /data/git/.ssh ]; then
|
|||
fi
|
||||
|
||||
if [ ! -f /data/git/.ssh/environment ]; then
|
||||
echo "GITEA_CUSTOM=/data/gitea" >| /data/git/.ssh/environment
|
||||
echo "GITEA_CUSTOM=$GITEA_CUSTOM" >| /data/git/.ssh/environment
|
||||
chmod 600 /data/git/.ssh/environment
|
||||
|
||||
elif ! grep -q "^GITEA_CUSTOM=$GITEA_CUSTOM$" /data/git/.ssh/environment; then
|
||||
sed -i /^GITEA_CUSTOM=/d /data/git/.ssh/environment
|
||||
echo "GITEA_CUSTOM=$GITEA_CUSTOM" >> /data/git/.ssh/environment
|
||||
fi
|
||||
|
||||
if [ ! -f /data/gitea/conf/app.ini ]; then
|
||||
mkdir -p /data/gitea/conf
|
||||
if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
|
||||
mkdir -p ${GITEA_CUSTOM}/conf
|
||||
|
||||
# Set INSTALL_LOCK to true only if SECRET_KEY is not empty and
|
||||
# INSTALL_LOCK is empty
|
||||
|
@ -27,6 +31,7 @@ if [ ! -f /data/gitea/conf/app.ini ]; then
|
|||
ROOT_URL=${ROOT_URL:-""} \
|
||||
DISABLE_SSH=${DISABLE_SSH:-"false"} \
|
||||
SSH_PORT=${SSH_PORT:-"22"} \
|
||||
LFS_START_SERVER=${LFS_START_SERVER:-"false"} \
|
||||
DB_TYPE=${DB_TYPE:-"sqlite3"} \
|
||||
DB_HOST=${DB_HOST:-"localhost:3306"} \
|
||||
DB_NAME=${DB_NAME:-"gitea"} \
|
||||
|
@ -36,7 +41,9 @@ if [ ! -f /data/gitea/conf/app.ini ]; then
|
|||
DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-"false"} \
|
||||
REQUIRE_SIGNIN_VIEW=${REQUIRE_SIGNIN_VIEW:-"false"} \
|
||||
SECRET_KEY=${SECRET_KEY:-""} \
|
||||
envsubst < /etc/templates/app.ini > /data/gitea/conf/app.ini
|
||||
envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini
|
||||
|
||||
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
|
||||
fi
|
||||
|
||||
# only chown if current owner is not already the gitea ${USER}. No recursive check to save time
|
||||
|
|
|
@ -24,6 +24,13 @@ if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then
|
|||
ssh-keygen -t ecdsa -b 256 -f /data/ssh/ssh_host_ecdsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ -d /etc/ssh ]; then
|
||||
SSH_PORT=${SSH_PORT:-"22"} \
|
||||
envsubst < /etc/templates/sshd_config > /etc/ssh/sshd_config
|
||||
|
||||
chmod 0644 /etc/ssh/sshd_config
|
||||
fi
|
||||
|
||||
chown root:root /data/ssh/*
|
||||
chmod 0700 /data/ssh
|
||||
chmod 0600 /data/ssh/*
|
||||
|
|
|
@ -17,6 +17,7 @@ HTTP_PORT = $HTTP_PORT
|
|||
ROOT_URL = $ROOT_URL
|
||||
DISABLE_SSH = $DISABLE_SSH
|
||||
SSH_PORT = $SSH_PORT
|
||||
LFS_START_SERVER = $LFS_START_SERVER
|
||||
LFS_CONTENT_PATH = /data/git/lfs
|
||||
|
||||
[database]
|
||||
|
@ -35,6 +36,7 @@ PROVIDER_CONFIG = /data/gitea/sessions
|
|||
|
||||
[picture]
|
||||
AVATAR_UPLOAD_PATH = /data/gitea/avatars
|
||||
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
|
||||
|
||||
[attachment]
|
||||
PATH = /data/gitea/attachments
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Port 22
|
||||
Port ${SSH_PORT}
|
||||
Protocol 2
|
||||
|
||||
AddressFamily any
|
|
@ -15,8 +15,8 @@ menu:
|
|||
|
||||
# Configuration Cheat Sheet
|
||||
|
||||
This is a cheat sheet for the Gitea configuration file. It contains most settings
|
||||
that can configured as well as their default values.
|
||||
This is a cheat sheet for the Gitea configuration file. It contains most of the settings
|
||||
that can be configured as well as their default values.
|
||||
|
||||
Any changes to the Gitea configuration file should be made in `custom/conf/app.ini`
|
||||
or any corresponding location. When installing from a distribution, this will
|
||||
|
@ -76,6 +76,16 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
|
||||
- `LOCK_REASONS`: **Too heated,Off-topic,Resolved,Spam**: A list of reasons why a Pull Request or Issue can be locked
|
||||
|
||||
## CORS (`cors`)
|
||||
|
||||
- `ENABLED`: **false**: enable cors headers (disabled by default)
|
||||
- `SCHEME`: **http**: scheme of allowed requests
|
||||
- `ALLOW_DOMAIN`: **\***: list of requesting domains that are allowed
|
||||
- `ALLOW_SUBDOMAIN`: **false**: allow subdomains of headers listed above to request
|
||||
- `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: list of methods allowed to request
|
||||
- `MAX_AGE`: **10m**: max time to cache response
|
||||
- `ALLOW_CREDENTIALS`: **false**: allow request with credentials
|
||||
|
||||
## UI (`ui`)
|
||||
|
||||
- `EXPLORE_PAGING_NUM`: **20**: Number of repositories that are shown in one explore page.
|
||||
|
@ -150,6 +160,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
- `USER`: **root**: Database username.
|
||||
- `PASSWD`: **\<empty\>**: Database user password. Use \`your password\` for quoting if you use special characters in the password.
|
||||
- `SSL_MODE`: **disable**: For PostgreSQL and MySQL only.
|
||||
- `CHARSET`: **utf8**: For MySQL only, either "utf8" or "utf8mb4", default is "utf8". NOTICE: for "utf8mb4" you must use MySQL InnoDB > 5.6. Gitea is unable to check this.
|
||||
- `PATH`: **data/gitea.db**: For SQLite3 only, the database file path.
|
||||
- `LOG_SQL`: **true**: Log the executed SQL.
|
||||
- `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed.
|
||||
|
@ -184,6 +195,9 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
- `DISABLE_GIT_HOOKS`: **false**: Set to `true` to prevent all users (including admin) from creating custom
|
||||
git hooks.
|
||||
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
|
||||
- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
|
||||
- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
|
||||
- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[pbkdf2, argon2, scrypt, bcrypt\].
|
||||
|
||||
## OpenID (`openid`)
|
||||
|
||||
|
@ -203,6 +217,9 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
Requires `Mailer` to be enabled.
|
||||
- `DISABLE_REGISTRATION`: **false**: Disable registration, after which only admin can create
|
||||
accounts for users.
|
||||
- `REQUIRE_EXTERNAL_REGISTRATION_PASSWORD`: **false**: Enable this to force externally created
|
||||
accounts (via GitHub, OpenID Connect, etc) to create a password. Warning: enabling this will
|
||||
decrease security, so you should only enable it if you know what you're doing.
|
||||
- `REQUIRE_SIGNIN_VIEW`: **false**: Enable this to force users to log in to view any page.
|
||||
- `ENABLE_NOTIFY_MAIL`: **false**: Enable this to send e-mail to watchers of a repository when
|
||||
something happens, like creating issues. Requires `Mailer` to be enabled.
|
||||
|
@ -212,6 +229,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
- `ENABLE_REVERSE_PROXY_EMAIL`: **false**: Enable this to allow to auto-registration with a
|
||||
provided email rather than a generated email.
|
||||
- `ENABLE_CAPTCHA`: **false**: Enable this to use captcha validation for registration.
|
||||
- `REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA`: **false**: Enable this to force captcha validation
|
||||
even for External Accounts (i.e. GitHub, OpenID Connect, etc). You must `ENABLE_CAPTCHA` also.
|
||||
- `CAPTCHA_TYPE`: **image**: \[image, recaptcha\]
|
||||
- `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha.
|
||||
- `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha.
|
||||
|
@ -279,7 +298,16 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
- `DISABLE_GRAVATAR`: **false**: Enable this to use local avatars only.
|
||||
- `ENABLE_FEDERATED_AVATAR`: **false**: Enable support for federated avatars (see
|
||||
[http://www.libravatar.org](http://www.libravatar.org)).
|
||||
- `AVATAR_UPLOAD_PATH`: **data/avatars**: Path to store local and cached files.
|
||||
- `AVATAR_UPLOAD_PATH`: **data/avatars**: Path to store user avatar image files.
|
||||
- `REPOSITORY_AVATAR_UPLOAD_PATH`: **data/repo-avatars**: Path to store repository avatar image files.
|
||||
- `REPOSITORY_AVATAR_FALLBACK`: **none**: How Gitea deals with missing repository avatars
|
||||
- none = no avatar will be displayed
|
||||
- random = random avatar will be generated
|
||||
- image = default image will be used (which is set in `REPOSITORY_AVATAR_DEFAULT_IMAGE`)
|
||||
- `REPOSITORY_AVATAR_FALLBACK_IMAGE`: **/img/repo_default.png**: Image used as default repository avatar (if `REPOSITORY_AVATAR_FALLBACK` is set to image and none was uploaded)
|
||||
- `AVATAR_MAX_WIDTH`: **4096**: Maximum avatar image width in pixels.
|
||||
- `AVATAR_MAX_HEIGHT`: **3072**: Maximum avatar image height in pixels.
|
||||
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): Maximum avatar image file size in bytes.
|
||||
|
||||
## Attachment (`attachment`)
|
||||
|
||||
|
@ -324,7 +352,7 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
|
|||
|
||||
### Console log mode (`log.console`, `log.console.*`, or `MODE=console`)
|
||||
|
||||
- For the console logger `COLORIZE` will default to `true` if not on windows.
|
||||
- For the console logger `COLORIZE` will default to `true` if not on windows or the terminal is determined to be able to color.
|
||||
- `STDERR`: **false**: Use Stderr instead of Stdout.
|
||||
|
||||
### File log mode (`log.file`, `log.file.*` or `MODE=file`)
|
||||
|
@ -334,7 +362,6 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
|
|||
- `MAX_SIZE_SHIFT`: **28**: Maximum size shift of a single file, 28 represents 256Mb.
|
||||
- `DAILY_ROTATE`: **true**: Rotate logs daily.
|
||||
- `MAX_DAYS`: **7**: Delete the log file after n days
|
||||
- NB: `COLORIZE`: will default to `true` if not on windows.
|
||||
- `COMPRESS`: **true**: Compress old log files by default with gzip
|
||||
- `COMPRESSION_LEVEL`: **-1**: Compression level
|
||||
|
||||
|
@ -382,10 +409,12 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
|
|||
|
||||
## Git (`git`)
|
||||
|
||||
- `PATH`: **""**: The path of git executable. If empty, Gitea searches through the PATH environment.
|
||||
- `MAX_GIT_DIFF_LINES`: **100**: Max number of lines allowed of a single file in diff view.
|
||||
- `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view.
|
||||
- `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view.
|
||||
- `GC_ARGS`: **\<empty\>**: Arguments for command `git gc`, e.g. `--aggressive --auto`. See more on http://git-scm.com/docs/git-gc/
|
||||
- `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1
|
||||
|
||||
## Git - Timeout settings (`git.timeout`)
|
||||
- `DEFAUlT`: **360**: Git operations default timeout seconds.
|
||||
|
@ -397,7 +426,7 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
|
|||
|
||||
## Metrics (`metrics`)
|
||||
|
||||
- `ENABLED`: **false**: Enables /metrics endpoint for prometheus.
|
||||
- `ENABLED`: **false**: Enables /metrics endpoint for prometheus.
|
||||
- `TOKEN`: **\<empty\>**: You need to specify the token, if you want to include in the authorization the metrics . The same token need to be used in prometheus parameters `bearer_token` or `bearer_token_file`.
|
||||
|
||||
## API (`api`)
|
||||
|
@ -410,7 +439,7 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
|
|||
|
||||
## OAuth2 (`oauth2`)
|
||||
|
||||
- `ENABLED`: **true**: Enables OAuth2 provider.
|
||||
- `ENABLE`: **true**: Enables OAuth2 provider.
|
||||
- `ACCESS_TOKEN_EXPIRATION_TIME`: **3600**: Lifetime of an OAuth2 access token in seconds
|
||||
- `REFRESH_TOKEN_EXPIRATION_TIME`: **730**: Lifetime of an OAuth2 access token in hours
|
||||
- `INVALIDATE_REFRESH_TOKEN`: **false**: Check if refresh token got already used
|
||||
|
|
|
@ -78,7 +78,8 @@ menu:
|
|||
- `NAME`: 数据库名称。
|
||||
- `USER`: 数据库用户名。
|
||||
- `PASSWD`: 数据库用户密码。
|
||||
- `SSL_MODE`: PostgreSQL数据库是否启用SSL模式。
|
||||
- `SSL_MODE`: MySQL 或 PostgreSQL数据库是否启用SSL模式。
|
||||
- `CHARSET`: **utf8**: 仅当数据库为 MySQL 时有效, 可以为 "utf8" 或 "utf8mb4"。注意:如果使用 "utf8mb4",你的 MySQL InnoDB 版本必须在 5.6 以上。
|
||||
- `PATH`: Tidb 或者 SQLite3 数据文件存放路径。
|
||||
- `LOG_SQL`: **true**: 显示生成的SQL,默认为真。
|
||||
|
||||
|
@ -209,6 +210,7 @@ menu:
|
|||
- `CLONE`: **300**: 内部仓库间克隆的超时时间,单位秒
|
||||
- `PULL`: **300**: 内部仓库间拉取的超时时间,单位秒
|
||||
- `GC`: **60**: git仓库GC的超时时间,单位秒
|
||||
- `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: 是否根据 Git Wire Protocol协议支持情况自动切换版本,当 git 版本在 2.18 及以上时会自动切换到版本2。为 `false` 则不切换。
|
||||
|
||||
## API (`api`)
|
||||
|
||||
|
|
|
@ -98,6 +98,20 @@ Apart from `extra_links.tmpl` and `extra_tabs.tmpl`, there are other useful temp
|
|||
- `body_outer_post.tmpl`, before the bottom `<footer>` element.
|
||||
- `footer.tmpl`, right before the end of the `<body>` tag, a good place for additional Javascript.
|
||||
|
||||
## Customizing Gitea mails
|
||||
|
||||
The `custom/templates/mail` folder allows changing the body of every mail of Gitea.
|
||||
Templates to override can be found in the
|
||||
[`templates/mail`](https://github.com/go-gitea/gitea/tree/master/templates/mail)
|
||||
directory of Gitea source.
|
||||
Override by making a copy of the file under `custom/templates/mail` using a
|
||||
full path structure matching source.
|
||||
|
||||
Any statement contained inside `{{` and `}}` are Gitea's template
|
||||
syntax and shouldn't be touched without fully understanding these components.
|
||||
|
||||
|
||||
|
||||
## Adding Analytics to Gitea
|
||||
|
||||
Google Analytics, Matomo (previously Piwik), and other analytics services can be added to Gitea. To add the tracking code, refer to the `Other additions to the page` section of this document, and add the JavaScript to the `custom/templates/custom/header.tmpl` file.
|
||||
|
|
|
@ -32,7 +32,7 @@ necessary. To be able to use these you must have the `"$GOPATH"/bin` directory
|
|||
on the executable path. If you don't add the go bin directory to the
|
||||
executable path you will have to manage this yourself.
|
||||
|
||||
**Note 2**: Go version 1.9 or higher is required; however, it is important
|
||||
**Note 2**: Go version 1.11 or higher is required; however, it is important
|
||||
to note that our continuous integration will check that the formatting of the
|
||||
source code is not changed by `gofmt` using `make fmt-check`. Unfortunately,
|
||||
the results of `gofmt` can differ by the version of `go`. It is therefore
|
||||
|
@ -136,31 +136,24 @@ You should lint, vet and spell-check with:
|
|||
make vet lint misspell-check
|
||||
```
|
||||
|
||||
### Updating the stylesheets
|
||||
### Updating CSS
|
||||
|
||||
To generate the stylsheets, you will need [Node.js](https://nodejs.org/) at version 8.0 or above.
|
||||
To generate the CSS, you will need [Node.js](https://nodejs.org/) 8.0 or greater with npm. At present we use [less](http://lesscss.org/) and [postcss](https://postcss.org) to generate our CSS. Do **not** edit the files in `public/css` directly, as they are generated from `lessc` from the files in `public/less`.
|
||||
|
||||
At present we use [less](http://lesscss.org/) and [postcss](https://postcss.org) to generate our stylesheets. Do
|
||||
**not** edit the files in `public/css/` directly, as they are generated from
|
||||
`lessc` from the files in `public/less/`.
|
||||
|
||||
If you wish to work on the stylesheets, you will need to install `lessc` the
|
||||
less compiler and `postcss`. The recommended way to do this is using `npm install`:
|
||||
Edit files in `public/less`, run the linter, regenerate the CSS and commit all changed files:
|
||||
|
||||
```bash
|
||||
cd "$GOPATH/src/code.gitea.io/gitea"
|
||||
npm install
|
||||
make css
|
||||
```
|
||||
|
||||
You can then edit the less stylesheets and regenerate the stylesheets using:
|
||||
### Updating JS
|
||||
|
||||
To run the JavaScript linter you will need [Node.js](https://nodejs.org/) 8.0 or greater with npm. Edit files in `public/js` and run the linter:
|
||||
|
||||
```bash
|
||||
make generate-stylesheets
|
||||
make js
|
||||
```
|
||||
|
||||
You should commit both the changes to the css and the less files when making
|
||||
PRs.
|
||||
|
||||
### Updating the API
|
||||
|
||||
When creating new API routes or modifying existing API routes, you **MUST**
|
||||
|
@ -203,7 +196,7 @@ OpenAPI 3 documentation.
|
|||
When creating new configuration options, it is not enough to add them to the
|
||||
`modules/setting` files. You should add information to `custom/conf/app.ini`
|
||||
and to the
|
||||
<a href='{{ relref "doc/advanced/config-cheat-sheet.en-us.md"}}'>configuration cheat sheet</a>
|
||||
<a href='{{< relref "doc/advanced/config-cheat-sheet.en-us.md" >}}'>configuration cheat sheet</a>
|
||||
found in `docs/content/doc/advanced/config-cheat-sheet.en-us.md`
|
||||
|
||||
### Changing the logo
|
||||
|
@ -244,7 +237,7 @@ TAGS="bindata sqlite sqlite_unlock_notify" make generate build test-sqlite
|
|||
```
|
||||
|
||||
will run the integration tests in an sqlite environment. Other database tests
|
||||
are available but may need adjustment to the local environment.
|
||||
are available but may need adjustment to the local environment.
|
||||
|
||||
Look at
|
||||
[`integrations/README.md`](https://github.com/go-gitea/gitea/blob/master/integrations/README.md)
|
||||
|
|
|
@ -27,7 +27,6 @@ log groups:
|
|||
* The Router logger
|
||||
* The Access logger
|
||||
* The XORM logger
|
||||
* A logger called the `GitLogger` which is used during hooks.
|
||||
|
||||
There is also the go log logger.
|
||||
|
||||
|
@ -180,21 +179,6 @@ which will not be inherited from the `[log]` or relevant
|
|||
* `EXPRESSION` will default to `""`
|
||||
* `PREFIX` will default to `""`
|
||||
|
||||
### The Hook and Serv "GitLoggers"
|
||||
|
||||
These are less well defined loggers. Essentially these should only be
|
||||
used within Gitea's subsystems and cannot be configured at present.
|
||||
|
||||
They will write log files in:
|
||||
|
||||
* `%(ROOT_PATH)/hooks/pre-receive.log`
|
||||
* `%(ROOT_PATH)/hooks/update.log`
|
||||
* `%(ROOT_PATH)/hooks/post-receive.log`
|
||||
* `%(ROOT_PATH)/serv.log`
|
||||
* `%(ROOT_PATH)/http.log`
|
||||
|
||||
In the future these logs may be rationalised.
|
||||
|
||||
## Log outputs
|
||||
|
||||
Gitea provides 4 possible log outputs:
|
||||
|
@ -213,7 +197,7 @@ from `[log.sublogger]`.
|
|||
a stacktrace. This value is inherited.
|
||||
* `MODE` is the mode of the log output. It will default to the sublogger
|
||||
name. Thus `[log.console.macaron]` will default to `MODE = console`.
|
||||
* `COLORIZE` will default to `true` for `file` and `console` as
|
||||
* `COLORIZE` will default to `true` for `console` as
|
||||
described, otherwise it will default to `false`.
|
||||
|
||||
### Non-inherited default values
|
||||
|
@ -274,7 +258,6 @@ Other values:
|
|||
* `MAX_SIZE_SHIFT`: **28**: Maximum size shift of a single file, 28 represents 256Mb.
|
||||
* `DAILY_ROTATE`: **true**: Rotate logs daily.
|
||||
* `MAX_DAYS`: **7**: Delete the log file after n days
|
||||
* NB: `COLORIZE`: will default to `true` if not on windows.
|
||||
* `COMPRESS`: **true**: Compress old log files by default with gzip
|
||||
* `COMPRESSION_LEVEL`: **-1**: Compression level
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ the destination platform from the [downloads page](https://dl.gitea.io/gitea/),
|
|||
the URL and replace the URL within the commands below:
|
||||
|
||||
```sh
|
||||
wget -O gitea https://dl.gitea.io/gitea/1.7.0/gitea-1.7.0-linux-amd64
|
||||
wget -O gitea https://dl.gitea.io/gitea/1.8.3/gitea-1.8.3-linux-amd64
|
||||
chmod +x gitea
|
||||
```
|
||||
|
||||
|
@ -30,7 +30,7 @@ Gitea signs all binaries with a [GPG key](https://pgp.mit.edu/pks/lookup?op=vind
|
|||
|
||||
```sh
|
||||
gpg --keyserver pgp.mit.edu --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
|
||||
gpg --verify gitea-1.7.0-linux-amd64.asc gitea-1.7.0-linux-amd64
|
||||
gpg --verify gitea-1.8.3-linux-amd64.asc gitea-1.8.3-linux-amd64
|
||||
```
|
||||
|
||||
## Test
|
||||
|
@ -143,6 +143,15 @@ bind: address already in use` Gitea needs to be started on another free port. Th
|
|||
is possible using `./gitea web -p $PORT`. It's possible another instance of Gitea
|
||||
is already running.
|
||||
|
||||
### Running Gitea on Raspbian
|
||||
|
||||
As of v1.8, there is a problem with the arm7 version of Gitea and it doesn't run on Raspberry Pi and similar devices.
|
||||
|
||||
It is therefore recommended to switch to the arm6 version which has been tested and shown to work on Raspberry Pi and similar devices.
|
||||
|
||||
<!---
|
||||
please remove after fixing the arm7 bug
|
||||
--->
|
||||
### Git error after updating to a new version of Gitea
|
||||
|
||||
If the binary file name has been changed during the update to a new version of Gitea,
|
||||
|
|
|
@ -27,7 +27,7 @@ necessary. To be able to use these, you must have the `"$GOPATH/bin"` directory
|
|||
on the executable path. If you don't add the go bin directory to the
|
||||
executable path, you will have to manage this yourself.
|
||||
|
||||
**Note 2**: Go version 1.9 or higher is required. However, it is recommended to
|
||||
**Note 2**: Go version 1.11 or higher is required. However, it is recommended to
|
||||
obtain the same version as our continuous integration, see the advice given in
|
||||
<a href='{{< relref "doc/advanced/hacking-on-gitea.en-us.md" >}}'>Hacking on
|
||||
Gitea</a>
|
||||
|
|
|
@ -248,6 +248,7 @@ You can configure some of Gitea's settings via environment variables:
|
|||
* `DISABLE_SSH`: **false**: Disable SSH feature when it's not available.
|
||||
* `HTTP_PORT`: **3000**: HTTP listen port.
|
||||
* `ROOT_URL`: **""**: Overwrite the automatically generated public URL. This is useful if the internal and the external URL don't match (e.g. in Docker).
|
||||
* `LFS_START_SERVER`: **false**: Enables git-lfs support.
|
||||
* `DB_TYPE`: **sqlite3**: The database type in use \[mysql, postgres, mssql, sqlite3\].
|
||||
* `DB_HOST`: **localhost:3306**: Database host address and port.
|
||||
* `DB_NAME`: **gitea**: Database name.
|
||||
|
|
|
@ -25,8 +25,12 @@ All global options can be placed at the command level.
|
|||
|
||||
- `--help`, `-h`: Show help text and exit. Optional.
|
||||
- `--version`, `-v`: Show version and exit. Optional. (example: `Gitea version 1.1.0+218-g7b907ed built with: bindata, sqlite`).
|
||||
- `--custom-path path`, `-C path`: Location of the Gitea custom folder. Optional. (default: $PWD/custom).
|
||||
- `--config path`, `-c path`: Gitea configuration file path. Optional. (default: custom/conf/app.ini).
|
||||
- `--custom-path path`, `-C path`: Location of the Gitea custom folder. Optional. (default: `AppWorkPath`/custom or `$GITEA_CUSTOM`).
|
||||
- `--config path`, `-c path`: Gitea configuration file path. Optional. (default: `custom`/conf/app.ini).
|
||||
- `--work-path path`, `-w path`: Gitea `AppWorkPath`. Optional. (default: LOCATION_OF_GITEA_BINARY or `$GITEA_WORK_DIR`)
|
||||
|
||||
NB: The defaults custom-path, config and work-path can also be
|
||||
changed at build time (if preferred).
|
||||
|
||||
### Commands
|
||||
|
||||
|
@ -58,6 +62,7 @@ Admin operations:
|
|||
- `--password value`: Password. Required.
|
||||
- `--email value`: Email. Required.
|
||||
- `--admin`: If provided, this makes the user an admin. Optional.
|
||||
- `--access-token`: If provided, an access token will be created for the user. Optional. (default: false).
|
||||
- `--must-change-password`: If provided, the created user will be required to choose a newer password after
|
||||
the initial login. Optional. (default: true).
|
||||
- ``--random-password``: If provided, a randomly generated password will be used as the password of
|
||||
|
@ -118,6 +123,94 @@ Admin operations:
|
|||
- `--custom-email-url`: Use a custom Email URL (option for GitHub).
|
||||
- Examples:
|
||||
- `gitea admin auth update-oauth --id 1 --name external-github-updated`
|
||||
- `add-ldap`: Add new LDAP (via Bind DN) authentication source
|
||||
- Options:
|
||||
- `--name value`: Authentication name. Required.
|
||||
- `--not-active`: Deactivate the authentication source.
|
||||
- `--security-protocol value`: Security protocol name. Required.
|
||||
- `--skip-tls-verify`: Disable TLS verification.
|
||||
- `--host value`: The address where the LDAP server can be reached. Required.
|
||||
- `--port value`: The port to use when connecting to the LDAP server. Required.
|
||||
- `--user-search-base value`: The LDAP base at which user accounts will be searched for. Required.
|
||||
- `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required.
|
||||
- `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
|
||||
- `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
|
||||
- `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
|
||||
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
||||
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address. Required.
|
||||
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
||||
- `--bind-dn value`: The DN to bind to the LDAP server with when searching for the user.
|
||||
- `--bind-password value`: The password for the Bind DN, if any.
|
||||
- `--attributes-in-bind`: Fetch attributes in bind DN context.
|
||||
- `--synchronize-users`: Enable user synchronization.
|
||||
- `--page-size value`: Search page size.
|
||||
- Examples:
|
||||
- `gitea admin auth add-ldap --name ldap --security-protocol unencrypted --host mydomain.org --port 389 --user-search-base "ou=Users,dc=mydomain,dc=org" --user-filter "(&(objectClass=posixAccount)(uid=%s))" --email-attribute mail`
|
||||
- `update-ldap`: Update existing LDAP (via Bind DN) authentication source
|
||||
- Options:
|
||||
- `--id value`: ID of authentication source. Required.
|
||||
- `--name value`: Authentication name.
|
||||
- `--not-active`: Deactivate the authentication source.
|
||||
- `--security-protocol value`: Security protocol name.
|
||||
- `--skip-tls-verify`: Disable TLS verification.
|
||||
- `--host value`: The address where the LDAP server can be reached.
|
||||
- `--port value`: The port to use when connecting to the LDAP server.
|
||||
- `--user-search-base value`: The LDAP base at which user accounts will be searched for.
|
||||
- `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate.
|
||||
- `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
|
||||
- `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
|
||||
- `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
|
||||
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
||||
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address.
|
||||
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
||||
- `--bind-dn value`: The DN to bind to the LDAP server with when searching for the user.
|
||||
- `--bind-password value`: The password for the Bind DN, if any.
|
||||
- `--attributes-in-bind`: Fetch attributes in bind DN context.
|
||||
- `--synchronize-users`: Enable user synchronization.
|
||||
- `--page-size value`: Search page size.
|
||||
- Examples:
|
||||
- `gitea admin auth update-ldap --id 1 --name "my ldap auth source"`
|
||||
- `gitea admin auth update-ldap --id 1 --username-attribute uid --firstname-attribute givenName --surname-attribute sn`
|
||||
- `add-ldap-simple`: Add new LDAP (simple auth) authentication source
|
||||
- Options:
|
||||
- `--name value`: Authentication name. Required.
|
||||
- `--not-active`: Deactivate the authentication source.
|
||||
- `--security-protocol value`: Security protocol name. Required.
|
||||
- `--skip-tls-verify`: Disable TLS verification.
|
||||
- `--host value`: The address where the LDAP server can be reached. Required.
|
||||
- `--port value`: The port to use when connecting to the LDAP server. Required.
|
||||
- `--user-search-base value`: The LDAP base at which user accounts will be searched for.
|
||||
- `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required.
|
||||
- `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
|
||||
- `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
|
||||
- `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
|
||||
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
||||
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address. Required.
|
||||
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
||||
- `--user-dn value`: The user’s DN. Required.
|
||||
- Examples:
|
||||
- `gitea admin auth add-ldap-simple --name ldap --security-protocol unencrypted --host mydomain.org --port 389 --user-dn "cn=%s,ou=Users,dc=mydomain,dc=org" --user-filter "(&(objectClass=posixAccount)(cn=%s))" --email-attribute mail`
|
||||
- `update-ldap-simple`: Update existing LDAP (simple auth) authentication source
|
||||
- Options:
|
||||
- `--id value`: ID of authentication source. Required.
|
||||
- `--name value`: Authentication name.
|
||||
- `--not-active`: Deactivate the authentication source.
|
||||
- `--security-protocol value`: Security protocol name.
|
||||
- `--skip-tls-verify`: Disable TLS verification.
|
||||
- `--host value`: The address where the LDAP server can be reached.
|
||||
- `--port value`: The port to use when connecting to the LDAP server.
|
||||
- `--user-search-base value`: The LDAP base at which user accounts will be searched for.
|
||||
- `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate.
|
||||
- `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
|
||||
- `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
|
||||
- `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
|
||||
- `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
|
||||
- `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address.
|
||||
- `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
|
||||
- `--user-dn value`: The user’s DN.
|
||||
- Examples:
|
||||
- `gitea admin auth update-ldap-simple --id 1 --name "my ldap auth source"`
|
||||
- `gitea admin auth update-ldap-simple --id 1 --username-attribute uid --firstname-attribute givenName --surname-attribute sn`
|
||||
|
||||
#### cert
|
||||
|
||||
|
|
54
go.mod
54
go.mod
|
@ -7,11 +7,10 @@ require (
|
|||
github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34
|
||||
github.com/RoaringBitmap/roaring v0.4.7 // indirect
|
||||
github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca
|
||||
github.com/Unknwon/com v0.0.0-20170819223952-7677a1d7c113
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755
|
||||
github.com/Unknwon/i18n v0.0.0-20171114194641-b64d33658966
|
||||
github.com/Unknwon/paginater v0.0.0-20151104151617-7748a72e0141
|
||||
github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470 // indirect
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/blevesearch/bleve v0.0.0-20190214220507-05d86ea8f6e3
|
||||
github.com/blevesearch/blevex v0.0.0-20180227211930-4b158bb555a3 // indirect
|
||||
|
@ -27,10 +26,9 @@ require (
|
|||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20161101193935-9ed569b5d1ac
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190121005146-b04fd42d9952
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect
|
||||
github.com/elazarl/go-bindata-assetfs v0.0.0-20151224045452-57eb5e1fc594 // indirect
|
||||
github.com/emirpasic/gods v1.12.0
|
||||
github.com/etcd-io/bbolt v1.3.2 // indirect
|
||||
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a
|
||||
|
@ -42,25 +40,25 @@ require (
|
|||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 // indirect
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
|
||||
github.com/gliderlabs/ssh v0.2.2
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd // indirect
|
||||
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e // indirect
|
||||
github.com/go-macaron/bindata v0.0.0-20161222093048-85786f57eee3
|
||||
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443
|
||||
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776
|
||||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab
|
||||
github.com/go-macaron/captcha v0.0.0-20190710000913-8dc5911259df
|
||||
github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9
|
||||
github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372
|
||||
github.com/go-macaron/i18n v0.0.0-20160612092837-ef57533c3b0f
|
||||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191
|
||||
github.com/go-macaron/session v0.0.0-20190131233854-0a0a789bf193
|
||||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90
|
||||
github.com/go-redis/redis v6.15.2+incompatible
|
||||
github.com/go-sql-driver/mysql v1.4.0
|
||||
github.com/go-xorm/builder v0.3.3
|
||||
github.com/go-xorm/core v0.6.0
|
||||
github.com/go-xorm/xorm v0.0.0-20190116032649-a6300f2a45e0
|
||||
github.com/go-sql-driver/mysql v1.4.1
|
||||
github.com/go-xorm/core v0.6.0 // indirect
|
||||
github.com/go-xorm/xorm v0.7.3-0.20190620151208-f1b4f8368459
|
||||
github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561
|
||||
github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183
|
||||
github.com/gogo/protobuf v1.2.1 // indirect
|
||||
github.com/google/go-cmp v0.3.0 // indirect
|
||||
github.com/google/go-github/v24 v24.0.1
|
||||
github.com/gorilla/context v1.1.1
|
||||
github.com/issue9/assert v1.3.2 // indirect
|
||||
|
@ -83,25 +81,23 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.7
|
||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect
|
||||
github.com/mattn/go-sqlite3 v1.10.0
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75
|
||||
github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae // indirect
|
||||
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc
|
||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5
|
||||
github.com/oliamb/cutter v0.2.2
|
||||
github.com/philhofer/fwd v1.0.0 // indirect
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e
|
||||
github.com/prometheus/client_golang v0.9.0
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect
|
||||
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 // indirect
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d // indirect
|
||||
github.com/prometheus/client_golang v0.9.3
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
|
||||
github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/sergi/go-diff v1.0.0
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
||||
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff // indirect
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect
|
||||
|
@ -113,28 +109,30 @@ require (
|
|||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 // indirect
|
||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
|
||||
go.etcd.io/bbolt v1.3.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519
|
||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443
|
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b
|
||||
golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
|
||||
golang.org/x/text v0.3.0
|
||||
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45
|
||||
golang.org/x/text v0.3.2
|
||||
golang.org/x/tools v0.0.0-20190620154339-431033348dd0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
|
||||
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect
|
||||
gopkg.in/editorconfig/editorconfig-core-go.v1 v1.2.0
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.31.1
|
||||
gopkg.in/ini.v1 v1.42.0
|
||||
gopkg.in/ldap.v3 v3.0.2
|
||||
gopkg.in/macaron.v1 v1.3.2
|
||||
gopkg.in/redis.v2 v2.3.2 // indirect
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0
|
||||
gopkg.in/src-d/go-git.v4 v4.10.0
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0
|
||||
gopkg.in/stretchr/testify.v1 v1.2.2 // indirect
|
||||
gopkg.in/testfixtures.v2 v2.5.0
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
mvdan.cc/xurls/v2 v2.0.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a
|
||||
xorm.io/builder v0.3.5
|
||||
xorm.io/core v0.6.3
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f => github.com/denisenkom/go-mssqldb v0.0.0-20161128230840-e32ca5036449
|
||||
github.com/go-sql-driver/mysql v1.4.0 => github.com/go-sql-driver/mysql v0.0.0-20181218123637-c45f530f8e7f
|
||||
)
|
||||
replace github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20180315180555-6a30f4e59a44
|
||||
|
|
173
go.sum
173
go.sum
|
@ -1,26 +1,35 @@
|
|||
cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 h1:UsHpWO0Elp6NaWVARdZHjiYwkhrspHVEGsyIKPb9OI8=
|
||||
github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA=
|
||||
github.com/RoaringBitmap/roaring v0.4.7 h1:eGUudvFzvF7Kxh7JjYvXfI1f7l22/2duFby7r5+d4oc=
|
||||
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
|
||||
github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca h1:xU8R31tsvj6TesCBog973+UgI3TXjh/LqN5clki6hcc=
|
||||
github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca/go.mod h1:IRSre9/SEhVuy972TVuJLyaPTS73+8Owhe0Y0l9NXHc=
|
||||
github.com/Unknwon/com v0.0.0-20170819223952-7677a1d7c113 h1:YwXm6KwmrA5R5yJRhcnpqRUHmBXSKciHuWtK9zP5qKQ=
|
||||
github.com/Unknwon/com v0.0.0-20170819223952-7677a1d7c113/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 h1:1B7wb36fHLSwZfHg6ngZhhtIEHQjiC5H4p7qQGBEffg=
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68=
|
||||
github.com/Unknwon/i18n v0.0.0-20171114194641-b64d33658966 h1:Mp8GNJ/tdTZIEdLdZfykEJaL3mTyEYrSzYNcdoQKpJk=
|
||||
github.com/Unknwon/i18n v0.0.0-20171114194641-b64d33658966/go.mod h1:SFtfq0zFPsENI7DpE87QM2hcYu5QQ0fRdCgP+P1Hrqo=
|
||||
github.com/Unknwon/paginater v0.0.0-20151104151617-7748a72e0141 h1:SSvHGK7iMpeypcHjI8UzNMz7zW/K8/dcgqk/82lCYP0=
|
||||
github.com/Unknwon/paginater v0.0.0-20151104151617-7748a72e0141/go.mod h1:fw0McLecf/G5NFwddCRmDckU6yovtk1YsgWIoepMbYo=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470 h1:4jHLmof+Hba81591gfH5xYA8QXzuvgksxwPNrmjR2BA=
|
||||
github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470/go.mod h1:3I+3V7B6gTBYfdpYgIG2ymALS9H+5VDKUl3lHH7ToM4=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/blevesearch/bleve v0.0.0-20190214220507-05d86ea8f6e3 h1:vinCy/rcjbtxWnMiw11CbMKcuyNi+y4L4MbZUpk7m4M=
|
||||
|
@ -35,6 +44,7 @@ github.com/boombuler/barcode v0.0.0-20161226211916-fe0f26ff6d26 h1:NGpwhs9FOwddM
|
|||
github.com/boombuler/barcode v0.0.0-20161226211916-fe0f26ff6d26/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a h1:k5TuEkqEYCRs8+66WdOkswWOj+L/YbP5ruainvn94wg=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f h1:REH9VH5ubNR0skLaOxK7TRJeRbE2dDfvaouQo8FsRcA=
|
||||
github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f/go.mod h1:6QaC0vFoKWYDth94dHFNgRT2YkT5FHdQp/Yx15aAAi0=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
|
@ -56,15 +66,13 @@ github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20161128230840-e32ca5036449 h1:JpA+YMG4JLW8nzLmU05mTiuB0O17xHGxpWolEZ0zDuA=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20161128230840-e32ca5036449/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20161101193935-9ed569b5d1ac h1:xrQJVwQCGqDvOO7/0+RyIq5J2M3Q4ZF7Ug/BMQtML1E=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20161101193935-9ed569b5d1ac/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20180315180555-6a30f4e59a44 h1:DWxZh2sImfCFn/79OUBhzFkPTKnsdDzXH/JTxpw5n6w=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20180315180555-6a30f4e59a44/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/go-bindata-assetfs v0.0.0-20151224045452-57eb5e1fc594 h1:McZ/pt/pP/XAbLMDQGzm/iQUwW6OXmKVbFtmH9klWmc=
|
||||
github.com/elazarl/go-bindata-assetfs v0.0.0-20151224045452-57eb5e1fc594/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0=
|
||||
|
@ -91,20 +99,24 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr
|
|||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
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/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd h1:r04MMPyLHj/QwZuMJ5+7tJcBr1AQjpiAK/rZWRrQT7o=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e h1:SiEs4J3BKVIeaWrH3tKaz3QLZhJ68iJ/A4xrzIoE5+Y=
|
||||
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-macaron/bindata v0.0.0-20161222093048-85786f57eee3 h1:n0H90987ZwasTc9r/RnuaU1G5ePfzLG6Bkc1cY3KqnY=
|
||||
github.com/go-macaron/bindata v0.0.0-20161222093048-85786f57eee3/go.mod h1:NkmXhFuAlCOqgV5EWhU1DKLrgztCEIVXGmr2DZv/+sk=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443 h1:i801KPR7j76uRMLLlGVyb0hiYbgX1FM5+ur81TJWzIw=
|
||||
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443/go.mod h1:u+H6rwW+HQwUL+w5uaEJSpIlVZDye1o9MB4Su0JfRfM=
|
||||
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776 h1:UYIHS1r0WotqB5cIa0PAiV0m6GzD9rDBcn4alp5JgCw=
|
||||
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776/go.mod h1:hHAsZm/oBZVcY+S7qdQL6Vbg5VrXF6RuKGuqsszt3Ok=
|
||||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab h1:4VFhsA3GE5Wwq1Ymr8KWCmrOWi1wRLEgdj48LPfQjxI=
|
||||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab/go.mod h1:j9TJ+0nwUOWBvNnm0bheHIPFf3cC62EQo7n7O6PbjZA=
|
||||
github.com/go-macaron/captcha v0.0.0-20190710000913-8dc5911259df h1:MdgvtI3Y1u/DHNj7xUGOqAv+KGoTikjy8xQtCm12L78=
|
||||
github.com/go-macaron/captcha v0.0.0-20190710000913-8dc5911259df/go.mod h1:j9TJ+0nwUOWBvNnm0bheHIPFf3cC62EQo7n7O6PbjZA=
|
||||
github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9 h1:A0QGzY6UHHEil0I2e7C21JenNNG0mmrj5d9SFWTlgr8=
|
||||
github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9/go.mod h1:utmMRnVIrXPSfA9MFcpIYKEpKawjKxf62vv62k4707E=
|
||||
github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372 h1:acrx8CnDmlKl+BPoOOLEK9Ko+SrWFB5pxRuGkKj4iqo=
|
||||
github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372/go.mod h1:oZGMxI7MBnicI0jJqJvH4qQzyrWKhtiKxLSJKHC+ydc=
|
||||
github.com/go-macaron/i18n v0.0.0-20160612092837-ef57533c3b0f h1:wDKrZFc9pYJlqFOf7EzGbFMrSFFtyHt3plr2uTdo8Rg=
|
||||
|
@ -117,29 +129,31 @@ github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90 h1:3wYKrRg9IjUM
|
|||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90/go.mod h1:Ut/NmkIMGVYlEdJBzEZgWVWG5ZpYS9BLmUgXfAgi+qM=
|
||||
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
|
||||
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v0.0.0-20181218123637-c45f530f8e7f h1:fbIzwEaXt5b2bl9mm+PIufKTSGKk6ZuwSSTQ7iZj7Lo=
|
||||
github.com/go-sql-driver/mysql v0.0.0-20181218123637-c45f530f8e7f/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-xorm/builder v0.3.2/go.mod h1:v8mE3MFBgtL+RGFNfUnAMUqqfk/Y4W5KuwCFQIEpQLk=
|
||||
github.com/go-xorm/builder v0.3.3 h1:v8grgrwOGv/iHXIEhIvOwHZIPLrpxRKSX8yWSMLFn/4=
|
||||
github.com/go-xorm/builder v0.3.3/go.mod h1:v8mE3MFBgtL+RGFNfUnAMUqqfk/Y4W5KuwCFQIEpQLk=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-xorm/core v0.6.0 h1:tp6hX+ku4OD9khFZS8VGBDRY3kfVCtelPfmkgCyHxL0=
|
||||
github.com/go-xorm/core v0.6.0/go.mod h1:d8FJ9Br8OGyQl12MCclmYBuBqqxsyeedpXciV5Myih8=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
||||
github.com/go-xorm/xorm v0.0.0-20190116032649-a6300f2a45e0 h1:GBnJjWjp2SGXBZsyZfYksyp7QocvQwf9vZQ0NRN2FXM=
|
||||
github.com/go-xorm/xorm v0.0.0-20190116032649-a6300f2a45e0/go.mod h1:EHS1htMQFptzMaIHKyzqpHGw6C9Rtug75nsq6DA9unI=
|
||||
github.com/go-xorm/xorm v0.7.3-0.20190620151208-f1b4f8368459 h1:JGEuhH169J7Wtm1hN/HFOGENsAq+6FDHfuhGEZj/1e4=
|
||||
github.com/go-xorm/xorm v0.7.3-0.20190620151208-f1b4f8368459/go.mod h1:UK1YDlWscDspd23xW9HC24749jhvwO6riZ/HUt3gbHQ=
|
||||
github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561 h1:deE7ritpK04PgtpyVOS2TYcQEld9qLCD5b5EbVNOuLA=
|
||||
github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:YgYOrVn3Nj9Tq0EvjmFbphRytDj7JNRoWSStJZWDJTQ=
|
||||
github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183 h1:EBTlva3AOSb80G3JSwY6ZMdILEZJ1JKuewrbqrNjWuE=
|
||||
github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183/go.mod h1:pX+V62FFmklia2fhP3P4YSY6iJdPO5jIDKFQ5fEd5QE=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
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/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/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/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-github/v24 v24.0.1 h1:KCt1LjMJEey1qvPXxa9SjaWxwTsCWSq6p2Ju57UR4Q4=
|
||||
|
@ -148,6 +162,8 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu
|
|||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
|
||||
|
@ -166,34 +182,37 @@ github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c h1:A/PDn117UYld5m
|
|||
github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c/go.mod h1:5mTb/PQNkqmq2x3IxlQZE0aSnTksJg7fg/oWmJ5SKXQ=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||
github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY=
|
||||
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90=
|
||||
github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
|
||||
github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d h1:ig/iUfDDg06RVW8OMby+GrmW6K2nPO3AFHlEIdvJSd4=
|
||||
github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657 h1:vE7J1m7cCpiRVEIr1B5ccDxRpbPsWT5JU3if2Di5nE4=
|
||||
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6 h1:9mszGwKDxHEY2cy+9XxCQKWIfkGPSAEFrcN8ghzyAKg=
|
||||
github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v0.0.0-20161025140425-8df558b6cb6f h1:tCnZKEmDovgV4jmsclh6CuKk9AMzTzyVWfejgkgccVg=
|
||||
github.com/klauspost/compress v0.0.0-20161025140425-8df558b6cb6f/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v0.0.0-20160302075316-09cded8978dc h1:WW8B7p7QBnFlqRVv/k6ro/S8Z7tCnYjJHcQNScx9YVs=
|
||||
github.com/klauspost/cpuid v0.0.0-20160302075316-09cded8978dc/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6 h1:KAZ1BW2TCmT6PRihDPpocIy1QTtsAsrx6TneU/4+CMg=
|
||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
@ -220,7 +239,6 @@ github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc
|
|||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZhg15yI2IwC9BtucBiRwSDm9VL8g=
|
||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
|
@ -229,16 +247,20 @@ github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 h1:Pijfgr7ZuvX
|
|||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
|
||||
github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a h1:d18LCO3ctH2kugUqt0pEyKKP8L+IYrocaPqGFilhTKk=
|
||||
github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA=
|
||||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc h1:z1PgdCCmYYVL0BoJTUgmAq1p7ca8fzYIPsNyfsN3xAU=
|
||||
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc/go.mod h1:np1wUFZ6tyoke22qDJZY40URn9Ae51gX7ljIWXN5TJs=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
|
||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oliamb/cutter v0.2.2 h1:Lfwkya0HHNU1YLnGv2hTkzHfasrSMkgv4Dn+5rmlk3k=
|
||||
github.com/oliamb/cutter v0.2.2/go.mod h1:4BenG2/4GuRBDbVm/OPahDVqbrOemzpPiG5mi1iryBU=
|
||||
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=
|
||||
|
@ -255,14 +277,21 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e h1:ApqncJ84HYN8x8x5WV1T1YWDuPRF/0aXZhr91LnRMCQ=
|
||||
github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e/go.mod h1:Zad1CMQfSQZI5KLpahDiSUX4tMMREnXw98IvL1nhgMk=
|
||||
github.com/prometheus/client_golang v0.9.0 h1:tXuTFVHC03mW0D+Ua1Q2d1EAVqLTuggX50V0VLICCzY=
|
||||
github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 h1:Cto4X6SVMWRPBkJ/3YHn1iDGDGc/Z+sW+AEMKHMVvN4=
|
||||
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff h1:g9ZlAHmkc/h5So+OjNCkZWh+FjuKEOOOoyRkqlGA8+c=
|
||||
|
@ -275,20 +304,30 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
|||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc h1:3wIrJvFb3Pf6B/2mDBnN1G5IfUVev4X5apadQlWOczE=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d h1:qQWKKOvHN7Q9c6GdmUteCef2F9ubxMpxY1IKwpIKz68=
|
||||
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff h1:86HlEv0yBCry9syNuylzqznKXDK11p6D0DT596yNMys=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 h1:JNEGSiWg6D3lcBCMCBqN3ELniXujt+0QNHLhNnO0w3s=
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2/go.mod h1:mjqs7N0Q6m5HpR7QfXVBZXZWSqTjQLeTujjA/xUp2uw=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
|
@ -305,8 +344,8 @@ github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
|||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 h1:E8u341JM/N8LCnPXBV6ZFD1RKo/j+qHl1XOqSV+GstA=
|
||||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
|
||||
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53 h1:HsIQ6yAjfjQ3IxPGrTusxp6Qxn92gNVq2x5CbvQvx3w=
|
||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53/go.mod h1:f6elajwZV+xceiaqgRL090YzLEDGSbqr3poGL3ZgXYo=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
|
@ -314,30 +353,63 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK
|
|||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190122013713-64072686203f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 h1:IcSOAf4PyMp3U3XbIEj1/xJ2BjNN2jWv7JoyOsMxXUU=
|
||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b h1:lkjdUzSyJ5P1+eal9fxXX9Xg2BTfswsonKUse48C0uE=
|
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759 h1:TMrx+Qdx7uJAeUbv15N72h5Hmyb5+VDjEiMufAEAM04=
|
||||
golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
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/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45 h1:Dl2hc890lrizvUppGbRWhnIh2f8jOTCQpY5IKWRS0oM=
|
||||
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190620154339-431033348dd0 h1:qUGDNmGEM+ZBtwF9vuzEv+9nQQPL+l/oNBZ+DCDTAyo=
|
||||
golang.org/x/tools v0.0.0-20190620154339-431033348dd0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8KqxhFwKci30UxHy3HXPTyQ=
|
||||
|
@ -353,21 +425,20 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
|||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/ini.v1 v1.31.1 h1:8EY/6KDwKM9Qg4vu1+01ZpsxClC/XV71R+nZ/TL7D4M=
|
||||
gopkg.in/ini.v1 v1.31.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w=
|
||||
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
|
||||
gopkg.in/macaron.v1 v1.3.2 h1:AvWIaPmwBUA87/OWzePkoxeaw6YJWDfBt1pDFPBnLf8=
|
||||
gopkg.in/macaron.v1 v1.3.2/go.mod h1:PrsiawTWAGZs6wFbT5hlr7SQ2Ns9h7cUVtcUu4lQOVo=
|
||||
gopkg.in/redis.v2 v2.3.2 h1:GPVIIB/JnL1wvfULefy3qXmPu1nfNu2d0yA09FHgwfs=
|
||||
gopkg.in/redis.v2 v2.3.2/go.mod h1:4wl9PJ/CqzeHk3LVq1hNLHH8krm3+AXEgut4jVc++LU=
|
||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.10.0 h1:NWjTJTQnk8UpIGlssuefyDZ6JruEjo5s88vm88uASbw=
|
||||
gopkg.in/src-d/go-git.v4 v4.10.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0 h1:CKgvBCJCcdfNnyXPYI4Cp8PaDDAmAPEN0CtfEdEAbd8=
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0/go.mod h1:zjlNnzc1Wjn43v3Mtii7RVxiReNP0fIu9npcXKzuNp4=
|
||||
gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M=
|
||||
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
|
||||
gopkg.in/testfixtures.v2 v2.5.0 h1:N08B7l2GzFQenyYbzqthDnKAA+cmb17iAZhhFxr7JHw=
|
||||
|
@ -378,7 +449,13 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
|||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
mvdan.cc/xurls/v2 v2.0.0 h1:r1zSOSNS/kqtpmATyMMMvaZ4/djsesbYz5kr0+qMRWc=
|
||||
mvdan.cc/xurls/v2 v2.0.0/go.mod h1:2/webFPYOXN9jp/lzuj0zuAVlF+9g4KPFJANH1oJhRU=
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a h1:8q33ShxKXRwQ7JVd1ZnhIU3hZhwwn0Le+4fTeAackuM=
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
|
||||
xorm.io/builder v0.3.5 h1:EilU39fvWDxjb1cDaELpYhsF+zziRBhew8xk4pngO+A=
|
||||
xorm.io/builder v0.3.5/go.mod h1:ZFbByS/KxZI1FKRjL05PyJ4YrK2bcxlUaAxdum5aTR8=
|
||||
xorm.io/core v0.6.3 h1:n1NhVZt1s2oLw1BZfX2ocIJsHyso259uPgg63BGr37M=
|
||||
xorm.io/core v0.6.3/go.mod h1:8kz/C6arVW/O9vk3PgCiMJO2hIAm1UcuOL3dSPyZ2qo=
|
||||
|
|
86
integrations/api_admin_org_test.go
Normal file
86
integrations/api_admin_org_test.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAPIAdminOrgCreate(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
session := loginUser(t, "user1")
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
|
||||
var org = api.CreateOrgOption{
|
||||
UserName: "user2_org",
|
||||
FullName: "User2's organization",
|
||||
Description: "This organization created by admin for user2",
|
||||
Website: "https://try.gitea.io",
|
||||
Location: "Shanghai",
|
||||
Visibility: "private",
|
||||
}
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/admin/users/user2/orgs?token="+token, &org)
|
||||
resp := session.MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
var apiOrg api.Organization
|
||||
DecodeJSON(t, resp, &apiOrg)
|
||||
|
||||
assert.Equal(t, org.UserName, apiOrg.UserName)
|
||||
assert.Equal(t, org.FullName, apiOrg.FullName)
|
||||
assert.Equal(t, org.Description, apiOrg.Description)
|
||||
assert.Equal(t, org.Website, apiOrg.Website)
|
||||
assert.Equal(t, org.Location, apiOrg.Location)
|
||||
assert.Equal(t, org.Visibility, apiOrg.Visibility)
|
||||
|
||||
models.AssertExistsAndLoadBean(t, &models.User{
|
||||
Name: org.UserName,
|
||||
LowerName: strings.ToLower(org.UserName),
|
||||
FullName: org.FullName,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIAdminOrgCreateBadVisibility(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
session := loginUser(t, "user1")
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
|
||||
var org = api.CreateOrgOption{
|
||||
UserName: "user2_org",
|
||||
FullName: "User2's organization",
|
||||
Description: "This organization created by admin for user2",
|
||||
Website: "https://try.gitea.io",
|
||||
Location: "Shanghai",
|
||||
Visibility: "notvalid",
|
||||
}
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/admin/users/user2/orgs?token="+token, &org)
|
||||
session.MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIAdminOrgCreateNotAdmin(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
nonAdminUsername := "user2"
|
||||
session := loginUser(t, nonAdminUsername)
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
var org = api.CreateOrgOption{
|
||||
UserName: "user2_org",
|
||||
FullName: "User2's organization",
|
||||
Description: "This organization created by admin for user2",
|
||||
Website: "https://try.gitea.io",
|
||||
Location: "Shanghai",
|
||||
Visibility: "public",
|
||||
}
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/admin/users/user2/orgs?token="+token, &org)
|
||||
session.MakeRequest(t, req, http.StatusForbidden)
|
||||
}
|
|
@ -5,11 +5,14 @@
|
|||
package integrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -63,6 +66,44 @@ func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*tes
|
|||
}
|
||||
}
|
||||
|
||||
func doAPIAddCollaborator(ctx APITestContext, username string, mode models.AccessMode) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
permission := "read"
|
||||
|
||||
if mode == models.AccessModeAdmin {
|
||||
permission = "admin"
|
||||
} else if mode > models.AccessModeRead {
|
||||
permission = "write"
|
||||
}
|
||||
addCollaboratorOption := &api.AddCollaboratorOption{
|
||||
Permission: &permission,
|
||||
}
|
||||
req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/collaborators/%s?token=%s", ctx.Username, ctx.Reponame, username, ctx.Token), addCollaboratorOption)
|
||||
if ctx.ExpectedCode != 0 {
|
||||
ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
|
||||
return
|
||||
}
|
||||
ctx.Session.MakeRequest(t, req, http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
func doAPIForkRepository(ctx APITestContext, username string, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
createForkOption := &api.CreateForkOption{}
|
||||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks?token=%s", username, ctx.Reponame, ctx.Token), createForkOption)
|
||||
if ctx.ExpectedCode != 0 {
|
||||
ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
|
||||
return
|
||||
}
|
||||
resp := ctx.Session.MakeRequest(t, req, http.StatusAccepted)
|
||||
var repository api.Repository
|
||||
DecodeJSON(t, resp, &repository)
|
||||
if len(callback) > 0 {
|
||||
callback[0](t, repository)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doAPIGetRepository(ctx APITestContext, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
|
||||
|
@ -150,3 +191,42 @@ func doAPICreateDeployKey(ctx APITestContext, keyname, keyFile string, readOnly
|
|||
ctx.Session.MakeRequest(t, req, http.StatusCreated)
|
||||
}
|
||||
}
|
||||
|
||||
func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBranch string) func(*testing.T) (api.PullRequest, error) {
|
||||
return func(t *testing.T) (api.PullRequest, error) {
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s",
|
||||
owner, repo, ctx.Token)
|
||||
req := NewRequestWithJSON(t, http.MethodPost, urlStr, &api.CreatePullRequestOption{
|
||||
Head: headBranch,
|
||||
Base: baseBranch,
|
||||
Title: fmt.Sprintf("create a pr from %s to %s", headBranch, baseBranch),
|
||||
})
|
||||
|
||||
expected := 201
|
||||
if ctx.ExpectedCode != 0 {
|
||||
expected = ctx.ExpectedCode
|
||||
}
|
||||
resp := ctx.Session.MakeRequest(t, req, expected)
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
pr := api.PullRequest{}
|
||||
err := decoder.Decode(&pr)
|
||||
return pr, err
|
||||
}
|
||||
}
|
||||
|
||||
func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s",
|
||||
owner, repo, index, ctx.Token)
|
||||
req := NewRequestWithJSON(t, http.MethodPost, urlStr, &auth.MergePullRequestForm{
|
||||
MergeMessageField: "doAPIMergePullRequest Merge",
|
||||
Do: string(models.MergeStyleMerge),
|
||||
})
|
||||
|
||||
if ctx.ExpectedCode != 0 {
|
||||
ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
|
||||
return
|
||||
}
|
||||
ctx.Session.MakeRequest(t, req, 200)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAPIOrg(t *testing.T) {
|
||||
func TestAPIOrgCreate(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
session := loginUser(t, "user1")
|
||||
|
||||
|
@ -28,6 +28,7 @@ func TestAPIOrg(t *testing.T) {
|
|||
Description: "This organization created by user1",
|
||||
Website: "https://try.gitea.io",
|
||||
Location: "Shanghai",
|
||||
Visibility: "limited",
|
||||
}
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/orgs?token="+token, &org)
|
||||
resp := session.MakeRequest(t, req, http.StatusCreated)
|
||||
|
@ -40,6 +41,7 @@ func TestAPIOrg(t *testing.T) {
|
|||
assert.Equal(t, org.Description, apiOrg.Description)
|
||||
assert.Equal(t, org.Website, apiOrg.Website)
|
||||
assert.Equal(t, org.Location, apiOrg.Location)
|
||||
assert.Equal(t, org.Visibility, apiOrg.Visibility)
|
||||
|
||||
models.AssertExistsAndLoadBean(t, &models.User{
|
||||
Name: org.UserName,
|
||||
|
@ -72,6 +74,50 @@ func TestAPIOrg(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAPIOrgEdit(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
session := loginUser(t, "user1")
|
||||
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
var org = api.EditOrgOption{
|
||||
FullName: "User3 organization new full name",
|
||||
Description: "A new description",
|
||||
Website: "https://try.gitea.io/new",
|
||||
Location: "Beijing",
|
||||
Visibility: "private",
|
||||
}
|
||||
req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/user3?token="+token, &org)
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
var apiOrg api.Organization
|
||||
DecodeJSON(t, resp, &apiOrg)
|
||||
|
||||
assert.Equal(t, "user3", apiOrg.UserName)
|
||||
assert.Equal(t, org.FullName, apiOrg.FullName)
|
||||
assert.Equal(t, org.Description, apiOrg.Description)
|
||||
assert.Equal(t, org.Website, apiOrg.Website)
|
||||
assert.Equal(t, org.Location, apiOrg.Location)
|
||||
assert.Equal(t, org.Visibility, apiOrg.Visibility)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIOrgEditBadVisibility(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
session := loginUser(t, "user1")
|
||||
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
var org = api.EditOrgOption{
|
||||
FullName: "User3 organization new full name",
|
||||
Description: "A new description",
|
||||
Website: "https://try.gitea.io/new",
|
||||
Location: "Beijing",
|
||||
Visibility: "badvisibility",
|
||||
}
|
||||
req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/user3?token="+token, &org)
|
||||
session.MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIOrgDeny(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
setting.Service.RequireSignInView = true
|
||||
|
|
225
integrations/api_repo_edit_test.go
Normal file
225
integrations/api_repo_edit_test.go
Normal file
|
@ -0,0 +1,225 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// getRepoEditOptionFromRepo gets the options for an existing repo exactly as is
|
||||
func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption {
|
||||
name := repo.Name
|
||||
description := repo.Description
|
||||
website := repo.Website
|
||||
private := repo.IsPrivate
|
||||
hasIssues := false
|
||||
if _, err := repo.GetUnit(models.UnitTypeIssues); err == nil {
|
||||
hasIssues = true
|
||||
}
|
||||
hasWiki := false
|
||||
if _, err := repo.GetUnit(models.UnitTypeWiki); err == nil {
|
||||
hasWiki = true
|
||||
}
|
||||
defaultBranch := repo.DefaultBranch
|
||||
hasPullRequests := false
|
||||
ignoreWhitespaceConflicts := false
|
||||
allowMerge := false
|
||||
allowRebase := false
|
||||
allowRebaseMerge := false
|
||||
allowSquash := false
|
||||
if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil {
|
||||
config := unit.PullRequestsConfig()
|
||||
hasPullRequests = true
|
||||
ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts
|
||||
allowMerge = config.AllowMerge
|
||||
allowRebase = config.AllowRebase
|
||||
allowRebaseMerge = config.AllowRebaseMerge
|
||||
allowSquash = config.AllowSquash
|
||||
}
|
||||
archived := repo.IsArchived
|
||||
return &api.EditRepoOption{
|
||||
Name: &name,
|
||||
Description: &description,
|
||||
Website: &website,
|
||||
Private: &private,
|
||||
HasIssues: &hasIssues,
|
||||
HasWiki: &hasWiki,
|
||||
DefaultBranch: &defaultBranch,
|
||||
HasPullRequests: &hasPullRequests,
|
||||
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
||||
AllowMerge: &allowMerge,
|
||||
AllowRebase: &allowRebase,
|
||||
AllowRebaseMerge: &allowRebaseMerge,
|
||||
AllowSquash: &allowSquash,
|
||||
Archived: &archived,
|
||||
}
|
||||
}
|
||||
|
||||
// getNewRepoEditOption Gets the options to change everything about an existing repo by adding to strings or changing
|
||||
// the boolean
|
||||
func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption {
|
||||
// Gives a new property to everything
|
||||
name := *opts.Name + "renamed"
|
||||
description := "new description"
|
||||
website := "http://wwww.newwebsite.com"
|
||||
private := !*opts.Private
|
||||
hasIssues := !*opts.HasIssues
|
||||
hasWiki := !*opts.HasWiki
|
||||
defaultBranch := "master"
|
||||
hasPullRequests := !*opts.HasPullRequests
|
||||
ignoreWhitespaceConflicts := !*opts.IgnoreWhitespaceConflicts
|
||||
allowMerge := !*opts.AllowMerge
|
||||
allowRebase := !*opts.AllowRebase
|
||||
allowRebaseMerge := !*opts.AllowRebaseMerge
|
||||
allowSquash := !*opts.AllowSquash
|
||||
archived := !*opts.Archived
|
||||
|
||||
return &api.EditRepoOption{
|
||||
Name: &name,
|
||||
Description: &description,
|
||||
Website: &website,
|
||||
Private: &private,
|
||||
DefaultBranch: &defaultBranch,
|
||||
HasIssues: &hasIssues,
|
||||
HasWiki: &hasWiki,
|
||||
HasPullRequests: &hasPullRequests,
|
||||
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
||||
AllowMerge: &allowMerge,
|
||||
AllowRebase: &allowRebase,
|
||||
AllowRebaseMerge: &allowRebaseMerge,
|
||||
AllowSquash: &allowSquash,
|
||||
Archived: &archived,
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIRepoEdit(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
|
||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo
|
||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
|
||||
|
||||
// Get user2's token
|
||||
session := loginUser(t, user2.Name)
|
||||
token2 := getTokenForLoggedInUser(t, session)
|
||||
session = emptyTestSession(t)
|
||||
// Get user4's token
|
||||
session = loginUser(t, user4.Name)
|
||||
token4 := getTokenForLoggedInUser(t, session)
|
||||
session = emptyTestSession(t)
|
||||
|
||||
// Test editing a repo1 which user2 owns, changing name and many properties
|
||||
origRepoEditOption := getRepoEditOptionFromRepo(repo1)
|
||||
repoEditOption := getNewRepoEditOption(origRepoEditOption)
|
||||
url := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo1.Name, token2)
|
||||
req := NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
var repo api.Repository
|
||||
DecodeJSON(t, resp, &repo)
|
||||
assert.NotNil(t, repo)
|
||||
// check response
|
||||
assert.Equal(t, *repoEditOption.Name, repo.Name)
|
||||
assert.Equal(t, *repoEditOption.Description, repo.Description)
|
||||
assert.Equal(t, *repoEditOption.Website, repo.Website)
|
||||
assert.Equal(t, *repoEditOption.Archived, repo.Archived)
|
||||
// check repo1 from database
|
||||
repo1edited := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||
repo1editedOption := getRepoEditOptionFromRepo(repo1edited)
|
||||
assert.Equal(t, *repoEditOption.Name, *repo1editedOption.Name)
|
||||
assert.Equal(t, *repoEditOption.Description, *repo1editedOption.Description)
|
||||
assert.Equal(t, *repoEditOption.Website, *repo1editedOption.Website)
|
||||
assert.Equal(t, *repoEditOption.Archived, *repo1editedOption.Archived)
|
||||
assert.Equal(t, *repoEditOption.Private, *repo1editedOption.Private)
|
||||
assert.Equal(t, *repoEditOption.HasWiki, *repo1editedOption.HasWiki)
|
||||
// reset repo in db
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, *repoEditOption.Name, token2)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Test editing a non-existing repo
|
||||
name := "repodoesnotexist"
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, name, token2)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{Name: &name})
|
||||
resp = session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Test editing repo16 by user4 who does not have write access
|
||||
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
||||
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token4)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Tests a repo with no token given so will fail
|
||||
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
||||
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
resp = session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Test using access token for a private repo that the user of the token owns
|
||||
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
||||
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token2)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
// reset repo in db
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, *repoEditOption.Name, token2)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Test making a repo public that is private
|
||||
repo16 = models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository)
|
||||
assert.True(t, repo16.IsPrivate)
|
||||
private := false
|
||||
repoEditOption = &api.EditRepoOption{
|
||||
Private: &private,
|
||||
}
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token2)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
repo16 = models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository)
|
||||
assert.False(t, repo16.IsPrivate)
|
||||
// Make it private again
|
||||
private = true
|
||||
repoEditOption.Private = &private
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Test using org repo "user3/repo3" where user2 is a collaborator
|
||||
origRepoEditOption = getRepoEditOptionFromRepo(repo3)
|
||||
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user3.Name, repo3.Name, token2)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
// reset repo in db
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user3.Name, *repoEditOption.Name, token2)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Test using org repo "user3/repo3" with no user token
|
||||
origRepoEditOption = getRepoEditOptionFromRepo(repo3)
|
||||
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s", user3.Name, repo3.Name)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Test using repo "user2/repo1" where user4 is a NOT collaborator
|
||||
origRepoEditOption = getRepoEditOptionFromRepo(repo1)
|
||||
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo1.Name, token4)
|
||||
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||
session.MakeRequest(t, req, http.StatusForbidden)
|
||||
})
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getExpectedFileContentResponseForFileContents(branch string) *api.FileContentResponse {
|
||||
treePath := "README.md"
|
||||
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
|
||||
return &api.FileContentResponse{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
Size: 30,
|
||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/" + branch + "/" + treePath,
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
|
||||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/" + branch + "/" + treePath,
|
||||
Type: "blob",
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/" + branch + "/" + treePath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGetFileContents(t *testing.T) {
|
||||
onGiteaRun(t, testAPIGetFileContents)
|
||||
}
|
||||
|
||||
func testAPIGetFileContents(t *testing.T, u *url.URL) {
|
||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
|
||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo
|
||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
|
||||
treePath := "README.md"
|
||||
|
||||
// Get user2's token
|
||||
session := loginUser(t, user2.Name)
|
||||
token2 := getTokenForLoggedInUser(t, session)
|
||||
session = emptyTestSession(t)
|
||||
// Get user4's token
|
||||
session = loginUser(t, user4.Name)
|
||||
token4 := getTokenForLoggedInUser(t, session)
|
||||
session = emptyTestSession(t)
|
||||
|
||||
// Make a second master branch in repo1
|
||||
repo1.CreateNewBranch(user2, repo1.DefaultBranch, "master2")
|
||||
|
||||
// ref is default branch
|
||||
branch := repo1.DefaultBranch
|
||||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, branch)
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
var fileContentResponse api.FileContentResponse
|
||||
DecodeJSON(t, resp, &fileContentResponse)
|
||||
assert.NotNil(t, fileContentResponse)
|
||||
expectedFileContentResponse := getExpectedFileContentResponseForFileContents(branch)
|
||||
assert.EqualValues(t, *expectedFileContentResponse, fileContentResponse)
|
||||
|
||||
// No ref
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &fileContentResponse)
|
||||
assert.NotNil(t, fileContentResponse)
|
||||
expectedFileContentResponse = getExpectedFileContentResponseForFileContents(repo1.DefaultBranch)
|
||||
assert.EqualValues(t, *expectedFileContentResponse, fileContentResponse)
|
||||
|
||||
// ref is master2
|
||||
branch = "master2"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, branch)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &fileContentResponse)
|
||||
assert.NotNil(t, fileContentResponse)
|
||||
expectedFileContentResponse = getExpectedFileContentResponseForFileContents("master2")
|
||||
assert.EqualValues(t, *expectedFileContentResponse, fileContentResponse)
|
||||
|
||||
// Test file contents a file with the wrong branch
|
||||
branch = "badbranch"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, branch)
|
||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
|
||||
expectedAPIError := context.APIError{
|
||||
Message: "object does not exist [id: " + branch + ", rel_path: ]",
|
||||
URL: base.DocURL,
|
||||
}
|
||||
var apiError context.APIError
|
||||
DecodeJSON(t, resp, &apiError)
|
||||
assert.Equal(t, expectedAPIError, apiError)
|
||||
|
||||
// Test accessing private branch with user token that does not have access - should fail
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4)
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Test access private branch of owner of token
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/readme.md?token=%s", user2.Name, repo16.Name, token2)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Test access of org user3 private repo file by owner user2
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
|
@ -13,7 +13,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -29,7 +28,7 @@ func getCreateFileOptions() api.CreateFileOptions {
|
|||
FileOptions: api.FileOptions{
|
||||
BranchName: "master",
|
||||
NewBranchName: "master",
|
||||
Message: "Creates new/file.txt",
|
||||
Message: "Making this new file new/file.txt",
|
||||
Author: api.Identity{
|
||||
Name: "John Doe",
|
||||
Email: "johndoe@example.com",
|
||||
|
@ -45,21 +44,29 @@ func getCreateFileOptions() api.CreateFileOptions {
|
|||
|
||||
func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileResponse {
|
||||
sha := "a635aa942442ddfdba07468cf9661c08fbdf0ebf"
|
||||
encoding := "base64"
|
||||
content := "VGhpcyBpcyBuZXcgdGV4dA=="
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
|
||||
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
|
||||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
|
||||
return &api.FileResponse{
|
||||
Content: &api.FileContentResponse{
|
||||
Content: &api.ContentsResponse{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
Size: 16,
|
||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
|
||||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/" + treePath,
|
||||
Type: "blob",
|
||||
Type: "file",
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
HTMLURL: &htmlURL,
|
||||
},
|
||||
},
|
||||
Commit: &api.FileCommitResponse{
|
||||
|
@ -146,11 +153,24 @@ func TestAPICreateFile(t *testing.T) {
|
|||
var fileResponse api.FileResponse
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf"
|
||||
expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/new/file%d.txt", fileID)
|
||||
expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID)
|
||||
expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/new/file%d.txt", fileID)
|
||||
expectedDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID)
|
||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
|
||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL)
|
||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL)
|
||||
assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
|
||||
assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
|
||||
assert.EqualValues(t, createFileOptions.Message+"\n", fileResponse.Commit.Message)
|
||||
|
||||
// Test creating a file without a message
|
||||
createFileOptions = getCreateFileOptions()
|
||||
createFileOptions.Message = ""
|
||||
fileID++
|
||||
treePath = fmt.Sprintf("new/file%d.txt", fileID)
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2)
|
||||
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
|
||||
resp = session.MakeRequest(t, req, http.StatusCreated)
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
expectedMessage := "Add '" + treePath + "'\n"
|
||||
assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message)
|
||||
|
||||
// Test trying to create a file that already exists, should fail
|
||||
createFileOptions = getCreateFileOptions()
|
||||
|
@ -160,7 +180,7 @@ func TestAPICreateFile(t *testing.T) {
|
|||
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
|
||||
expectedAPIError := context.APIError{
|
||||
Message: "repository file already exists [path: " + treePath + "]",
|
||||
URL: base.DocURL,
|
||||
URL: setting.API.SwaggerURL,
|
||||
}
|
||||
var apiError context.APIError
|
||||
DecodeJSON(t, resp, &apiError)
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -23,7 +23,7 @@ func getDeleteFileOptions() *api.DeleteFileOptions {
|
|||
FileOptions: api.FileOptions{
|
||||
BranchName: "master",
|
||||
NewBranchName: "master",
|
||||
Message: "Updates new/file.txt",
|
||||
Message: "Removing the file new/file.txt",
|
||||
Author: api.Identity{
|
||||
Name: "John Doe",
|
||||
Email: "johndoe@example.com",
|
||||
|
@ -89,6 +89,20 @@ func TestAPIDeleteFile(t *testing.T) {
|
|||
DecodeJSON(t, resp, &fileResponse)
|
||||
assert.NotNil(t, fileResponse)
|
||||
assert.Nil(t, fileResponse.Content)
|
||||
assert.EqualValues(t, deleteFileOptions.Message+"\n", fileResponse.Commit.Message)
|
||||
|
||||
// Test deleting file without a message
|
||||
fileID++
|
||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
|
||||
createFile(user2, repo1, treePath)
|
||||
deleteFileOptions = getDeleteFileOptions()
|
||||
deleteFileOptions.Message = ""
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2)
|
||||
req = NewRequestWithJSON(t, "DELETE", url, &deleteFileOptions)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
expectedMessage := "Delete '" + treePath + "'\n"
|
||||
assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message)
|
||||
|
||||
// Test deleting a file with the wrong SHA
|
||||
fileID++
|
||||
|
@ -102,13 +116,13 @@ func TestAPIDeleteFile(t *testing.T) {
|
|||
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
|
||||
expectedAPIError := context.APIError{
|
||||
Message: "sha does not match [given: " + deleteFileOptions.SHA + ", expected: " + correctSHA + "]",
|
||||
URL: base.DocURL,
|
||||
URL: setting.API.SwaggerURL,
|
||||
}
|
||||
var apiError context.APIError
|
||||
DecodeJSON(t, resp, &apiError)
|
||||
assert.Equal(t, expectedAPIError, apiError)
|
||||
|
||||
// Test creating a file in repo1 by user4 who does not have write access
|
||||
// Test creating a file in repo16 by user4 who does not have write access
|
||||
fileID++
|
||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
|
||||
createFile(user2, repo16, treePath)
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -26,28 +25,51 @@ func getUpdateFileOptions() *api.UpdateFileOptions {
|
|||
content := "This is updated text"
|
||||
contentEncoded := base64.StdEncoding.EncodeToString([]byte(content))
|
||||
return &api.UpdateFileOptions{
|
||||
DeleteFileOptions: *getDeleteFileOptions(),
|
||||
Content: contentEncoded,
|
||||
DeleteFileOptions: api.DeleteFileOptions{
|
||||
FileOptions: api.FileOptions{
|
||||
BranchName: "master",
|
||||
NewBranchName: "master",
|
||||
Message: "My update of new/file.txt",
|
||||
Author: api.Identity{
|
||||
Name: "John Doe",
|
||||
Email: "johndoe@example.com",
|
||||
},
|
||||
Committer: api.Identity{
|
||||
Name: "Jane Doe",
|
||||
Email: "janedoe@example.com",
|
||||
},
|
||||
},
|
||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||
},
|
||||
Content: contentEncoded,
|
||||
}
|
||||
}
|
||||
|
||||
func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileResponse {
|
||||
sha := "08bd14b2e2852529157324de9c226b3364e76136"
|
||||
encoding := "base64"
|
||||
content := "VGhpcyBpcyB1cGRhdGVkIHRleHQ="
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
|
||||
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
|
||||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
|
||||
return &api.FileResponse{
|
||||
Content: &api.FileContentResponse{
|
||||
Content: &api.ContentsResponse{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
Type: "file",
|
||||
Size: 20,
|
||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
|
||||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/" + treePath,
|
||||
Type: "blob",
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
HTMLURL: &htmlURL,
|
||||
},
|
||||
},
|
||||
Commit: &api.FileCommitResponse{
|
||||
|
@ -68,7 +90,7 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon
|
|||
Email: "johndoe@example.com",
|
||||
},
|
||||
},
|
||||
Message: "Updates README.md\n",
|
||||
Message: "My update of README.md\n",
|
||||
},
|
||||
Verification: &api.PayloadCommitVerification{
|
||||
Verified: false,
|
||||
|
@ -136,11 +158,12 @@ func TestAPIUpdateFile(t *testing.T) {
|
|||
var fileResponse api.FileResponse
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
expectedSHA := "08bd14b2e2852529157324de9c226b3364e76136"
|
||||
expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/update/file%d.txt", fileID)
|
||||
expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID)
|
||||
expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/update/file%d.txt", fileID)
|
||||
expectedDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID)
|
||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
|
||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL)
|
||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL)
|
||||
assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
|
||||
assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
|
||||
assert.EqualValues(t, updateFileOptions.Message+"\n", fileResponse.Commit.Message)
|
||||
|
||||
// Test updating a file and renaming it
|
||||
updateFileOptions = getUpdateFileOptions()
|
||||
|
@ -155,11 +178,25 @@ func TestAPIUpdateFile(t *testing.T) {
|
|||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
expectedSHA = "08bd14b2e2852529157324de9c226b3364e76136"
|
||||
expectedHTMLURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/master/rename/update/file%d.txt", fileID)
|
||||
expectedDownloadURL = fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID)
|
||||
expectedHTMLURL = fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/master/rename/update/file%d.txt", fileID)
|
||||
expectedDownloadURL = fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID)
|
||||
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
|
||||
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL)
|
||||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL)
|
||||
assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
|
||||
assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
|
||||
|
||||
// Test updating a file without a message
|
||||
updateFileOptions = getUpdateFileOptions()
|
||||
updateFileOptions.Message = ""
|
||||
updateFileOptions.BranchName = repo1.DefaultBranch
|
||||
fileID++
|
||||
treePath = fmt.Sprintf("update/file%d.txt", fileID)
|
||||
createFile(user2, repo1, treePath)
|
||||
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2)
|
||||
req = NewRequestWithJSON(t, "PUT", url, &updateFileOptions)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
expectedMessage := "Update '" + treePath + "'\n"
|
||||
assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message)
|
||||
|
||||
// Test updating a file with the wrong SHA
|
||||
fileID++
|
||||
|
@ -173,7 +210,7 @@ func TestAPIUpdateFile(t *testing.T) {
|
|||
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
|
||||
expectedAPIError := context.APIError{
|
||||
Message: "sha does not match [given: " + updateFileOptions.SHA + ", expected: " + correctSHA + "]",
|
||||
URL: base.DocURL,
|
||||
URL: setting.API.SwaggerURL,
|
||||
}
|
||||
var apiError context.APIError
|
||||
DecodeJSON(t, resp, &apiError)
|
||||
|
|
156
integrations/api_repo_get_contents_list_test.go
Normal file
156
integrations/api_repo_get_contents_list_test.go
Normal file
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getExpectedContentsListResponseForContents(ref, refType string) []*api.ContentsResponse {
|
||||
treePath := "README.md"
|
||||
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=" + ref
|
||||
htmlURL := setting.AppURL + "user2/repo1/src/" + refType + "/" + ref + "/" + treePath
|
||||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath
|
||||
return []*api.ContentsResponse{
|
||||
{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
HTMLURL: &htmlURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGetContentsList(t *testing.T) {
|
||||
onGiteaRun(t, testAPIGetContentsList)
|
||||
}
|
||||
|
||||
func testAPIGetContentsList(t *testing.T, u *url.URL) {
|
||||
/*** SETUP ***/
|
||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
|
||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo
|
||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
|
||||
treePath := "" // root dir
|
||||
|
||||
// Get user2's token
|
||||
session := loginUser(t, user2.Name)
|
||||
token2 := getTokenForLoggedInUser(t, session)
|
||||
session = emptyTestSession(t)
|
||||
// Get user4's token
|
||||
session = loginUser(t, user4.Name)
|
||||
token4 := getTokenForLoggedInUser(t, session)
|
||||
session = emptyTestSession(t)
|
||||
|
||||
// Make a new branch in repo1
|
||||
newBranch := "test_branch"
|
||||
repo1.CreateNewBranch(user2, repo1.DefaultBranch, newBranch)
|
||||
// Get the commit ID of the default branch
|
||||
gitRepo, _ := git.OpenRepository(repo1.RepoPath())
|
||||
commitID, _ := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
|
||||
// Make a new tag in repo1
|
||||
newTag := "test_tag"
|
||||
gitRepo.CreateTag(newTag, commitID)
|
||||
/*** END SETUP ***/
|
||||
|
||||
// ref is default ref
|
||||
ref := repo1.DefaultBranch
|
||||
refType := "branch"
|
||||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
var contentsListResponse []*api.ContentsResponse
|
||||
DecodeJSON(t, resp, &contentsListResponse)
|
||||
assert.NotNil(t, contentsListResponse)
|
||||
expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType)
|
||||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
|
||||
|
||||
// No ref
|
||||
refType = "branch"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &contentsListResponse)
|
||||
assert.NotNil(t, contentsListResponse)
|
||||
expectedContentsListResponse = getExpectedContentsListResponseForContents(repo1.DefaultBranch, refType)
|
||||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
|
||||
|
||||
// ref is the branch we created above in setup
|
||||
ref = newBranch
|
||||
refType = "branch"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &contentsListResponse)
|
||||
assert.NotNil(t, contentsListResponse)
|
||||
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType)
|
||||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
|
||||
|
||||
// ref is the new tag we created above in setup
|
||||
ref = newTag
|
||||
refType = "tag"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &contentsListResponse)
|
||||
assert.NotNil(t, contentsListResponse)
|
||||
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType)
|
||||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
|
||||
|
||||
// ref is a commit
|
||||
ref = commitID
|
||||
refType = "commit"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &contentsListResponse)
|
||||
assert.NotNil(t, contentsListResponse)
|
||||
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType)
|
||||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
|
||||
|
||||
// Test file contents a file with a bad ref
|
||||
ref = "badref"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
|
||||
expectedAPIError := context.APIError{
|
||||
Message: "object does not exist [id: " + ref + ", rel_path: ]",
|
||||
URL: setting.API.SwaggerURL,
|
||||
}
|
||||
var apiError context.APIError
|
||||
DecodeJSON(t, resp, &apiError)
|
||||
assert.Equal(t, expectedAPIError, apiError)
|
||||
|
||||
// Test accessing private ref with user token that does not have access - should fail
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4)
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Test access private ref of owner of token
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/readme.md?token=%s", user2.Name, repo16.Name, token2)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Test access of org user3 private repo file by owner user2
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
157
integrations/api_repo_get_contents_test.go
Normal file
157
integrations/api_repo_get_contents_test.go
Normal file
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getExpectedContentsResponseForContents(ref, refType string) *api.ContentsResponse {
|
||||
treePath := "README.md"
|
||||
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
|
||||
encoding := "base64"
|
||||
content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=" + ref
|
||||
htmlURL := setting.AppURL + "user2/repo1/src/" + refType + "/" + ref + "/" + treePath
|
||||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath
|
||||
return &api.ContentsResponse{
|
||||
Name: treePath,
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
HTMLURL: &htmlURL,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGetContents(t *testing.T) {
|
||||
onGiteaRun(t, testAPIGetContents)
|
||||
}
|
||||
|
||||
func testAPIGetContents(t *testing.T, u *url.URL) {
|
||||
/*** SETUP ***/
|
||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
|
||||
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo
|
||||
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
|
||||
treePath := "README.md"
|
||||
|
||||
// Get user2's token
|
||||
session := loginUser(t, user2.Name)
|
||||
token2 := getTokenForLoggedInUser(t, session)
|
||||
session = emptyTestSession(t)
|
||||
// Get user4's token
|
||||
session = loginUser(t, user4.Name)
|
||||
token4 := getTokenForLoggedInUser(t, session)
|
||||
session = emptyTestSession(t)
|
||||
|
||||
// Make a new branch in repo1
|
||||
newBranch := "test_branch"
|
||||
repo1.CreateNewBranch(user2, repo1.DefaultBranch, newBranch)
|
||||
// Get the commit ID of the default branch
|
||||
gitRepo, _ := git.OpenRepository(repo1.RepoPath())
|
||||
commitID, _ := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
|
||||
// Make a new tag in repo1
|
||||
newTag := "test_tag"
|
||||
gitRepo.CreateTag(newTag, commitID)
|
||||
/*** END SETUP ***/
|
||||
|
||||
// ref is default ref
|
||||
ref := repo1.DefaultBranch
|
||||
refType := "branch"
|
||||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
var contentsResponse api.ContentsResponse
|
||||
DecodeJSON(t, resp, &contentsResponse)
|
||||
assert.NotNil(t, contentsResponse)
|
||||
expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType)
|
||||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
|
||||
|
||||
// No ref
|
||||
refType = "branch"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &contentsResponse)
|
||||
assert.NotNil(t, contentsResponse)
|
||||
expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType)
|
||||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
|
||||
|
||||
// ref is the branch we created above in setup
|
||||
ref = newBranch
|
||||
refType = "branch"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &contentsResponse)
|
||||
assert.NotNil(t, contentsResponse)
|
||||
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType)
|
||||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
|
||||
|
||||
// ref is the new tag we created above in setup
|
||||
ref = newTag
|
||||
refType = "tag"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &contentsResponse)
|
||||
assert.NotNil(t, contentsResponse)
|
||||
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType)
|
||||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
|
||||
|
||||
// ref is a commit
|
||||
ref = commitID
|
||||
refType = "commit"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &contentsResponse)
|
||||
assert.NotNil(t, contentsResponse)
|
||||
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType)
|
||||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
|
||||
|
||||
// Test file contents a file with a bad ref
|
||||
ref = "badref"
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
|
||||
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
|
||||
expectedAPIError := context.APIError{
|
||||
Message: "object does not exist [id: " + ref + ", rel_path: ]",
|
||||
URL: setting.API.SwaggerURL,
|
||||
}
|
||||
var apiError context.APIError
|
||||
DecodeJSON(t, resp, &apiError)
|
||||
assert.Equal(t, expectedAPIError, apiError)
|
||||
|
||||
// Test accessing private ref with user token that does not have access - should fail
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4)
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Test access private ref of owner of token
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/readme.md?token=%s", user2.Name, repo16.Name, token2)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Test access of org user3 private repo file by owner user2
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
59
integrations/api_repo_git_tags_test.go
Normal file
59
integrations/api_repo_git_tags_test.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAPIGitTags(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
|
||||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||
// Login as User2.
|
||||
session := loginUser(t, user.Name)
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
|
||||
// Set up git config for the tagger
|
||||
git.NewCommand("config", "user.name", user.Name).RunInDir(repo.RepoPath())
|
||||
git.NewCommand("config", "user.email", user.Email).RunInDir(repo.RepoPath())
|
||||
|
||||
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
||||
commit, _ := gitRepo.GetBranchCommit("master")
|
||||
lTagName := "lightweightTag"
|
||||
gitRepo.CreateTag(lTagName, commit.ID.String())
|
||||
|
||||
aTagName := "annotatedTag"
|
||||
aTagMessage := "my annotated message"
|
||||
gitRepo.CreateAnnotatedTag(aTagName, aTagMessage, commit.ID.String())
|
||||
aTag, _ := gitRepo.GetTag(aTagName)
|
||||
|
||||
// SHOULD work for annotated tags
|
||||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/tags/%s?token=%s", user.Name, repo.Name, aTag.ID.String(), token)
|
||||
res := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
var tag *api.AnnotatedTag
|
||||
DecodeJSON(t, res, &tag)
|
||||
|
||||
assert.Equal(t, aTagName, tag.Tag)
|
||||
assert.Equal(t, aTag.ID.String(), tag.SHA)
|
||||
assert.Equal(t, commit.ID.String(), tag.Object.SHA)
|
||||
assert.Equal(t, aTagMessage, tag.Message)
|
||||
assert.Equal(t, user.Name, tag.Tagger.Name)
|
||||
assert.Equal(t, user.Email, tag.Tagger.Email)
|
||||
assert.Equal(t, util.URLJoin(repo.APIURL(), "git/tags", aTag.ID.String()), tag.URL)
|
||||
|
||||
// Should NOT work for lightweight tags
|
||||
badReq := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/tags/%s?token=%s", user.Name, repo.Name, commit.ID.String(), token)
|
||||
session.MakeRequest(t, badReq, http.StatusBadRequest)
|
||||
}
|
|
@ -6,7 +6,6 @@ package integrations
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
|
@ -32,7 +31,7 @@ func TestAPIReposGetTags(t *testing.T) {
|
|||
assert.EqualValues(t, 1, len(tags))
|
||||
assert.Equal(t, "v1.1", tags[0].Name)
|
||||
assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.SHA)
|
||||
assert.Equal(t, path.Join(setting.AppSubURL, "/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d"), tags[0].Commit.URL)
|
||||
assert.Equal(t, path.Join(setting.AppSubURL, "/user2/repo1/archive/v1.1.zip"), tags[0].ZipballURL)
|
||||
assert.Equal(t, path.Join(setting.AppSubURL, "/user2/repo1/archive/v1.1.tar.gz"), tags[0].TarballURL)
|
||||
assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.URL)
|
||||
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.zip", tags[0].ZipballURL)
|
||||
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.tar.gz", tags[0].TarballURL)
|
||||
}
|
||||
|
|
|
@ -69,40 +69,41 @@ func TestAPISearchRepo(t *testing.T) {
|
|||
name, requestURL string
|
||||
expectedResults
|
||||
}{
|
||||
{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{
|
||||
{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50&private=false", expectedResults: expectedResults{
|
||||
nil: {count: 21},
|
||||
user: {count: 21},
|
||||
user2: {count: 21}},
|
||||
},
|
||||
{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{
|
||||
{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10&private=false", expectedResults: expectedResults{
|
||||
nil: {count: 10},
|
||||
user: {count: 10},
|
||||
user2: {count: 10}},
|
||||
},
|
||||
{name: "RepositoriesDefaultMax10", requestURL: "/api/v1/repos/search?default", expectedResults: expectedResults{
|
||||
{name: "RepositoriesDefaultMax10", requestURL: "/api/v1/repos/search?default&private=false", expectedResults: expectedResults{
|
||||
nil: {count: 10},
|
||||
user: {count: 10},
|
||||
user2: {count: 10}},
|
||||
},
|
||||
{name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s", "big_test_"), expectedResults: expectedResults{
|
||||
{name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s&private=false", "big_test_"), expectedResults: expectedResults{
|
||||
nil: {count: 7, repoName: "big_test_"},
|
||||
user: {count: 7, repoName: "big_test_"},
|
||||
user2: {count: 7, repoName: "big_test_"}},
|
||||
},
|
||||
{name: "RepositoriesAccessibleAndRelatedToUser", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user.ID), expectedResults: expectedResults{
|
||||
nil: {count: 4},
|
||||
user: {count: 8, includesPrivate: true},
|
||||
user2: {count: 4}},
|
||||
nil: {count: 5},
|
||||
user: {count: 9, includesPrivate: true},
|
||||
user2: {count: 5, includesPrivate: true}},
|
||||
},
|
||||
{name: "RepositoriesAccessibleAndRelatedToUser2", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user2.ID), expectedResults: expectedResults{
|
||||
nil: {count: 1},
|
||||
user: {count: 1},
|
||||
user2: {count: 2, includesPrivate: true}},
|
||||
user: {count: 2, includesPrivate: true},
|
||||
user2: {count: 2, includesPrivate: true},
|
||||
user4: {count: 1}},
|
||||
},
|
||||
{name: "RepositoriesAccessibleAndRelatedToUser3", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user3.ID), expectedResults: expectedResults{
|
||||
nil: {count: 1},
|
||||
user: {count: 1},
|
||||
user2: {count: 1},
|
||||
user: {count: 4, includesPrivate: true},
|
||||
user2: {count: 2, includesPrivate: true},
|
||||
user3: {count: 4, includesPrivate: true}},
|
||||
},
|
||||
{name: "RepositoriesOwnedByOrganization", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", orgUser.ID), expectedResults: expectedResults{
|
||||
|
@ -112,12 +113,12 @@ func TestAPISearchRepo(t *testing.T) {
|
|||
},
|
||||
{name: "RepositoriesAccessibleAndRelatedToUser4", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user4.ID), expectedResults: expectedResults{
|
||||
nil: {count: 3},
|
||||
user: {count: 3},
|
||||
user4: {count: 6, includesPrivate: true}}},
|
||||
user: {count: 4, includesPrivate: true},
|
||||
user4: {count: 7, includesPrivate: true}}},
|
||||
{name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "source"), expectedResults: expectedResults{
|
||||
nil: {count: 0},
|
||||
user: {count: 0},
|
||||
user4: {count: 0, includesPrivate: true}}},
|
||||
user: {count: 1, includesPrivate: true},
|
||||
user4: {count: 1, includesPrivate: true}}},
|
||||
{name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "fork"), expectedResults: expectedResults{
|
||||
nil: {count: 1},
|
||||
user: {count: 1},
|
||||
|
@ -136,8 +137,8 @@ func TestAPISearchRepo(t *testing.T) {
|
|||
user4: {count: 2, includesPrivate: true}}},
|
||||
{name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "collaborative"), expectedResults: expectedResults{
|
||||
nil: {count: 0},
|
||||
user: {count: 0},
|
||||
user4: {count: 0, includesPrivate: true}}},
|
||||
user: {count: 1, includesPrivate: true},
|
||||
user4: {count: 1, includesPrivate: true}}},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
@ -164,14 +165,19 @@ func TestAPISearchRepo(t *testing.T) {
|
|||
var body api.SearchResults
|
||||
DecodeJSON(t, response, &body)
|
||||
|
||||
assert.Len(t, body.Data, expected.count)
|
||||
repoNames := make([]string, 0, len(body.Data))
|
||||
for _, repo := range body.Data {
|
||||
repoNames = append(repoNames, fmt.Sprintf("%d:%s:%t", repo.ID, repo.FullName, repo.Private))
|
||||
}
|
||||
assert.Len(t, repoNames, expected.count)
|
||||
for _, repo := range body.Data {
|
||||
r := getRepo(t, repo.ID)
|
||||
hasAccess, err := models.HasAccess(userID, r)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, hasAccess)
|
||||
assert.NoError(t, err, "Error when checking if User: %d has access to %s: %v", userID, repo.FullName, err)
|
||||
assert.True(t, hasAccess, "User: %d does not have access to %s", userID, repo.FullName)
|
||||
|
||||
assert.NotEmpty(t, repo.Name)
|
||||
assert.Equal(t, repo.Name, r.Name)
|
||||
|
||||
if len(expected.repoName) > 0 {
|
||||
assert.Contains(t, repo.Name, expected.repoName)
|
||||
|
@ -182,7 +188,7 @@ func TestAPISearchRepo(t *testing.T) {
|
|||
}
|
||||
|
||||
if !expected.includesPrivate {
|
||||
assert.False(t, repo.Private)
|
||||
assert.False(t, repo.Private, "User: %d not expecting private repository: %s", userID, repo.FullName)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -38,6 +38,7 @@ func TestUserOrgs(t *testing.T) {
|
|||
Description: "",
|
||||
Website: "",
|
||||
Location: "",
|
||||
Visibility: "public",
|
||||
},
|
||||
}, orgs)
|
||||
}
|
||||
|
@ -63,6 +64,7 @@ func TestMyOrgs(t *testing.T) {
|
|||
Description: "",
|
||||
Website: "",
|
||||
Location: "",
|
||||
Visibility: "public",
|
||||
},
|
||||
}, orgs)
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ func branchAction(t *testing.T, button string) (*HTMLDoc, string) {
|
|||
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
||||
"_csrf": getCsrf(t, htmlDoc.doc),
|
||||
})
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
url, err := url.Parse(link)
|
||||
assert.NoError(t, err)
|
||||
|
|
22
integrations/cors_test.go
Normal file
22
integrations/cors_test.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCORSNotSet(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
req := NewRequestf(t, "GET", "/api/v1/version")
|
||||
session := loginUser(t, "user2")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, resp.Code, http.StatusOK)
|
||||
corsHeader := resp.Header().Get("Access-Control-Allow-Origin")
|
||||
assert.Equal(t, corsHeader, "", "Access-Control-Allow-Origin: generated header should match") // header not set
|
||||
}
|
|
@ -34,7 +34,7 @@ func TestCreateFile(t *testing.T) {
|
|||
"content": "Content",
|
||||
"commit_choice": "direct",
|
||||
})
|
||||
resp = session.MakeRequest(t, req, http.StatusFound)
|
||||
session.MakeRequest(t, req, http.StatusFound)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
|
|||
"_csrf": csrf,
|
||||
"protected": "on",
|
||||
})
|
||||
resp := session.MakeRequest(t, req, http.StatusFound)
|
||||
session.MakeRequest(t, req, http.StatusFound)
|
||||
// Check if master branch has been locked successfully
|
||||
flashCookie := session.GetCookie("macaron_flash")
|
||||
assert.NotNil(t, flashCookie)
|
||||
|
@ -56,7 +56,7 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
|
|||
|
||||
// Request editor page
|
||||
req = NewRequest(t, "GET", "/user2/repo1/_new/master/")
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
doc := NewHTMLParser(t, resp.Body)
|
||||
lastCommit := doc.GetInputValueByName("last_commit")
|
||||
|
|
|
@ -112,16 +112,44 @@ func doGitAddRemote(dstPath, remoteName string, u *url.URL) func(*testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func doGitPushTestRepository(dstPath, remoteName, branch string) func(*testing.T) {
|
||||
func doGitPushTestRepository(dstPath string, args ...string) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
_, err := git.NewCommand("push", "-u", remoteName, branch).RunInDir(dstPath)
|
||||
_, err := git.NewCommand(append([]string{"push", "-u"}, args...)...).RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func doGitPushTestRepositoryFail(dstPath, remoteName, branch string) func(*testing.T) {
|
||||
func doGitPushTestRepositoryFail(dstPath string, args ...string) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
_, err := git.NewCommand("push", "-u", remoteName, branch).RunInDir(dstPath)
|
||||
_, err := git.NewCommand(append([]string{"push"}, args...)...).RunInDir(dstPath)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func doGitCreateBranch(dstPath, branch string) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
_, err := git.NewCommand("checkout", "-b", branch).RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
_, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func doGitMerge(dstPath string, args ...string) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
_, err := git.NewCommand(append([]string{"merge"}, args...)...).RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func doGitPull(dstPath string, args ...string) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
_, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -37,225 +39,80 @@ func testGit(t *testing.T, u *url.URL) {
|
|||
|
||||
u.Path = baseAPITestContext.GitPath()
|
||||
|
||||
forkedUserCtx := NewAPITestContext(t, "user4", "repo1")
|
||||
|
||||
t.Run("HTTP", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
ensureAnonymousClone(t, u)
|
||||
httpContext := baseAPITestContext
|
||||
httpContext.Reponame = "repo-tmp-17"
|
||||
forkedUserCtx.Reponame = httpContext.Reponame
|
||||
|
||||
dstPath, err := ioutil.TempDir("", httpContext.Reponame)
|
||||
var little, big, littleLFS, bigLFS string
|
||||
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dstPath)
|
||||
t.Run("Standard", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
ensureAnonymousClone(t, u)
|
||||
|
||||
t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
|
||||
t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false))
|
||||
t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, httpContext.Username, models.AccessModeRead))
|
||||
|
||||
u.Path = httpContext.GitPath()
|
||||
u.User = url.UserPassword(username, userPassword)
|
||||
t.Run("ForkFromDifferentUser", doAPIForkRepository(httpContext, forkedUserCtx.Username))
|
||||
|
||||
t.Run("Clone", doGitClone(dstPath, u))
|
||||
u.Path = httpContext.GitPath()
|
||||
u.User = url.UserPassword(username, userPassword)
|
||||
|
||||
t.Run("PushCommit", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
t.Run("Little", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
little = commitAndPush(t, littleSize, dstPath)
|
||||
})
|
||||
t.Run("Big", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
big = commitAndPush(t, bigSize, dstPath)
|
||||
})
|
||||
})
|
||||
t.Run("Clone", doGitClone(dstPath, u))
|
||||
|
||||
little, big := standardCommitAndPushTest(t, dstPath)
|
||||
littleLFS, bigLFS := lfsCommitAndPushTest(t, dstPath)
|
||||
rawTest(t, &httpContext, little, big, littleLFS, bigLFS)
|
||||
mediaTest(t, &httpContext, little, big, littleLFS, bigLFS)
|
||||
|
||||
t.Run("BranchProtectMerge", doBranchProtectPRMerge(&httpContext, dstPath))
|
||||
t.Run("MergeFork", func(t *testing.T) {
|
||||
t.Run("CreatePRAndMerge", doMergeFork(httpContext, forkedUserCtx, "master", httpContext.Username+":master"))
|
||||
t.Run("DeleteRepository", doAPIDeleteRepository(httpContext))
|
||||
rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
|
||||
mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
|
||||
})
|
||||
t.Run("LFS", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
t.Run("PushCommit", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
//Setup git LFS
|
||||
_, err = git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("lfs").AddArguments("track", "data-file-*").RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
err = git.AddChanges(dstPath, false, ".gitattributes")
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("Little", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
littleLFS = commitAndPush(t, littleSize, dstPath)
|
||||
})
|
||||
t.Run("Big", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
bigLFS = commitAndPush(t, bigSize, dstPath)
|
||||
})
|
||||
})
|
||||
t.Run("Locks", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
lockTest(t, u.String(), dstPath)
|
||||
})
|
||||
})
|
||||
t.Run("Raw", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Request raw paths
|
||||
req := NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/raw/branch/master/", little))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Body.Len())
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/raw/branch/master/", big))
|
||||
nilResp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, nilResp.Length)
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/raw/branch/master/", littleLFS))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEqual(t, littleSize, resp.Body.Len())
|
||||
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/raw/branch/master/", bigLFS))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEqual(t, bigSize, resp.Body.Len())
|
||||
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
||||
|
||||
})
|
||||
t.Run("Media", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Request media paths
|
||||
req := NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/media/branch/master/", little))
|
||||
resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Length)
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/media/branch/master/", big))
|
||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, resp.Length)
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/media/branch/master/", littleLFS))
|
||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Length)
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/media/branch/master/", bigLFS))
|
||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, resp.Length)
|
||||
})
|
||||
|
||||
})
|
||||
t.Run("SSH", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
sshContext := baseAPITestContext
|
||||
sshContext.Reponame = "repo-tmp-18"
|
||||
keyname := "my-testing-key"
|
||||
forkedUserCtx.Reponame = sshContext.Reponame
|
||||
t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false))
|
||||
t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, sshContext.Username, models.AccessModeRead))
|
||||
t.Run("ForkFromDifferentUser", doAPIForkRepository(sshContext, forkedUserCtx.Username))
|
||||
|
||||
//Setup key the user ssh key
|
||||
withKeyFile(t, keyname, func(keyFile string) {
|
||||
t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
|
||||
PrintCurrentTest(t)
|
||||
|
||||
//Setup remote link
|
||||
//TODO: get url from api
|
||||
sshURL := createSSHUrl(sshContext.GitPath(), u)
|
||||
|
||||
//Setup clone folder
|
||||
dstPath, err := ioutil.TempDir("", sshContext.Reponame)
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dstPath)
|
||||
var little, big, littleLFS, bigLFS string
|
||||
|
||||
t.Run("Standard", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
t.Run("CreateRepo", doAPICreateRepository(sshContext, false))
|
||||
t.Run("Clone", doGitClone(dstPath, sshURL))
|
||||
|
||||
//TODO get url from api
|
||||
t.Run("Clone", doGitClone(dstPath, sshURL))
|
||||
little, big := standardCommitAndPushTest(t, dstPath)
|
||||
littleLFS, bigLFS := lfsCommitAndPushTest(t, dstPath)
|
||||
rawTest(t, &sshContext, little, big, littleLFS, bigLFS)
|
||||
mediaTest(t, &sshContext, little, big, littleLFS, bigLFS)
|
||||
|
||||
//time.Sleep(5 * time.Minute)
|
||||
t.Run("PushCommit", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
t.Run("Little", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
little = commitAndPush(t, littleSize, dstPath)
|
||||
})
|
||||
t.Run("Big", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
big = commitAndPush(t, bigSize, dstPath)
|
||||
})
|
||||
})
|
||||
t.Run("BranchProtectMerge", doBranchProtectPRMerge(&sshContext, dstPath))
|
||||
t.Run("MergeFork", func(t *testing.T) {
|
||||
t.Run("CreatePRAndMerge", doMergeFork(sshContext, forkedUserCtx, "master", sshContext.Username+":master"))
|
||||
t.Run("DeleteRepository", doAPIDeleteRepository(sshContext))
|
||||
rawTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
|
||||
mediaTest(t, &forkedUserCtx, little, big, littleLFS, bigLFS)
|
||||
})
|
||||
t.Run("LFS", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
t.Run("PushCommit", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
//Setup git LFS
|
||||
_, err = git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("lfs").AddArguments("track", "data-file-*").RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
err = git.AddChanges(dstPath, false, ".gitattributes")
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("Little", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
littleLFS = commitAndPush(t, littleSize, dstPath)
|
||||
})
|
||||
t.Run("Big", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
bigLFS = commitAndPush(t, bigSize, dstPath)
|
||||
})
|
||||
})
|
||||
t.Run("Locks", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
lockTest(t, u.String(), dstPath)
|
||||
})
|
||||
})
|
||||
t.Run("Raw", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Request raw paths
|
||||
req := NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/raw/branch/master/", little))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Body.Len())
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/raw/branch/master/", big))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, resp.Body.Len())
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/raw/branch/master/", littleLFS))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEqual(t, littleSize, resp.Body.Len())
|
||||
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/raw/branch/master/", bigLFS))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEqual(t, bigSize, resp.Body.Len())
|
||||
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
||||
|
||||
})
|
||||
t.Run("Media", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Request media paths
|
||||
req := NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/media/branch/master/", little))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Body.Len())
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/media/branch/master/", big))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, resp.Body.Len())
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/media/branch/master/", littleLFS))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Body.Len())
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/media/branch/master/", bigLFS))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, resp.Body.Len())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -267,35 +124,146 @@ func ensureAnonymousClone(t *testing.T, u *url.URL) {
|
|||
|
||||
}
|
||||
|
||||
func lockTest(t *testing.T, remote, repoPath string) {
|
||||
_, err := git.NewCommand("remote").AddArguments("set-url", "origin", remote).RunInDir(repoPath) //TODO add test ssh git-lfs-creds
|
||||
func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string) {
|
||||
t.Run("Standard", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
little, big = commitAndPushTest(t, dstPath, "data-file-")
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
|
||||
t.Run("LFS", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
prefix := "lfs-data-file-"
|
||||
_, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("lfs").AddArguments("track", prefix+"*").RunInDir(dstPath)
|
||||
assert.NoError(t, err)
|
||||
err = git.AddChanges(dstPath, false, ".gitattributes")
|
||||
assert.NoError(t, err)
|
||||
|
||||
littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix)
|
||||
|
||||
t.Run("Locks", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
lockTest(t, dstPath)
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func commitAndPushTest(t *testing.T, dstPath, prefix string) (little, big string) {
|
||||
t.Run("PushCommit", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
t.Run("Little", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
little = doCommitAndPush(t, littleSize, dstPath, prefix)
|
||||
})
|
||||
t.Run("Big", func(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test in short mode.")
|
||||
return
|
||||
}
|
||||
PrintCurrentTest(t)
|
||||
big = doCommitAndPush(t, bigSize, dstPath, prefix)
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) {
|
||||
t.Run("Raw", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
username := ctx.Username
|
||||
reponame := ctx.Reponame
|
||||
|
||||
session := loginUser(t, username)
|
||||
|
||||
// Request raw paths
|
||||
req := NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", little))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Body.Len())
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEqual(t, littleSize, resp.Body.Len())
|
||||
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
||||
|
||||
if !testing.Short() {
|
||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, resp.Body.Len())
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.NotEqual(t, bigSize, resp.Body.Len())
|
||||
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) {
|
||||
t.Run("Media", func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
|
||||
username := ctx.Username
|
||||
reponame := ctx.Reponame
|
||||
|
||||
session := loginUser(t, username)
|
||||
|
||||
// Request media paths
|
||||
req := NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", little))
|
||||
resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Length)
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
|
||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, littleSize, resp.Length)
|
||||
|
||||
if !testing.Short() {
|
||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
|
||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, resp.Length)
|
||||
|
||||
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
|
||||
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
|
||||
assert.Equal(t, bigSize, resp.Length)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func lockTest(t *testing.T, repoPath string) {
|
||||
lockFileTest(t, "README.md", repoPath)
|
||||
}
|
||||
|
||||
func lockFileTest(t *testing.T, filename, repoPath string) {
|
||||
_, err := git.NewCommand("lfs").AddArguments("locks").RunInDir(repoPath)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("lfs").AddArguments("lock", filename).RunInDir(repoPath)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("lfs").AddArguments("locks").RunInDir(repoPath)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("lfs").AddArguments("lock", "README.md").RunInDir(repoPath)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("lfs").AddArguments("locks").RunInDir(repoPath)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("lfs").AddArguments("unlock", "README.md").RunInDir(repoPath)
|
||||
_, err = git.NewCommand("lfs").AddArguments("unlock", filename).RunInDir(repoPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func commitAndPush(t *testing.T, size int, repoPath string) string {
|
||||
name, err := generateCommitWithNewData(size, repoPath, "user2@example.com", "User Two")
|
||||
func doCommitAndPush(t *testing.T, size int, repoPath, prefix string) string {
|
||||
name, err := generateCommitWithNewData(size, repoPath, "user2@example.com", "User Two", prefix)
|
||||
assert.NoError(t, err)
|
||||
_, err = git.NewCommand("push").RunInDir(repoPath) //Push
|
||||
_, err = git.NewCommand("push", "origin", "master").RunInDir(repoPath) //Push
|
||||
assert.NoError(t, err)
|
||||
return name
|
||||
}
|
||||
|
||||
func generateCommitWithNewData(size int, repoPath, email, fullName string) (string, error) {
|
||||
func generateCommitWithNewData(size int, repoPath, email, fullName, prefix string) (string, error) {
|
||||
//Generate random file
|
||||
data := make([]byte, size)
|
||||
_, err := rand.Read(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tmpFile, err := ioutil.TempFile(repoPath, "data-file-")
|
||||
tmpFile, err := ioutil.TempFile(repoPath, prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -325,3 +293,84 @@ func generateCommitWithNewData(size int, repoPath, email, fullName string) (stri
|
|||
})
|
||||
return filepath.Base(tmpFile.Name()), err
|
||||
}
|
||||
|
||||
func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
PrintCurrentTest(t)
|
||||
t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected"))
|
||||
t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected"))
|
||||
|
||||
ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame)
|
||||
t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", ""))
|
||||
t.Run("GenerateCommit", func(t *testing.T) {
|
||||
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("FailToPushToProtectedBranch", doGitPushTestRepositoryFail(dstPath, "origin", "protected"))
|
||||
t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected"))
|
||||
var pr api.PullRequest
|
||||
var err error
|
||||
t.Run("CreatePullRequest", func(t *testing.T) {
|
||||
pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "protected", "unprotected")(t)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("MergePR", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index))
|
||||
t.Run("PullProtected", doGitPull(dstPath, "origin", "protected"))
|
||||
t.Run("ProtectProtectedBranchWhitelist", doProtectBranch(ctx, "protected", baseCtx.Username))
|
||||
|
||||
t.Run("CheckoutMaster", doGitCheckoutBranch(dstPath, "master"))
|
||||
t.Run("CreateBranchForced", doGitCreateBranch(dstPath, "toforce"))
|
||||
t.Run("GenerateCommit", func(t *testing.T) {
|
||||
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("FailToForcePushToProtectedBranch", doGitPushTestRepositoryFail(dstPath, "-f", "origin", "toforce:protected"))
|
||||
t.Run("MergeProtectedToToforce", doGitMerge(dstPath, "protected"))
|
||||
t.Run("PushToProtectedBranch", doGitPushTestRepository(dstPath, "origin", "toforce:protected"))
|
||||
t.Run("CheckoutMasterAgain", doGitCheckoutBranch(dstPath, "master"))
|
||||
}
|
||||
}
|
||||
|
||||
func doProtectBranch(ctx APITestContext, branch string, userToWhitelist string) func(t *testing.T) {
|
||||
// We are going to just use the owner to set the protection.
|
||||
return func(t *testing.T) {
|
||||
csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings/branches", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)))
|
||||
|
||||
if userToWhitelist == "" {
|
||||
// Change branch to protected
|
||||
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
|
||||
"_csrf": csrf,
|
||||
"protected": "on",
|
||||
})
|
||||
ctx.Session.MakeRequest(t, req, http.StatusFound)
|
||||
} else {
|
||||
user, err := models.GetUserByName(userToWhitelist)
|
||||
assert.NoError(t, err)
|
||||
// Change branch to protected
|
||||
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
|
||||
"_csrf": csrf,
|
||||
"protected": "on",
|
||||
"enable_whitelist": "on",
|
||||
"whitelist_users": strconv.FormatInt(user.ID, 10),
|
||||
})
|
||||
ctx.Session.MakeRequest(t, req, http.StatusFound)
|
||||
}
|
||||
// Check if master branch has been locked successfully
|
||||
flashCookie := ctx.Session.GetCookie("macaron_flash")
|
||||
assert.NotNil(t, flashCookie)
|
||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527"+url.QueryEscape(branch)+"%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
var pr api.PullRequest
|
||||
var err error
|
||||
t.Run("CreatePullRequest", func(t *testing.T) {
|
||||
pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("MergePR", doAPIMergePullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index))
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
0abcb056019adb8336cf9db3ad9d9cf80cd4b141
|
|
@ -42,7 +42,7 @@ type NilResponseRecorder struct {
|
|||
}
|
||||
|
||||
func (n *NilResponseRecorder) Write(b []byte) (int, error) {
|
||||
n.Length = n.Length + len(b)
|
||||
n.Length += len(b)
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ func initIntegrationTest() {
|
|||
setting.CustomConf = giteaConf
|
||||
}
|
||||
|
||||
setting.SetCustomPathAndConf("", "")
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.NewContext()
|
||||
setting.CheckLFSVersion()
|
||||
models.LoadConfigs()
|
||||
|
@ -141,8 +141,7 @@ func initIntegrationTest() {
|
|||
if err != nil {
|
||||
log.Fatalf("sql.Open: %v", err)
|
||||
}
|
||||
rows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'",
|
||||
models.DbCfg.Name))
|
||||
rows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", models.DbCfg.Name))
|
||||
if err != nil {
|
||||
log.Fatalf("db.Query: %v", err)
|
||||
}
|
||||
|
@ -210,7 +209,7 @@ func (s *TestSession) MakeRequest(t testing.TB, req *http.Request, expectedStatu
|
|||
resp := MakeRequest(t, req, expectedStatus)
|
||||
|
||||
ch := http.Header{}
|
||||
ch.Add("Cookie", strings.Join(resp.HeaderMap["Set-Cookie"], ";"))
|
||||
ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
|
||||
cr := http.Request{Header: ch}
|
||||
s.jar.SetCookies(baseURL, cr.Cookies())
|
||||
|
||||
|
@ -226,7 +225,7 @@ func (s *TestSession) MakeRequestNilResponseRecorder(t testing.TB, req *http.Req
|
|||
resp := MakeRequestNilResponseRecorder(t, req, expectedStatus)
|
||||
|
||||
ch := http.Header{}
|
||||
ch.Add("Cookie", strings.Join(resp.HeaderMap["Set-Cookie"], ";"))
|
||||
ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
|
||||
cr := http.Request{Header: ch}
|
||||
s.jar.SetCookies(baseURL, cr.Cookies())
|
||||
|
||||
|
@ -266,7 +265,7 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession
|
|||
resp = MakeRequest(t, req, http.StatusFound)
|
||||
|
||||
ch := http.Header{}
|
||||
ch.Add("Cookie", strings.Join(resp.HeaderMap["Set-Cookie"], ";"))
|
||||
ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
|
||||
cr := http.Request{Header: ch}
|
||||
|
||||
session := emptyTestSession(t)
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func assertProtectedBranch(t *testing.T, repoID int64, branchName string, isErr, canPush bool) {
|
||||
reqURL := fmt.Sprintf("/api/internal/branch/%d/%s", repoID, util.PathEscapeSegments(branchName))
|
||||
req := NewRequest(t, "GET", reqURL)
|
||||
t.Log(reqURL)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", setting.InternalToken))
|
||||
|
||||
resp := MakeRequest(t, req, NoExpectedStatus)
|
||||
if isErr {
|
||||
assert.EqualValues(t, http.StatusInternalServerError, resp.Code)
|
||||
} else {
|
||||
assert.EqualValues(t, http.StatusOK, resp.Code)
|
||||
var branch models.ProtectedBranch
|
||||
t.Log(resp.Body.String())
|
||||
assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), &branch))
|
||||
assert.Equal(t, canPush, !branch.IsProtected())
|
||||
}
|
||||
}
|
||||
|
||||
func TestInternal_GetProtectedBranch(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
|
||||
assertProtectedBranch(t, 1, "master", false, true)
|
||||
assertProtectedBranch(t, 1, "dev", false, true)
|
||||
assertProtectedBranch(t, 1, "lunny/dev", false, true)
|
||||
}
|
|
@ -45,7 +45,7 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string
|
|||
lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(*content)), RepositoryID: repositoryID}
|
||||
}
|
||||
|
||||
lfsID = lfsID + 1
|
||||
lfsID++
|
||||
lfsMetaObject, err = models.NewLFSMetaObject(lfsMetaObject)
|
||||
assert.NoError(t, err)
|
||||
contentStore := &lfs.ContentStore{BasePath: setting.LFS.ContentPath}
|
||||
|
|
|
@ -110,7 +110,7 @@ func testLinksAsUser(userName string, t *testing.T) {
|
|||
reqAPI := NewRequestf(t, "GET", "/api/v1/users/%s/repos", userName)
|
||||
respAPI := MakeRequest(t, reqAPI, http.StatusOK)
|
||||
|
||||
var apiRepos []api.Repository
|
||||
var apiRepos []*api.Repository
|
||||
DecodeJSON(t, respAPI, &apiRepos)
|
||||
|
||||
var repoLinks = []string{
|
||||
|
|
|
@ -57,21 +57,6 @@ func initMigrationTest(t *testing.T) {
|
|||
setting.NewLogServices(true)
|
||||
}
|
||||
|
||||
func getDialect() string {
|
||||
dialect := "sqlite"
|
||||
switch {
|
||||
case setting.UseSQLite3:
|
||||
dialect = "sqlite"
|
||||
case setting.UseMySQL:
|
||||
dialect = "mysql"
|
||||
case setting.UsePostgreSQL:
|
||||
dialect = "pgsql"
|
||||
case setting.UseMSSQL:
|
||||
dialect = "mssql"
|
||||
}
|
||||
return dialect
|
||||
}
|
||||
|
||||
func availableVersions() ([]string, error) {
|
||||
migrationsDir, err := os.Open("integrations/migration-test")
|
||||
if err != nil {
|
||||
|
|
|
@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-mssql
|
|||
OFFLINE_MODE = false
|
||||
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
|
||||
APP_DATA_PATH = integrations/gitea-integration-mssql/data
|
||||
BUILTIN_SSH_SERVER_USER = git
|
||||
|
||||
[mailer]
|
||||
ENABLED = true
|
||||
|
|
|
@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-mysql
|
|||
OFFLINE_MODE = false
|
||||
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
|
||||
APP_DATA_PATH = integrations/gitea-integration-mysql/data
|
||||
BUILTIN_SSH_SERVER_USER = git
|
||||
|
||||
[mailer]
|
||||
ENABLED = true
|
||||
|
|
|
@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-mysql8
|
|||
OFFLINE_MODE = false
|
||||
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
|
||||
APP_DATA_PATH = integrations/gitea-integration-mysql8/data
|
||||
BUILTIN_SSH_SERVER_USER = git
|
||||
|
||||
[mailer]
|
||||
ENABLED = false
|
||||
|
|
|
@ -92,6 +92,15 @@ func TestPrivateOrg(t *testing.T) {
|
|||
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// non-org member who is collaborator on repo in private org
|
||||
session = loginUser(t, "user4")
|
||||
req = NewRequest(t, "GET", "/privated_org")
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
req = NewRequest(t, "GET", "/privated_org/public_repo_on_private_org") // colab of this repo
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// site admin
|
||||
session = loginUser(t, "user1")
|
||||
req = NewRequest(t, "GET", "/privated_org")
|
||||
|
|
|
@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-pgsql
|
|||
OFFLINE_MODE = false
|
||||
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
|
||||
APP_DATA_PATH = integrations/gitea-integration-pgsql/data
|
||||
BUILTIN_SSH_SERVER_USER = git
|
||||
|
||||
[mailer]
|
||||
ENABLED = true
|
||||
|
|
|
@ -159,7 +159,7 @@ func TestCantMergeWorkInProgress(t *testing.T) {
|
|||
req := NewRequest(t, "GET", resp.Header().Get("Location"))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
text := strings.TrimSpace(htmlDoc.doc.Find(".merge.segment > .text.grey").Text())
|
||||
text := strings.TrimSpace(htmlDoc.doc.Find(".attached.header > .text.grey").Last().Text())
|
||||
assert.NotEmpty(t, text, "Can't find WIP text")
|
||||
|
||||
// remove <strong /> from lang
|
||||
|
|
|
@ -7,6 +7,9 @@ package integrations
|
|||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -27,6 +30,19 @@ func resultFilenames(t testing.TB, doc *HTMLDoc) []string {
|
|||
func TestSearchRepo(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
|
||||
repo, err := models.GetRepositoryByOwnerAndName("user2", "repo1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
waiter := make(chan error, 1)
|
||||
models.UpdateRepoIndexer(repo, waiter)
|
||||
|
||||
select {
|
||||
case err := <-waiter:
|
||||
assert.NoError(t, err)
|
||||
case <-time.After(1 * time.Minute):
|
||||
assert.Fail(t, "UpdateRepoIndexer took too long")
|
||||
}
|
||||
|
||||
req := NewRequestf(t, "GET", "/user2/repo1/search?q=Description&page=1")
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ func TestViewRepo1CloneLinkAuthorized(t *testing.T) {
|
|||
assert.Equal(t, setting.AppURL+"user2/repo1.git", link)
|
||||
link, exists = htmlDoc.doc.Find("#repo-clone-ssh").Attr("data-link")
|
||||
assert.True(t, exists, "The template has changed")
|
||||
sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.RunUser, setting.SSH.Domain, setting.SSH.Port)
|
||||
sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.SSH.BuiltinServerUser, setting.SSH.Domain, setting.SSH.Port)
|
||||
assert.Equal(t, sshURL, link)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,40 +24,32 @@ func getDeleteRepoFileOptions(repo *models.Repository) *repofiles.DeleteRepoFile
|
|||
TreePath: "README.md",
|
||||
Message: "Deletes README.md",
|
||||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
||||
Author: nil,
|
||||
Committer: nil,
|
||||
Author: &repofiles.IdentityOptions{
|
||||
Name: "Bob Smith",
|
||||
Email: "bob@smith.com",
|
||||
},
|
||||
Committer: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func getExpectedDeleteFileResponse(u *url.URL) *api.FileResponse {
|
||||
// Just returns fields that don't change, i.e. fields with commit SHAs and dates can't be determined
|
||||
return &api.FileResponse{
|
||||
Content: nil,
|
||||
Commit: &api.FileCommitResponse{
|
||||
CommitMeta: api.CommitMeta{
|
||||
URL: u.String() + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
},
|
||||
HTMLURL: u.String() + "user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
Author: &api.CommitUser{
|
||||
Identity: api.Identity{
|
||||
Name: "user1",
|
||||
Email: "address1@example.com",
|
||||
Name: "Bob Smith",
|
||||
Email: "bob@smith.com",
|
||||
},
|
||||
Date: "2017-03-19T20:47:59Z",
|
||||
},
|
||||
Committer: &api.CommitUser{
|
||||
Identity: api.Identity{
|
||||
Name: "Ethan Koenig",
|
||||
Email: "ethantkoenig@gmail.com",
|
||||
Name: "Bob Smith",
|
||||
Email: "bob@smith.com",
|
||||
},
|
||||
Date: "2017-03-19T20:47:59Z",
|
||||
},
|
||||
Parents: []*api.CommitMeta{},
|
||||
Message: "Initial commit\n",
|
||||
Tree: &api.CommitMeta{
|
||||
URL: u.String() + "api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6",
|
||||
SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6",
|
||||
},
|
||||
Message: "Deletes README.md\n",
|
||||
},
|
||||
Verification: &api.PayloadCommitVerification{
|
||||
Verified: false,
|
||||
|
@ -89,7 +81,12 @@ func testDeleteRepoFile(t *testing.T, u *url.URL) {
|
|||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||
assert.Nil(t, err)
|
||||
expectedFileResponse := getExpectedDeleteFileResponse(u)
|
||||
assert.EqualValues(t, expectedFileResponse, fileResponse)
|
||||
assert.NotNil(t, fileResponse)
|
||||
assert.Nil(t, fileResponse.Content)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.Message, fileResponse.Commit.Message)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Identity, fileResponse.Commit.Author.Identity)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.Committer.Identity, fileResponse.Commit.Committer.Identity)
|
||||
assert.EqualValues(t, expectedFileResponse.Verification, fileResponse.Verification)
|
||||
})
|
||||
|
||||
t.Run("Verify README.md has been deleted", func(t *testing.T) {
|
||||
|
@ -124,7 +121,12 @@ func testDeleteRepoFileWithoutBranchNames(t *testing.T, u *url.URL) {
|
|||
fileResponse, err := repofiles.DeleteRepoFile(repo, doer, opts)
|
||||
assert.Nil(t, err)
|
||||
expectedFileResponse := getExpectedDeleteFileResponse(u)
|
||||
assert.EqualValues(t, expectedFileResponse, fileResponse)
|
||||
assert.NotNil(t, fileResponse)
|
||||
assert.Nil(t, fileResponse.Content)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.Message, fileResponse.Commit.Message)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.Author.Identity, fileResponse.Commit.Author.Identity)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.Committer.Identity, fileResponse.Commit.Committer.Identity)
|
||||
assert.EqualValues(t, expectedFileResponse.Verification, fileResponse.Verification)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ package integrations
|
|||
|
||||
import (
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -47,21 +48,30 @@ func getUpdateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFile
|
|||
}
|
||||
|
||||
func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileResponse {
|
||||
treePath := "new/file.txt"
|
||||
encoding := "base64"
|
||||
content := "VGhpcyBpcyBhIE5FVyBmaWxl"
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
|
||||
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
|
||||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885"
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
|
||||
return &api.FileResponse{
|
||||
Content: &api.FileContentResponse{
|
||||
Name: "file.txt",
|
||||
Path: "new/file.txt",
|
||||
Content: &api.ContentsResponse{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||
Type: "file",
|
||||
Size: 18,
|
||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt",
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt",
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/new/file.txt",
|
||||
Type: "blob",
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt",
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt",
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
HTMLURL: &htmlURL,
|
||||
},
|
||||
},
|
||||
Commit: &api.FileCommitResponse{
|
||||
|
@ -105,22 +115,30 @@ func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileRespons
|
|||
}
|
||||
}
|
||||
|
||||
func getExpectedFileResponseForRepofilesUpdate(commitID string) *api.FileResponse {
|
||||
func getExpectedFileResponseForRepofilesUpdate(commitID, filename string) *api.FileResponse {
|
||||
encoding := "base64"
|
||||
content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ=="
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + filename + "?ref=master"
|
||||
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + filename
|
||||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647"
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename
|
||||
return &api.FileResponse{
|
||||
Content: &api.FileContentResponse{
|
||||
Name: "README.md",
|
||||
Path: "README.md",
|
||||
Content: &api.ContentsResponse{
|
||||
Name: filename,
|
||||
Path: filename,
|
||||
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||
Type: "file",
|
||||
Size: 43,
|
||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md",
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md",
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/README.md",
|
||||
Type: "blob",
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md",
|
||||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md",
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
HTMLURL: &htmlURL,
|
||||
},
|
||||
},
|
||||
Commit: &api.FileCommitResponse{
|
||||
|
@ -213,7 +231,7 @@ func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
||||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath)
|
||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
||||
|
@ -234,9 +252,8 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) {
|
|||
repo := ctx.Repo.Repository
|
||||
doer := ctx.User
|
||||
opts := getUpdateRepoFileOptions(repo)
|
||||
suffix := "_new"
|
||||
opts.FromTreePath = "README.md"
|
||||
opts.TreePath = "README.md" + suffix // new file name, README.md_new
|
||||
opts.TreePath = "README_new.md" // new file name, README_new.md
|
||||
|
||||
// test
|
||||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
|
||||
|
@ -245,7 +262,7 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
||||
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String())
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath)
|
||||
// assert that the old file no longer exists in the last commit of the branch
|
||||
fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath)
|
||||
toEntry, err := commit.GetTreeEntryByPath(opts.TreePath)
|
||||
|
@ -253,9 +270,9 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) {
|
|||
assert.NotNil(t, toEntry) // Should exist here
|
||||
// assert SHA has remained the same but paths use the new file name
|
||||
assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA)
|
||||
assert.EqualValues(t, expectedFileResponse.Content.Name+suffix, fileResponse.Content.Name)
|
||||
assert.EqualValues(t, expectedFileResponse.Content.Path+suffix, fileResponse.Content.Path)
|
||||
assert.EqualValues(t, expectedFileResponse.Content.URL+suffix, fileResponse.Content.URL)
|
||||
assert.EqualValues(t, expectedFileResponse.Content.Name, fileResponse.Content.Name)
|
||||
assert.EqualValues(t, expectedFileResponse.Content.Path, fileResponse.Content.Path)
|
||||
assert.EqualValues(t, expectedFileResponse.Content.URL, fileResponse.Content.URL)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
||||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
||||
})
|
||||
|
@ -284,7 +301,7 @@ func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
gitRepo, _ := git.OpenRepository(repo.RepoPath())
|
||||
commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath)
|
||||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ OFFLINE_MODE = false
|
|||
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
|
||||
APP_DATA_PATH = integrations/gitea-integration-sqlite/data
|
||||
ENABLE_GZIP = true
|
||||
BUILTIN_SSH_SERVER_USER = git
|
||||
|
||||
[mailer]
|
||||
ENABLED = true
|
||||
|
|
|
@ -73,7 +73,7 @@ func PrintCurrentTest(t testing.TB, skip ...int) {
|
|||
_, filename, line, _ := runtime.Caller(actualSkip)
|
||||
|
||||
if log.CanColorStdout {
|
||||
fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", log.NewColoredValue(t.Name()), strings.TrimPrefix(filename, prefix), line)
|
||||
fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", fmt.Formatter(log.NewColoredValue(t.Name())), strings.TrimPrefix(filename, prefix), line)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", t.Name(), strings.TrimPrefix(filename, prefix), line)
|
||||
}
|
||||
|
|
25
main.go
25
main.go
|
@ -21,6 +21,9 @@ import (
|
|||
_ "code.gitea.io/gitea/modules/markup/markdown"
|
||||
_ "code.gitea.io/gitea/modules/markup/orgmode"
|
||||
|
||||
// for embed
|
||||
_ "github.com/shurcooL/vfsgen"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -39,7 +42,7 @@ var (
|
|||
|
||||
func init() {
|
||||
setting.AppVer = Version
|
||||
setting.AppBuiltWith = formatBuiltWith(Tags)
|
||||
setting.AppBuiltWith = formatBuiltWith()
|
||||
|
||||
// Grab the original help templates
|
||||
originalAppHelpTemplate = cli.AppHelpTemplate
|
||||
|
@ -53,7 +56,7 @@ func main() {
|
|||
app.Usage = "A painless self-hosted Git service"
|
||||
app.Description = `By default, gitea will start serving using the webserver with no
|
||||
arguments - which can alternatively be run by running the subcommand web.`
|
||||
app.Version = Version + formatBuiltWith(Tags)
|
||||
app.Version = Version + formatBuiltWith()
|
||||
app.Commands = []cli.Command{
|
||||
cmd.CmdWeb,
|
||||
cmd.CmdServ,
|
||||
|
@ -64,11 +67,12 @@ arguments - which can alternatively be run by running the subcommand web.`
|
|||
cmd.CmdGenerate,
|
||||
cmd.CmdMigrate,
|
||||
cmd.CmdKeys,
|
||||
cmd.CmdConvert,
|
||||
}
|
||||
// Now adjust these commands to add our global configuration options
|
||||
|
||||
// First calculate the default paths and set the AppHelpTemplates in this context
|
||||
setting.SetCustomPathAndConf("", "")
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setAppHelpTemplates()
|
||||
|
||||
// default configuration flags
|
||||
|
@ -84,6 +88,11 @@ arguments - which can alternatively be run by running the subcommand web.`
|
|||
Usage: "Custom configuration file path",
|
||||
},
|
||||
cli.VersionFlag,
|
||||
cli.StringFlag{
|
||||
Name: "work-path, w",
|
||||
Value: setting.AppWorkPath,
|
||||
Usage: "Set the gitea working path",
|
||||
},
|
||||
}
|
||||
|
||||
// Set the default to be equivalent to cmdWeb and add the default flags
|
||||
|
@ -114,10 +123,11 @@ func setFlagsAndBeforeOnSubcommands(command *cli.Command, defaultFlags []cli.Fla
|
|||
func establishCustomPath(ctx *cli.Context) error {
|
||||
var providedCustom string
|
||||
var providedConf string
|
||||
var providedWorkPath string
|
||||
|
||||
currentCtx := ctx
|
||||
for {
|
||||
if len(providedCustom) != 0 && len(providedConf) != 0 {
|
||||
if len(providedCustom) != 0 && len(providedConf) != 0 && len(providedWorkPath) != 0 {
|
||||
break
|
||||
}
|
||||
if currentCtx == nil {
|
||||
|
@ -129,10 +139,13 @@ func establishCustomPath(ctx *cli.Context) error {
|
|||
if currentCtx.IsSet("config") && len(providedConf) == 0 {
|
||||
providedConf = currentCtx.String("config")
|
||||
}
|
||||
if currentCtx.IsSet("work-path") && len(providedWorkPath) == 0 {
|
||||
providedWorkPath = currentCtx.String("work-path")
|
||||
}
|
||||
currentCtx = currentCtx.Parent()
|
||||
|
||||
}
|
||||
setting.SetCustomPathAndConf(providedCustom, providedConf)
|
||||
setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
|
||||
|
||||
setAppHelpTemplates()
|
||||
|
||||
|
@ -166,7 +179,7 @@ DEFAULT CONFIGURATION:
|
|||
`, originalTemplate, setting.CustomPath, overrided, setting.CustomConf, setting.AppPath, setting.AppWorkPath)
|
||||
}
|
||||
|
||||
func formatBuiltWith(makeTags string) string {
|
||||
func formatBuiltWith() string {
|
||||
var version = runtime.Version()
|
||||
if len(MakeVersion) > 0 {
|
||||
version = MakeVersion + ", " + runtime.Version()
|
||||
|
|
|
@ -10,13 +10,6 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var accessModes = []AccessMode{
|
||||
AccessModeRead,
|
||||
AccessModeWrite,
|
||||
AccessModeAdmin,
|
||||
AccessModeOwner,
|
||||
}
|
||||
|
||||
func TestAccessLevel(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/builder"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ActionType represents the type of an action.
|
||||
|
@ -67,7 +67,7 @@ var (
|
|||
const issueRefRegexpStr = `(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)+`
|
||||
|
||||
func assembleKeywordsPattern(words []string) string {
|
||||
return fmt.Sprintf(`(?i)(?:%s) %s`, strings.Join(words, "|"), issueRefRegexpStr)
|
||||
return fmt.Sprintf(`(?i)(?:%s)(?::?) %s`, strings.Join(words, "|"), issueRefRegexpStr)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -896,6 +896,11 @@ func mirrorSyncAction(e Engine, opType ActionType, repo *Repository, refName str
|
|||
}); err != nil {
|
||||
return fmt.Errorf("notifyWatchers: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
go HookQueue.Add(repo.ID)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -166,6 +166,7 @@ func Test_getIssueFromRef(t *testing.T) {
|
|||
{"reopen #2", 2},
|
||||
{"user2/repo2#1", 4},
|
||||
{"fixes user2/repo2#1", 4},
|
||||
{"fixes: user2/repo2#1", 4},
|
||||
} {
|
||||
issue, err := getIssueFromRef(repo, test.Ref)
|
||||
assert.NoError(t, err)
|
||||
|
@ -260,6 +261,31 @@ func TestUpdateIssuesCommit(t *testing.T) {
|
|||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestUpdateIssuesCommit_Colon(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
pushCommits := []*PushCommit{
|
||||
{
|
||||
Sha1: "abcdef2",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User Two",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User Two",
|
||||
Message: "close: #2",
|
||||
},
|
||||
}
|
||||
|
||||
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
|
||||
repo.Owner = user
|
||||
|
||||
issueBean := &Issue{RepoID: repo.ID, Index: 2}
|
||||
|
||||
AssertNotExistsBean(t, &Issue{RepoID: repo.ID, Index: 2}, "is_closed=1")
|
||||
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
|
||||
AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
|
|
|
@ -63,7 +63,7 @@ func RemoveAllWithNotice(title, path string) {
|
|||
func removeAllWithNotice(e Engine, title, path string) {
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
desc := fmt.Sprintf("%s [%s]: %v", title, path, err)
|
||||
log.Warn(desc)
|
||||
log.Warn(title+" [%s]: %v", path, err)
|
||||
if err = createNotice(e, NoticeRepository, desc); err != nil {
|
||||
log.Error("CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
const (
|
||||
// ProtectedBranchRepoID protected Repo ID
|
||||
ProtectedBranchRepoID = "GITEA_REPO_ID"
|
||||
// ProtectedBranchPRID protected Repo PR ID
|
||||
ProtectedBranchPRID = "GITEA_PR_ID"
|
||||
)
|
||||
|
||||
// ProtectedBranch struct
|
||||
|
@ -126,14 +128,14 @@ func (protectBranch *ProtectedBranch) GetGrantedApprovalsCount(pr *PullRequest)
|
|||
}
|
||||
|
||||
// GetProtectedBranchByRepoID getting protected branch by repo ID
|
||||
func GetProtectedBranchByRepoID(RepoID int64) ([]*ProtectedBranch, error) {
|
||||
func GetProtectedBranchByRepoID(repoID int64) ([]*ProtectedBranch, error) {
|
||||
protectedBranches := make([]*ProtectedBranch, 0)
|
||||
return protectedBranches, x.Where("repo_id = ?", RepoID).Desc("updated_unix").Find(&protectedBranches)
|
||||
return protectedBranches, x.Where("repo_id = ?", repoID).Desc("updated_unix").Find(&protectedBranches)
|
||||
}
|
||||
|
||||
// GetProtectedBranchBy getting protected branch by ID/Name
|
||||
func GetProtectedBranchBy(repoID int64, BranchName string) (*ProtectedBranch, error) {
|
||||
rel := &ProtectedBranch{RepoID: repoID, BranchName: BranchName}
|
||||
func GetProtectedBranchBy(repoID int64, branchName string) (*ProtectedBranch, error) {
|
||||
rel := &ProtectedBranch{RepoID: repoID, BranchName: branchName}
|
||||
has, err := x.Get(rel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -6,16 +6,14 @@ package models
|
|||
|
||||
import (
|
||||
"container/list"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
// CommitStatusState holds the state of a Status
|
||||
|
@ -61,6 +59,7 @@ type CommitStatus struct {
|
|||
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
|
||||
TargetURL string `xorm:"TEXT"`
|
||||
Description string `xorm:"TEXT"`
|
||||
ContextHash string `xorm:"char(40) index"`
|
||||
Context string `xorm:"TEXT"`
|
||||
Creator *User `xorm:"-"`
|
||||
CreatorID int64
|
||||
|
@ -87,7 +86,7 @@ func (status *CommitStatus) loadRepo(e Engine) (err error) {
|
|||
|
||||
// APIURL returns the absolute APIURL to this commit-status.
|
||||
func (status *CommitStatus) APIURL() string {
|
||||
status.loadRepo(x)
|
||||
_ = status.loadRepo(x)
|
||||
return fmt.Sprintf("%sapi/v1/%s/statuses/%s",
|
||||
setting.AppURL, status.Repo.FullName(), status.SHA)
|
||||
}
|
||||
|
@ -95,7 +94,7 @@ func (status *CommitStatus) APIURL() string {
|
|||
// APIFormat assumes some fields assigned with values:
|
||||
// Required - Repo, Creator
|
||||
func (status *CommitStatus) APIFormat() *api.Status {
|
||||
status.loadRepo(x)
|
||||
_ = status.loadRepo(x)
|
||||
apiStatus := &api.Status{
|
||||
Created: status.CreatedUnix.AsTime(),
|
||||
Updated: status.CreatedUnix.AsTime(),
|
||||
|
@ -146,7 +145,7 @@ func GetLatestCommitStatus(repo *Repository, sha string, page int) ([]*CommitSta
|
|||
Table(&CommitStatus{}).
|
||||
Where("repo_id = ?", repo.ID).And("sha = ?", sha).
|
||||
Select("max( id ) as id").
|
||||
GroupBy("context").OrderBy("max( id ) desc").Find(&ids)
|
||||
GroupBy("context_hash").OrderBy("max( id ) desc").Find(&ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -157,27 +156,6 @@ func GetLatestCommitStatus(repo *Repository, sha string, page int) ([]*CommitSta
|
|||
return statuses, x.In("id", ids).Find(&statuses)
|
||||
}
|
||||
|
||||
// GetCommitStatus populates a given status for a given commit.
|
||||
// NOTE: If ID or Index isn't given, and only Context, TargetURL and/or Description
|
||||
// is given, the CommitStatus created _last_ will be returned.
|
||||
func GetCommitStatus(repo *Repository, sha string, status *CommitStatus) (*CommitStatus, error) {
|
||||
conds := &CommitStatus{
|
||||
Context: status.Context,
|
||||
State: status.State,
|
||||
TargetURL: status.TargetURL,
|
||||
Description: status.Description,
|
||||
}
|
||||
has, err := x.Where("repo_id = ?", repo.ID).And("sha = ?", sha).Desc("created_unix").Get(conds)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetCommitStatus[%s, %s]: %v", repo.RepoPath(), sha, err)
|
||||
}
|
||||
if !has {
|
||||
return nil, fmt.Errorf("GetCommitStatus[%s, %s]: not found", repo.RepoPath(), sha)
|
||||
}
|
||||
|
||||
return conds, nil
|
||||
}
|
||||
|
||||
// NewCommitStatusOptions holds options for creating a CommitStatus
|
||||
type NewCommitStatusOptions struct {
|
||||
Repo *Repository
|
||||
|
@ -186,30 +164,30 @@ type NewCommitStatusOptions struct {
|
|||
CommitStatus *CommitStatus
|
||||
}
|
||||
|
||||
func newCommitStatus(sess *xorm.Session, opts NewCommitStatusOptions) error {
|
||||
// NewCommitStatus save commit statuses into database
|
||||
func NewCommitStatus(opts NewCommitStatusOptions) error {
|
||||
if opts.Repo == nil {
|
||||
return fmt.Errorf("NewCommitStatus[nil, %s]: no repository specified", opts.SHA)
|
||||
}
|
||||
|
||||
repoPath := opts.Repo.RepoPath()
|
||||
if opts.Creator == nil {
|
||||
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %v", opts.Repo.ID, opts.Creator.ID, opts.SHA, err)
|
||||
}
|
||||
|
||||
opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
|
||||
opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
|
||||
opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
|
||||
opts.CommitStatus.SHA = opts.SHA
|
||||
opts.CommitStatus.CreatorID = opts.Creator.ID
|
||||
|
||||
if opts.Repo == nil {
|
||||
return fmt.Errorf("newCommitStatus[nil, %s]: no repository specified", opts.SHA)
|
||||
}
|
||||
opts.CommitStatus.RepoID = opts.Repo.ID
|
||||
repoPath := opts.Repo.repoPath(sess)
|
||||
|
||||
if opts.Creator == nil {
|
||||
return fmt.Errorf("newCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository[%s]: %v", repoPath, err)
|
||||
}
|
||||
if _, err := gitRepo.GetCommit(opts.SHA); err != nil {
|
||||
return fmt.Errorf("GetCommit[%s]: %v", opts.SHA, err)
|
||||
}
|
||||
|
||||
// Get the next Status Index
|
||||
var nextIndex int64
|
||||
|
@ -219,43 +197,26 @@ func newCommitStatus(sess *xorm.Session, opts NewCommitStatusOptions) error {
|
|||
}
|
||||
has, err := sess.Desc("index").Limit(1).Get(lastCommitStatus)
|
||||
if err != nil {
|
||||
sess.Rollback()
|
||||
return fmt.Errorf("newCommitStatus[%s, %s]: %v", repoPath, opts.SHA, err)
|
||||
if err := sess.Rollback(); err != nil {
|
||||
log.Error("NewCommitStatus: sess.Rollback: %v", err)
|
||||
}
|
||||
return fmt.Errorf("NewCommitStatus[%s, %s]: %v", repoPath, opts.SHA, err)
|
||||
}
|
||||
if has {
|
||||
log.Debug("newCommitStatus[%s, %s]: found", repoPath, opts.SHA)
|
||||
log.Debug("NewCommitStatus[%s, %s]: found", repoPath, opts.SHA)
|
||||
nextIndex = lastCommitStatus.Index
|
||||
}
|
||||
opts.CommitStatus.Index = nextIndex + 1
|
||||
log.Debug("newCommitStatus[%s, %s]: %d", repoPath, opts.SHA, opts.CommitStatus.Index)
|
||||
log.Debug("NewCommitStatus[%s, %s]: %d", repoPath, opts.SHA, opts.CommitStatus.Index)
|
||||
|
||||
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
|
||||
|
||||
// Insert new CommitStatus
|
||||
if _, err = sess.Insert(opts.CommitStatus); err != nil {
|
||||
sess.Rollback()
|
||||
return fmt.Errorf("newCommitStatus[%s, %s]: %v", repoPath, opts.SHA, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCommitStatus creates a new CommitStatus given a bunch of parameters
|
||||
// NOTE: All text-values will be trimmed from whitespaces.
|
||||
// Requires: Repo, Creator, SHA
|
||||
func NewCommitStatus(repo *Repository, creator *User, sha string, status *CommitStatus) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := sess.Begin(); err != nil {
|
||||
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %v", repo.ID, creator.ID, sha, err)
|
||||
}
|
||||
|
||||
if err := newCommitStatus(sess, NewCommitStatusOptions{
|
||||
Repo: repo,
|
||||
Creator: creator,
|
||||
SHA: sha,
|
||||
CommitStatus: status,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %v", repo.ID, creator.ID, sha, err)
|
||||
if err := sess.Rollback(); err != nil {
|
||||
log.Error("Insert CommitStatus: sess.Rollback: %v", err)
|
||||
}
|
||||
return fmt.Errorf("Insert CommitStatus[%s, %s]: %v", repoPath, opts.SHA, err)
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
|
@ -291,3 +252,8 @@ func ParseCommitsWithStatus(oldCommits *list.List, repo *Repository) *list.List
|
|||
}
|
||||
return newCommits
|
||||
}
|
||||
|
||||
// hashCommitStatusContext hash context
|
||||
func hashCommitStatusContext(context string) string {
|
||||
return fmt.Sprintf("%x", sha1.Sum([]byte(context)))
|
||||
}
|
|
@ -39,7 +39,7 @@ func CheckConsistencyFor(t *testing.T, beansToCheck ...interface{}) {
|
|||
ptrToSliceValue := reflect.New(sliceType)
|
||||
ptrToSliceValue.Elem().Set(sliceValue)
|
||||
|
||||
assert.NoError(t, x.Where(bean).Find(ptrToSliceValue.Interface()))
|
||||
assert.NoError(t, x.Table(bean).Find(ptrToSliceValue.Interface()))
|
||||
sliceValue = ptrToSliceValue.Elem()
|
||||
|
||||
for i := 0; i < sliceValue.Len(); i++ {
|
||||
|
|
27
models/convert.go
Normal file
27
models/convert.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ConvertUtf8ToUtf8mb4 converts database and tables from utf8 to utf8mb4 if it's mysql
|
||||
func ConvertUtf8ToUtf8mb4() error {
|
||||
_, err := x.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci", DbCfg.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tables, err := x.DBMetas()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, table := range tables {
|
||||
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;", table.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -674,6 +674,23 @@ func (err ErrRepoAlreadyExist) Error() string {
|
|||
return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name)
|
||||
}
|
||||
|
||||
// ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error.
|
||||
type ErrForkAlreadyExist struct {
|
||||
Uname string
|
||||
RepoName string
|
||||
ForkName string
|
||||
}
|
||||
|
||||
// IsErrForkAlreadyExist checks if an error is an ErrForkAlreadyExist.
|
||||
func IsErrForkAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrForkAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrForkAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("repository is already forked by user [uname: %s, repo path: %s, fork path: %s]", err.Uname, err.RepoName, err.ForkName)
|
||||
}
|
||||
|
||||
// ErrRepoRedirectNotExist represents a "RepoRedirectNotExist" kind of error.
|
||||
type ErrRepoRedirectNotExist struct {
|
||||
OwnerID int64
|
||||
|
|
|
@ -9,3 +9,9 @@
|
|||
repo_id: 4
|
||||
user_id: 4
|
||||
mode: 2 # write
|
||||
|
||||
-
|
||||
id: 3
|
||||
repo_id: 40
|
||||
user_id: 4
|
||||
mode: 2 # write
|
|
@ -86,3 +86,14 @@
|
|||
created_unix: 946684830
|
||||
updated_unix: 978307200
|
||||
|
||||
-
|
||||
id: 8
|
||||
repo_id: 10
|
||||
index: 1
|
||||
poster_id: 11
|
||||
name: pr2
|
||||
content: a pull request
|
||||
is_closed: false
|
||||
is_pull: true
|
||||
created_unix: 946684820
|
||||
updated_unix: 978307180
|
|
@ -13,3 +13,11 @@
|
|||
content: content2
|
||||
is_closed: false
|
||||
num_issues: 0
|
||||
|
||||
-
|
||||
id: 3
|
||||
repo_id: 1
|
||||
name: milestone3
|
||||
content: content3
|
||||
is_closed: true
|
||||
num_issues: 0
|
||||
|
|
|
@ -26,3 +26,17 @@
|
|||
base_branch: master
|
||||
merge_base: fedcba9876543210
|
||||
has_merged: false
|
||||
|
||||
-
|
||||
id: 3
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
issue_id: 8
|
||||
index: 1
|
||||
head_repo_id: 11
|
||||
base_repo_id: 10
|
||||
head_user_name: user13
|
||||
head_branch: branch2
|
||||
base_branch: master
|
||||
merge_base: 0abcb056019adb83
|
||||
has_merged: false
|
|
@ -8,7 +8,8 @@
|
|||
num_closed_issues: 1
|
||||
num_pulls: 2
|
||||
num_closed_pulls: 0
|
||||
num_milestones: 2
|
||||
num_milestones: 3
|
||||
num_closed_milestones: 1
|
||||
num_watches: 3
|
||||
|
||||
-
|
||||
|
@ -117,7 +118,7 @@
|
|||
is_private: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
num_pulls: 0
|
||||
num_pulls: 1
|
||||
num_closed_pulls: 0
|
||||
is_mirror: false
|
||||
num_forks: 1
|
||||
|
|
|
@ -80,6 +80,22 @@ func (d *DiffLine) GetCommentSide() string {
|
|||
return d.Comments[0].DiffSide()
|
||||
}
|
||||
|
||||
// GetLineTypeMarker returns the line type marker
|
||||
func (d *DiffLine) GetLineTypeMarker() string {
|
||||
if strings.IndexByte(" +-", d.Content[0]) > -1 {
|
||||
return d.Content[0:1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// escape a line's content or return <br> needed for copy/paste purposes
|
||||
func getLineContent(content string) string {
|
||||
if len(content) > 0 {
|
||||
return html.EscapeString(content)
|
||||
}
|
||||
return "<br>"
|
||||
}
|
||||
|
||||
// DiffSection represents a section of a DiffFile.
|
||||
type DiffSection struct {
|
||||
Name string
|
||||
|
@ -87,34 +103,26 @@ type DiffSection struct {
|
|||
}
|
||||
|
||||
var (
|
||||
addedCodePrefix = []byte("<span class=\"added-code\">")
|
||||
removedCodePrefix = []byte("<span class=\"removed-code\">")
|
||||
codeTagSuffix = []byte("</span>")
|
||||
addedCodePrefix = []byte(`<span class="added-code">`)
|
||||
removedCodePrefix = []byte(`<span class="removed-code">`)
|
||||
codeTagSuffix = []byte(`</span>`)
|
||||
)
|
||||
|
||||
func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
// Reproduce signs which are cut for inline diff before.
|
||||
switch lineType {
|
||||
case DiffLineAdd:
|
||||
buf.WriteByte('+')
|
||||
case DiffLineDel:
|
||||
buf.WriteByte('-')
|
||||
}
|
||||
|
||||
for i := range diffs {
|
||||
switch {
|
||||
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
|
||||
buf.Write(addedCodePrefix)
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.WriteString(getLineContent(diffs[i].Text))
|
||||
buf.Write(codeTagSuffix)
|
||||
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
|
||||
buf.Write(removedCodePrefix)
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.WriteString(getLineContent(diffs[i].Text))
|
||||
buf.Write(codeTagSuffix)
|
||||
case diffs[i].Type == diffmatchpatch.DiffEqual:
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.WriteString(getLineContent(diffs[i].Text))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,7 +181,7 @@ func init() {
|
|||
// GetComputedInlineDiffFor computes inline diff for the given line.
|
||||
func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
|
||||
if setting.Git.DisableDiffHighlight {
|
||||
return template.HTML(html.EscapeString(diffLine.Content[1:]))
|
||||
return template.HTML(getLineContent(diffLine.Content[1:]))
|
||||
}
|
||||
var (
|
||||
compareDiffLine *DiffLine
|
||||
|
@ -186,19 +194,22 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem
|
|||
case DiffLineAdd:
|
||||
compareDiffLine = diffSection.GetLine(DiffLineDel, diffLine.RightIdx)
|
||||
if compareDiffLine == nil {
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
return template.HTML(getLineContent(diffLine.Content[1:]))
|
||||
}
|
||||
diff1 = compareDiffLine.Content
|
||||
diff2 = diffLine.Content
|
||||
case DiffLineDel:
|
||||
compareDiffLine = diffSection.GetLine(DiffLineAdd, diffLine.LeftIdx)
|
||||
if compareDiffLine == nil {
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
return template.HTML(getLineContent(diffLine.Content[1:]))
|
||||
}
|
||||
diff1 = diffLine.Content
|
||||
diff2 = compareDiffLine.Content
|
||||
default:
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
if strings.IndexByte(" +-", diffLine.Content[0]) > -1 {
|
||||
return template.HTML(getLineContent(diffLine.Content[1:]))
|
||||
}
|
||||
return template.HTML(getLineContent(diffLine.Content))
|
||||
}
|
||||
|
||||
diffRecord := diffMatchPatch.DiffMain(diff1[1:], diff2[1:], true)
|
||||
|
@ -384,13 +395,9 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
|
|||
// headers + hunk header
|
||||
newHunk := make([]string, headerLines)
|
||||
// transfer existing headers
|
||||
for idx, lof := range hunk[:headerLines] {
|
||||
newHunk[idx] = lof
|
||||
}
|
||||
copy(newHunk, hunk[:headerLines])
|
||||
// transfer last n lines
|
||||
for _, lof := range hunk[len(hunk)-numbersOfLine-1:] {
|
||||
newHunk = append(newHunk, lof)
|
||||
}
|
||||
newHunk = append(newHunk, hunk[len(hunk)-numbersOfLine-1:]...)
|
||||
// calculate newBegin, ... by counting lines
|
||||
for i := len(hunk) - 1; i >= len(hunk)-numbersOfLine; i-- {
|
||||
switch hunk[i][0] {
|
||||
|
@ -582,7 +589,10 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
|
|||
diff.Files = append(diff.Files, curFile)
|
||||
if len(diff.Files) >= maxFiles {
|
||||
diff.IsIncomplete = true
|
||||
io.Copy(ioutil.Discard, reader)
|
||||
_, err := io.Copy(ioutil.Discard, reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Copy: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
curFileLinesCount = 0
|
||||
|
@ -673,7 +683,7 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
|
|||
|
||||
var cmd *exec.Cmd
|
||||
if len(beforeCommitID) == 0 && commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", "show", afterCommitID)
|
||||
cmd = exec.Command(git.GitExecutable, "show", afterCommitID)
|
||||
} else {
|
||||
actualBeforeCommitID := beforeCommitID
|
||||
if len(actualBeforeCommitID) == 0 {
|
||||
|
@ -686,7 +696,7 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
|
|||
}
|
||||
diffArgs = append(diffArgs, actualBeforeCommitID)
|
||||
diffArgs = append(diffArgs, afterCommitID)
|
||||
cmd = exec.Command("git", diffArgs...)
|
||||
cmd = exec.Command(git.GitExecutable, diffArgs...)
|
||||
}
|
||||
cmd.Dir = repoPath
|
||||
cmd.Stderr = os.Stderr
|
||||
|
@ -750,23 +760,23 @@ func GetRawDiffForFile(repoPath, startCommit, endCommit string, diffType RawDiff
|
|||
switch diffType {
|
||||
case RawDiffNormal:
|
||||
if len(startCommit) != 0 {
|
||||
cmd = exec.Command("git", append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...)...)
|
||||
cmd = exec.Command(git.GitExecutable, append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...)...)
|
||||
} else if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", append([]string{"show", endCommit}, fileArgs...)...)
|
||||
cmd = exec.Command(git.GitExecutable, append([]string{"show", endCommit}, fileArgs...)...)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
cmd = exec.Command("git", append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...)...)
|
||||
cmd = exec.Command(git.GitExecutable, append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...)...)
|
||||
}
|
||||
case RawDiffPatch:
|
||||
if len(startCommit) != 0 {
|
||||
query := fmt.Sprintf("%s...%s", endCommit, startCommit)
|
||||
cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...)...)
|
||||
cmd = exec.Command(git.GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...)...)
|
||||
} else if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...)...)
|
||||
cmd = exec.Command(git.GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...)...)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
query := fmt.Sprintf("%s...%s", endCommit, c.ID.String())
|
||||
cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...)...)
|
||||
cmd = exec.Command(git.GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...)...)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid diffType: %s", diffType)
|
||||
|
|
|
@ -17,21 +17,15 @@ func assertEqual(t *testing.T, s1 string, s2 template.HTML) {
|
|||
}
|
||||
}
|
||||
|
||||
func assertLineEqual(t *testing.T, d1 *DiffLine, d2 *DiffLine) {
|
||||
if d1 != d2 {
|
||||
t.Errorf("%v should be equal %v", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiffToHTML(t *testing.T) {
|
||||
assertEqual(t, "+foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
assertEqual(t, "foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
{Type: dmp.DiffEqual, Text: "foo "},
|
||||
{Type: dmp.DiffInsert, Text: "bar"},
|
||||
{Type: dmp.DiffDelete, Text: " baz"},
|
||||
{Type: dmp.DiffEqual, Text: " biz"},
|
||||
}, DiffLineAdd))
|
||||
|
||||
assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
assertEqual(t, "foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
{Type: dmp.DiffEqual, Text: "foo "},
|
||||
{Type: dmp.DiffDelete, Text: "bar"},
|
||||
{Type: dmp.DiffInsert, Text: " baz"},
|
||||
|
|
|
@ -12,24 +12,33 @@ import (
|
|||
|
||||
// PushingEnvironment returns an os environment to allow hooks to work on push
|
||||
func PushingEnvironment(doer *User, repo *Repository) []string {
|
||||
return FullPushingEnvironment(doer, doer, repo, 0)
|
||||
}
|
||||
|
||||
// FullPushingEnvironment returns an os environment to allow hooks to work on push
|
||||
func FullPushingEnvironment(author, committer *User, repo *Repository, prID int64) []string {
|
||||
isWiki := "false"
|
||||
if strings.HasSuffix(repo.Name, ".wiki") {
|
||||
isWiki = "true"
|
||||
}
|
||||
|
||||
sig := doer.NewGitSig()
|
||||
authorSig := author.NewGitSig()
|
||||
committerSig := committer.NewGitSig()
|
||||
|
||||
// We should add "SSH_ORIGINAL_COMMAND=gitea-internal",
|
||||
// once we have hook and pushing infrastructure working correctly
|
||||
return append(os.Environ(),
|
||||
"GIT_AUTHOR_NAME="+sig.Name,
|
||||
"GIT_AUTHOR_EMAIL="+sig.Email,
|
||||
"GIT_COMMITTER_NAME="+sig.Name,
|
||||
"GIT_COMMITTER_EMAIL="+sig.Email,
|
||||
"GIT_AUTHOR_NAME="+authorSig.Name,
|
||||
"GIT_AUTHOR_EMAIL="+authorSig.Email,
|
||||
"GIT_COMMITTER_NAME="+committerSig.Name,
|
||||
"GIT_COMMITTER_EMAIL="+committerSig.Email,
|
||||
EnvRepoName+"="+repo.Name,
|
||||
EnvRepoUsername+"="+repo.OwnerName,
|
||||
EnvRepoUsername+"="+repo.MustOwnerName(),
|
||||
EnvRepoIsWiki+"="+isWiki,
|
||||
EnvPusherName+"="+doer.Name,
|
||||
EnvPusherID+"="+fmt.Sprintf("%d", doer.ID),
|
||||
EnvPusherName+"="+committer.Name,
|
||||
EnvPusherID+"="+fmt.Sprintf("%d", committer.ID),
|
||||
ProtectedBranchRepoID+"="+fmt.Sprintf("%d", repo.ID),
|
||||
ProtectedBranchPRID+"="+fmt.Sprintf("%d", prID),
|
||||
"SSH_ORIGINAL_COMMAND=gitea-internal",
|
||||
)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
|
@ -18,33 +19,35 @@ import (
|
|||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/builder"
|
||||
"github.com/go-xorm/xorm"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// Issue represents an issue or pull request of repository.
|
||||
type Issue struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"`
|
||||
Repo *Repository `xorm:"-"`
|
||||
Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository.
|
||||
PosterID int64 `xorm:"INDEX"`
|
||||
Poster *User `xorm:"-"`
|
||||
Title string `xorm:"name"`
|
||||
Content string `xorm:"TEXT"`
|
||||
RenderedContent string `xorm:"-"`
|
||||
Labels []*Label `xorm:"-"`
|
||||
MilestoneID int64 `xorm:"INDEX"`
|
||||
Milestone *Milestone `xorm:"-"`
|
||||
Priority int
|
||||
AssigneeID int64 `xorm:"-"`
|
||||
Assignee *User `xorm:"-"`
|
||||
IsClosed bool `xorm:"INDEX"`
|
||||
IsRead bool `xorm:"-"`
|
||||
IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not.
|
||||
PullRequest *PullRequest `xorm:"-"`
|
||||
NumComments int
|
||||
Ref string
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"`
|
||||
Repo *Repository `xorm:"-"`
|
||||
Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository.
|
||||
PosterID int64 `xorm:"INDEX"`
|
||||
Poster *User `xorm:"-"`
|
||||
OriginalAuthor string
|
||||
OriginalAuthorID int64
|
||||
Title string `xorm:"name"`
|
||||
Content string `xorm:"TEXT"`
|
||||
RenderedContent string `xorm:"-"`
|
||||
Labels []*Label `xorm:"-"`
|
||||
MilestoneID int64 `xorm:"INDEX"`
|
||||
Milestone *Milestone `xorm:"-"`
|
||||
Priority int
|
||||
AssigneeID int64 `xorm:"-"`
|
||||
Assignee *User `xorm:"-"`
|
||||
IsClosed bool `xorm:"INDEX"`
|
||||
IsRead bool `xorm:"-"`
|
||||
IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not.
|
||||
PullRequest *PullRequest `xorm:"-"`
|
||||
NumComments int
|
||||
Ref string
|
||||
|
||||
DeadlineUnix util.TimeStamp `xorm:"INDEX"`
|
||||
|
||||
|
@ -1015,9 +1018,35 @@ type NewIssueOptions struct {
|
|||
IsPull bool
|
||||
}
|
||||
|
||||
// GetMaxIndexOfIssue returns the max index on issue
|
||||
func GetMaxIndexOfIssue(repoID int64) (int64, error) {
|
||||
return getMaxIndexOfIssue(x, repoID)
|
||||
}
|
||||
|
||||
func getMaxIndexOfIssue(e Engine, repoID int64) (int64, error) {
|
||||
var (
|
||||
maxIndex int64
|
||||
has bool
|
||||
err error
|
||||
)
|
||||
|
||||
has, err = e.SQL("SELECT COALESCE((SELECT MAX(`index`) FROM issue WHERE repo_id = ?),0)", repoID).Get(&maxIndex)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if !has {
|
||||
return 0, errors.New("Retrieve Max index from issue failed")
|
||||
}
|
||||
return maxIndex, nil
|
||||
}
|
||||
|
||||
func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
|
||||
opts.Issue.Title = strings.TrimSpace(opts.Issue.Title)
|
||||
opts.Issue.Index = opts.Repo.NextIssueIndex()
|
||||
|
||||
maxIndex, err := getMaxIndexOfIssue(e, opts.Issue.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.Issue.Index = maxIndex + 1
|
||||
|
||||
if opts.Issue.MilestoneID > 0 {
|
||||
milestone, err := getMilestoneByRepoID(e, opts.Issue.RepoID, opts.Issue.MilestoneID)
|
||||
|
@ -1303,7 +1332,7 @@ func sortIssuesSession(sess *xorm.Session, sortType string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (opts *IssuesOptions) setupSession(sess *xorm.Session) error {
|
||||
func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
|
||||
if opts.Page >= 0 && opts.PageSize > 0 {
|
||||
var start int
|
||||
if opts.Page == 0 {
|
||||
|
@ -1362,7 +1391,6 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) error {
|
|||
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CountIssuesByRepo map from repoID to number of issues matching the options
|
||||
|
@ -1370,9 +1398,7 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
|
|||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := opts.setupSession(sess); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.setupSession(sess)
|
||||
|
||||
countsSlice := make([]*struct {
|
||||
RepoID int64
|
||||
|
@ -1397,15 +1423,14 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
|
|||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err := opts.setupSession(sess); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.setupSession(sess)
|
||||
sortIssuesSession(sess, opts.SortType)
|
||||
|
||||
issues := make([]*Issue, 0, setting.UI.IssuePagingNum)
|
||||
if err := sess.Find(&issues); err != nil {
|
||||
return nil, fmt.Errorf("Find: %v", err)
|
||||
}
|
||||
sess.Close()
|
||||
|
||||
if err := IssueList(issues).LoadAttributes(); err != nil {
|
||||
return nil, fmt.Errorf("LoadAttributes: %v", err)
|
||||
|
|
|
@ -43,8 +43,7 @@ func TestUpdateAssignee(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
var expectedAssignees []*User
|
||||
expectedAssignees = append(expectedAssignees, user2)
|
||||
expectedAssignees = append(expectedAssignees, user3)
|
||||
expectedAssignees = append(expectedAssignees, user2, user3)
|
||||
|
||||
for in, assignee := range assignees {
|
||||
assert.Equal(t, assignee.ID, expectedAssignees[in].ID)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue