# Conflicts:
#	models/migrations/v51.go
This commit is contained in:
kolaente 2017-12-19 19:07:30 +01:00 committed by Konrad
commit 172b1f054a
No known key found for this signature in database
GPG Key ID: F40E70337AB24C9B
192 changed files with 5102 additions and 1430 deletions

View File

@ -55,6 +55,16 @@ pipeline:
when:
event: [ push, tag, pull_request ]
build-without-gcc:
image: webhippie/golang:edge
pull: true
environment:
GOPATH: /srv/app
commands:
- go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
when:
event: [ push, tag, pull_request ]
build:
image: webhippie/golang:edge
pull: true
@ -86,6 +96,19 @@ pipeline:
event: [ push, pull_request ]
branch: [ master ]
test:
image: webhippie/golang:edge
pull: true
group: test
environment:
TAGS: bindata sqlite
GOPATH: /srv/app
commands:
- make test
when:
event: [ push, pull_request ]
branch: [ release/* ]
test:
image: webhippie/golang:edge
pull: true

View File

@ -1,5 +1,242 @@
# Changelog
## [1.3.1](https://github.com/go-gitea/gitea/releases/tag/v1.3.1) - 2017-12-08
* BUGFIXES
* Sanitize logs for mirror sync (#3057, #3082) (#3078)
* Fix missing branch in release bug (#3108) (#3117)
* Fix repo indexer and submodule bug (#3107) (#3110)
* Fix legacy URL redirects (#3100) (#3106)
* Fix redis session failed (#3086) (#3089)
* Fix issue list branch link broken (#3061) (#3070)
* Fix missing password length check when change password (#3039) (#3071)
## [1.3.0](https://github.com/go-gitea/gitea/releases/tag/v1.3.0) - 2017-11-29
* BREAKING
* Make URL scheme unambiguous (#2408)
* FEATURE
* Add branch overiew page (#2108)
* Code/repo search (#2582)
* Add Activity page to repository (#2674)
* Issue Timetracking (#2211)
* Add orgmode document type on file view and readme (#2525)
* Add external markup render support (#2570)
* Implementation of discord webhook (#2402)
* Webhooks for repo creation/deletion (#1663)
* Complete push webhooks (#2530)
* Add possibility to record branch information in an issue (#780)
* Create new branch from branch selection dropdown (#2130)
* Implementation of all repositories of a user from user->settings (#1740)
* Add LFS object verification step after upload (#2868)
* Configurable SSH cipher suite (#913)
* Disable custom Git Hooks globally via configuration file (#2450)
* Sync releases table with tags on push and for mirrors (#2459)
* BUGFIXES
* Fix label comments for French locale (#3017)
* Remove duplicate "Max Diff Lines" from config view (#3001)
* Fix over-escaped characters (#2992)
* Fix go-get, src and raw urls to new scheme (#2986)
* Fix error when add user has full name to team (#2975)
* Fix files/commits of merged PRs (#2970)
* Update golang x/crypto dependencies - Fix SSH transport fail (#2951)
* Fix memcache support when value is returned as string always (#2950)
* Fix issue link rendering in commit messages (#2897)
* Fix adding a new authentication source after selecting OAuth (#2889)
* Fix new branch creation to new url scheme (#2884)
* Allow spaces in username for LDAP users (#2880)
* Fix LFS not returning correct content length when requesting a range … (#2864)
* Fix fork repository cycle to self (#2860)
* Fix click create pull request button 404 (#2859)
* Fix API raw file content access for default branch (#2849)
* Clean repository ROOT directory name with filepath.Clean (#2846)
* Fix API raw requests for commits and tags (#2841)
* Fix order of comments (#2835)
* Issue content should not be updated when closing with comment (#2833)
* Fix ordering in app.ini and fix run mode option (#2829)
* Fix redirect url of legacy commits route (#2825)
* Fix commits page url (#2823)
* Fix wrong translations (#2818)
* Fix dropdown menu position when explore repos (#2808)
* Fix Git LFS object/repo link storage in database and small refactoring (#2803)
* Use relative URLs for avatars on the dashboard (#2800)
* Add checks for commits with missing author and time (#2771)
* Fix emojify image URL (#2769)
* Hide unactive on explore users and some refactors (#2741)
* Fix IE unsupported javascript construction in branch dropdown (#2736)
* Only update mirror last update after successful sync (#2730)
* Fix semantic-ui style conflict with v-cloak (#2722)
* Fixing wrong translation on sort type oldest/latest (#2720)
* Fix PR, milestone and label functionality if issue unit is disabled (#2710)
* Fix plain readme didn't render correctly on repo home page (#2705)
* Fix organization removal from watch table migration (#2703)
* Fix repository search function (#2689)
* fix panic on gogs webhook creation (#2675)
* Fix orgnization user watch repository (#2670)
* GPG key email verification no longer case sensitive (#2661) (#2663)
* Fix index column deletion (#2651)
* table `pull_request` wasn't updated correctly (#2649)
* Fix go get response if only app URL is custom in configuration (#2634)
* Fix doubled issue tab introduced in migration v16 (#2611)
* Rewrite migrations to not depend on future code changes (#2604)
* Fix implementation of repo Home func (#2601)
* Fix translation upload to crowdin (#2599)
* Reduce usage of allcols on update (#2596)
* fix go get subpackage bug (#2584)
* Fix broken migration to add can_push field back to table (#2574)
* fix readme view bug (#2566)
* Fix sending mail with a non-latin display name. #2102 (#2559)
* Restricting access to fork functioanlity to users with Code access (#2534)
* fix updated update on public key (#2514)
* Added bucket name to s3 drone plugin (#2505)
* fixes 500 error on dashboard when using MSSQL (#2504)
* fix wrong rendering of commit detail page (#2503)
* Hotfix: Add time manually adds time in nanoseconds (#2499)
* Remove repository mirrors from "collaborative" list (#2497)
* fix release failed since the wrong token name (#2496)
* Fix slice out of bounds error in mailer (#2479)
* Fix #2470 (#2477)
* fix orgnization webhooks (#2422)
* fix webhook test (#2415)
* fix missing orgnization discord webhook (#2414)
* Fix route handler order (#2409)
* Prevent sending emails and notifications to inactive users (#2384)
* Move themes to plugin directory. Fixes #2372 (#2375)
* fix duplicated feed (#2370)
* Fix missing collabrative repos (#2367)
* Only check at least one email gpg key (#2266)
* don't check minimum key size when disabled (#1754)
* Fix run command race (#1470)
* fix .netrc authentication (#2700)
* Fix so that user can still fork his own repository to his organizations (#2699)
* Fix can_push value to false in protected_branch (#2560)
* Fix copy in email templates (#2801)
* Fix inconsistencies in user settings UI (#2901)
* Fix attachments icon size on zoom in/out (#2853)
* Fix ignored errors in API route (#2850)
* Fix activity css conflit with semantic ui (#2758)
* Fix notifications tabs according to semantic-ui docs (#2733)
* Fix typos in app.ini (#2732)
* Fix duplicated rel attribute (#2549)
* Fix tests code to prevent some runtime errors (#2381)
* ENHANCEMENT
* Memory usage improvements and lower minimal git requirement to 1.7.2 (#3013) (#3028)
* Set OpenID support on by default when installing new instance (#3010) (#3027)
* Use api.TrackedTime in API (#2807)
* Configurable SSH key exchange algorithm and MAC suite (#2806)
* Add Safari pinned tab icon (#2799)
* Improve force push detect when push (#2798)
* Add wrapping to long diff lines (#2789)
* Link members and repositories count to each page on org home. (#2787)
* Show Sendmail settings on admin config page (#2782)
* Add commit count caching (#2774)
* Use identicon image for default gravatar. (#2767)
* Add default ssh ciphers (#2761)
* Remove manual of unsupported option (#2757)
* Add search mode option to /api/repo/search (#2756)
* Move swagger-ui under /api/v1 (#2746)
* Add support for extra sendmail arguments (#2731)
* Use buffersize to reduce database connection when iterate (#2724)
* Render plain text README.txt monospaced (#2721)
* Integration test for activity page (#2704)
* Merge password and 2fa page on user settings (#2695)
* Allow custom SSH user in UI for built-in SSH server (#2617) (#2678)
* Refactor duplicated code in repo handlers (#2657)
* Replace deprecated Id method with ID (#2655)
* Remove redudant functions and code (#2652)
* hide navbar when only 1 sign-in method is available (#2444) (#2648)
* Change default sort order (#2647)
* Change pull description text (#2075) (#2646)
* Remove direct user adding to organization members (#2641)
* Use session when creating user (#2638)
* Use Semantic UI's Search component for user and repo search (#2636)
* Use AfterLoad instead of AfterSet on Structs (#2628)
* Remove redudant CheckUnit calls in router (#2627)
* Remove repo unit index (#2621)
* Remove redudant issue LoadAttributes() calls (#2614)
* Make indexer code more reusable (#2590)
* Use custom type and constants to hold available order by options (#2572)
* Use named ActionType constants in template helper (#2545)
* Make basic functionality work without JavaScript (#2541)
* Ctrl + Enter to submit forms (#2540)
* Automatically regenerate indexer for incompatible versions (#2524)
* Set default lfs content path to data/lfs (#2521)
* Convert spaces to tabs in footer.tmpl (#2520)
* Sort repository tree entries in natural way (#2506)
* Open external wiki in new window (#2489)
* Use created & updated instead BeforeInsert & BeforeUpdate (#2482)
* Hide branch on pull request view or create UI (#2454)
* improve protected branch to add whitelist support (#2451)
* some refactors for issue and comments (#2419)
* Restructure markup & markdown to prepare for multiple markup language… (#2411)
* Improve issue search (#2387)
* Add UseCompatSSHURI setting (#2356)
* Use custom search for each filter type in dashboard (#2343)
* Failed authentication are now properly logged (#2334)
* Add environment variable support for Docker image (#2201)
* Set session and indexers' data files rel to AppDataPath (#2192)
* Display commit status on landing page of repo (#1784)
* TESTING
* Add integration test for logging out (#2892)
* Integration test for user deleting account (#2891)
* Use different directories for session files in integration tests (#2834)
* Add deleted_branch table fixture (#2832)
* Include HTTP method in test error message (#2815)
* Add repository search unit and integration tests (#2575)
* Expand fixtures (#2571)
* Fix /api/repo/search integration tests (#2550)
* Make integration tests more user-friendly (#2536)
* Fix unit test race condition (#2516)
* Add missing fixture to clean gpg_key table (#2494)
* Hotfix for integration testing (#2473)
* Make repo private to not interfere with other tests (#2467)
* Error message for integration test (#2410)
* Fix "index out of range" runtime error in repo_list tests (#2376)
* Add git clone test on integration test (#1682)
* TRANSLATION
* Fix localization texts that contain semicolon (#2900)
* Fix activity locale (#2709)
* Update translation from crowdin (#2368)
* BUILD
* change the email and name to GitBot account. (#2848)
* Fix removing backslash before quotes in translations (#2831)
* add gitea remote in drone. (#2817)
* add remote name for git push. (#2816)
* Launch Gitea with custom UID/GID for 'git' user (fixes #2286) (#2791)
* Download and pushing translations (#2727)
* Automatic update of translations (#2585)
* Add pre-build step for nodejs stuff (#2581)
* Compress css with nodejs (#2580)
* Remove go version check for make fmt (#2558)
* Fix lint errors (#2547)
* Always run fmt check in CI (#2546)
* Fix fmt errors (#2544)
* add codecov.io service. (#2493)
* Fix some tests : make coverage -> test (#2492)
* Fix fmt error in mailer (#2490)
* Allow changing integration test database connection using env variables (#2484)
* Add changelog config file for generate changelog (#2461)
* Changes for latest DroneCI (#2362)
* Use standard lessc and minify CSS using Node.js (#2337)
* DOCS
* Update screenshots on README (#2910)
* Gogs -> Gitea (#2909)
* Update swagger documentation (#2899)
* Fix typo (#2810)
* Fix Polish language name spelling (#2766)
* Fix Various Grammar Issues and Adjust Unnatural Wording (#2737)
* Add maintainer label for docker file (#2658)
* Link to gitea-specific Vagrant example (#2624)
* add release notes of v1.1.4 (#2463)
* Wrap most paragraphs to 80 columns (#2396)
* Update CONTRIBUTING following #2329 discussion (#2394)
* Update hard-coded version to 1.3.0+dev (#2390)
* Clarify Translation Process. Also fix branch names (#2378)
* Admin grammar fixes and improvements (#2056)
* MISC
* Sync MaxGitDiffLineCharacters with conf/app.ini (#2779)
* Dockerfile: Updated alpine image to 3.6. (#2486)
* Basic VSCode configuration for building and debugging (#2483)
* Added vendor dir for js/css libs; Documented sources (#1484) (#2241)
## [1.2.3](https://github.com/go-gitea/gitea/releases/tag/v1.2.3) - 2017-11-03
* BUGFIXES
* Only require one email when validating GPG key (#2266, #2467, #2663) (#2788)

View File

@ -1,4 +1,4 @@
FROM alpine:3.6
FROM alpine:3.7
LABEL maintainer="The Gitea Authors"
EXPOSE 22 3000

View File

@ -131,7 +131,7 @@ fmt-check:
.PHONY: test
test:
$(GO) test $(PACKAGES)
$(GO) test -tags=sqlite $(PACKAGES)
.PHONY: coverage
coverage:
@ -142,7 +142,7 @@ coverage:
.PHONY: unit-test-coverage
unit-test-coverage:
for PKG in $(PACKAGES); do $(GO) test -cover -coverprofile $$GOPATH/src/$$PKG/coverage.out $$PKG || exit 1; done;
for PKG in $(PACKAGES); do $(GO) test -tags=sqlite -cover -coverprofile $$GOPATH/src/$$PKG/coverage.out $$PKG || exit 1; done;
.PHONY: test-vendor
test-vendor:

View File

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/Unknwon/com"
"github.com/dgrijalva/jwt-go"
@ -219,8 +220,8 @@ func runServ(c *cli.Context) error {
fail("Internal error", "GetDeployKey: %v", err)
}
deployKey.Updated = time.Now()
if err = models.UpdateDeployKey(deployKey); err != nil {
deployKey.UpdatedUnix = util.TimeStampNow()
if err = models.UpdateDeployKeyCols(deployKey, "updated_unix"); err != nil {
fail("Internal error", "UpdateDeployKey: %v", err)
}
} else {

View File

@ -19,8 +19,10 @@ import (
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes"
"github.com/Unknwon/com"
context2 "github.com/gorilla/context"
"github.com/urfave/cli"
ini "gopkg.in/ini.v1"
)
// CmdWeb represents the available web sub-command.
@ -69,6 +71,34 @@ func runWeb(ctx *cli.Context) error {
if ctx.IsSet("port") {
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
setting.HTTPPort = ctx.String("port")
switch setting.Protocol {
case setting.UnixSocket:
case setting.FCGI:
default:
// Save LOCAL_ROOT_URL if port changed
cfg := ini.Empty()
if com.IsFile(setting.CustomConf) {
// Keeps custom settings if there is already something.
if err := cfg.Append(setting.CustomConf); err != nil {
return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
}
}
defaultLocalURL := string(setting.Protocol) + "://"
if setting.HTTPAddr == "0.0.0.0" {
defaultLocalURL += "localhost"
} else {
defaultLocalURL += setting.HTTPAddr
}
defaultLocalURL += ":" + setting.HTTPPort + "/"
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
if err := cfg.SaveTo(setting.CustomConf); err != nil {
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
}
}
}
var listenAddr string

View File

@ -175,14 +175,14 @@ LFS_START_SERVER = false
; Where your lfs files put on, default is data/lfs.
LFS_CONTENT_PATH = data/lfs
; LFS authentication secret, changed this to yourself.
LFS_JWT_SECRET =
LFS_JWT_SECRET =
; Define allowed algorithms and their minimum key length (use -1 to disable a type)
[ssh.minimum_key_sizes]
ED25519 = 256
ECDSA = 256
RSA = 2048
DSA = 1024
ECDSA = 256
RSA = 2048
DSA = 1024
[database]
; Either "mysql", "postgres", "mssql" or "sqlite3", it's your choice
@ -496,7 +496,11 @@ SCHEDULE = @every 24h
; Clean up old repository archives
[cron.archive_cleanup]
; Whether to enable the job
ENABLED = true
; Whether to always run at least once at start up time (if ENABLED)
RUN_AT_START = true
; Time interval for job to run
SCHEDULE = @every 24h
; Archives created more than OLDER_THAN ago are subject to deletion
OLDER_THAN = 24h

View File

@ -41,6 +41,10 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `FORCE_PRIVATE`: Force every new repository to be private.
- `MAX_CREATION_LIMIT`: Global maximum creation limit of repositories per user, `-1` means no limit.
- `PULL_REQUEST_QUEUE_LENGTH`:exclamation:: Length of pull request patch test queue, make it as large as possible.
- `MIRROR_QUEUE_LENGTH`: Patch test queue length, increase if pull request patch testing starts hanging. Defaults to 1000.
- `PREFERRED_LICENSES`: Preferred Licenses to place at the top of the List. Name must match file name in conf/license or custom/conf/license. Defaults to 'Apache License 2.0,MIT License'
- `DISABLE_HTTP_GIT`: Disable ability to interact with repositories by HTTP protocol. Defaults to false
- `USE_COMPAT_SSH_URI`: Force ssh:// clone url instead of scp-style uri when default SSH port is used. Defaults to false.
## UI (`ui`)
@ -189,6 +193,13 @@ Note: Actually, Gitea supports only SMTP with STARTTLS.
- `ENABLED`: Enable this to run cron tasks periodically.
- `RUN_AT_START`: Enable this to run cron tasks at start time.
### Cron - Cleanup old repository archives (`cron.archive_cleanup`)
- `ENABLED`: Enable service. Defaults to true.
- `RUN_AT_START`: Run tasks at start up time (if ENABLED). Defaults to true.
- `SCHEDULE`: Cron syntax for scheduling repository archive cleanup, e.g. `@every 1h`. Defaults to `@every 24h`.
- `OLDER_THAN`: Archives created more than `OLDER_THAN` ago are subject to deletion, e.g. `12h`. Defaults to `24h`.
### Cron - Update Mirrors (`cron.update_mirrors`)
- `SCHEDULE`: Cron syntax for scheduling update mirrors, e.g. `@every 1h`.

View File

@ -123,7 +123,7 @@ func TestAPILFSLocksLogged(t *testing.T) {
assert.Len(t, lfsLocks.Locks, test.totalCount)
for i, lock := range lfsLocks.Locks {
assert.EqualValues(t, test.locksOwners[i].DisplayName(), lock.Owner.Name)
assert.WithinDuration(t, test.locksTimes[i], lock.LockedAt, 1*time.Second)
assert.WithinDuration(t, test.locksTimes[i], lock.LockedAt, 3*time.Second)
}
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/verify", test.repo.FullName()), map[string]string{})

View File

@ -6,27 +6,32 @@ package integrations
import (
"context"
"fmt"
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"testing"
"time"
"code.gitea.io/git"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/sdk/gitea"
"github.com/Unknwon/com"
"github.com/stretchr/testify/assert"
)
func onGiteaWebRun(t *testing.T, callback func(*testing.T, string)) {
func onGiteaWebRun(t *testing.T, callback func(*testing.T, *url.URL)) {
s := http.Server{
Handler: mac,
}
listener, err := net.Listen("tcp", "")
u, err := url.Parse(setting.AppURL)
assert.NoError(t, err)
listener, err := net.Listen("tcp", u.Host)
assert.NoError(t, err)
defer func() {
@ -37,24 +42,144 @@ func onGiteaWebRun(t *testing.T, callback func(*testing.T, string)) {
go s.Serve(listener)
_, port, err := net.SplitHostPort(listener.Addr().String())
assert.NoError(t, err)
callback(t, fmt.Sprintf("http://localhost:%s/", port))
callback(t, u)
}
func TestClone_ViaHTTP_NoLogin(t *testing.T) {
func TestGit(t *testing.T) {
prepareTestEnv(t)
onGiteaWebRun(t, func(t *testing.T, urlPrefix string) {
dstPath, err := ioutil.TempDir("", "repo1")
onGiteaWebRun(t, func(t *testing.T, u *url.URL) {
dstPath, err := ioutil.TempDir("", "repo-tmp-17")
assert.NoError(t, err)
defer os.RemoveAll(dstPath)
u.Path = "user2/repo1.git"
err = git.Clone(fmt.Sprintf("%suser2/repo1.git", urlPrefix),
dstPath, git.CloneRepoOptions{})
assert.NoError(t, err)
t.Run("Standard", func(t *testing.T) {
assert.True(t, com.IsExist(filepath.Join(dstPath, "README.md")))
t.Run("CloneNoLogin", func(t *testing.T) {
dstLocalPath, err := ioutil.TempDir("", "repo1")
assert.NoError(t, err)
defer os.RemoveAll(dstLocalPath)
err = git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{})
assert.NoError(t, err)
assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
})
t.Run("CreateRepo", func(t *testing.T) {
session := loginUser(t, "user2")
req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", &api.CreateRepoOption{
AutoInit: true,
Description: "Temporary repo",
Name: "repo-tmp-17",
Private: false,
Gitignores: "",
License: "WTFPL",
Readme: "Default",
})
session.MakeRequest(t, req, http.StatusCreated)
})
u.Path = "user2/repo-tmp-17.git"
u.User = url.UserPassword("user2", userPassword)
t.Run("Clone", func(t *testing.T) {
err = git.Clone(u.String(), dstPath, git.CloneRepoOptions{})
assert.NoError(t, err)
assert.True(t, com.IsExist(filepath.Join(dstPath, "README.md")))
})
t.Run("PushCommit", func(t *testing.T) {
data := make([]byte, 1024)
_, err := rand.Read(data)
assert.NoError(t, err)
tmpFile, err := ioutil.TempFile(dstPath, "data-file-")
defer tmpFile.Close()
_, err = tmpFile.Write(data)
assert.NoError(t, err)
//Commit
err = git.AddChanges(dstPath, false, filepath.Base(tmpFile.Name()))
assert.NoError(t, err)
err = git.CommitChanges(dstPath, git.CommitChangesOptions{
Committer: &git.Signature{
Email: "user2@example.com",
Name: "User Two",
When: time.Now(),
},
Author: &git.Signature{
Email: "user2@example.com",
Name: "User Two",
When: time.Now(),
},
Message: "Testing commit",
})
assert.NoError(t, err)
//Push
err = git.Push(dstPath, git.PushOptions{
Branch: "master",
Remote: u.String(),
Force: false,
})
assert.NoError(t, err)
})
})
t.Run("LFS", func(t *testing.T) {
t.Run("PushCommit", func(t *testing.T) {
/* Generate random file */
data := make([]byte, 1024)
_, err := rand.Read(data)
assert.NoError(t, err)
tmpFile, err := ioutil.TempFile(dstPath, "data-file-")
defer tmpFile.Close()
_, err = tmpFile.Write(data)
assert.NoError(t, err)
//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)
//Commit
err = git.AddChanges(dstPath, false, ".gitattributes", filepath.Base(tmpFile.Name()))
assert.NoError(t, err)
err = git.CommitChanges(dstPath, git.CommitChangesOptions{
Committer: &git.Signature{
Email: "user2@example.com",
Name: "User Two",
When: time.Now(),
},
Author: &git.Signature{
Email: "user2@example.com",
Name: "User Two",
When: time.Now(),
},
Message: "Testing LFS ",
})
assert.NoError(t, err)
//Push
u.User = url.UserPassword("user2", userPassword)
err = git.Push(dstPath, git.PushOptions{
Branch: "master",
Remote: u.String(),
Force: false,
})
assert.NoError(t, err)
})
t.Run("Locks", func(t *testing.T) {
_, err = git.NewCommand("remote").AddArguments("set-url", "origin", u.String()).RunInDir(dstPath) //TODO add test ssh git-lfs-creds
assert.NoError(t, err)
_, err = git.NewCommand("lfs").AddArguments("locks").RunInDir(dstPath)
assert.NoError(t, err)
_, err = git.NewCommand("lfs").AddArguments("lock", "README.md").RunInDir(dstPath)
assert.NoError(t, err)
_, err = git.NewCommand("lfs").AddArguments("locks").RunInDir(dstPath)
assert.NoError(t, err)
_, err = git.NewCommand("lfs").AddArguments("unlock", "README.md").RunInDir(dstPath)
assert.NoError(t, err)
})
})
})
}

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' post-receive
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" post-receive

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' pre-receive
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" pre-receive

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' update $1 $2 $3
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" update $1 $2 $3

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' post-receive
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" post-receive

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' pre-receive
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" pre-receive

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' update $1 $2 $3
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" update $1 $2 $3

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' post-receive
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" post-receive

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' pre-receive
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" pre-receive

View File

@ -1,2 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config='integrations/app.ini' update $1 $2 $3
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" update $1 $2 $3

View File

@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes"
"github.com/PuerkitoBio/goquery"
"github.com/Unknwon/com"
"github.com/stretchr/testify/assert"
"gopkg.in/macaron.v1"
@ -136,6 +137,7 @@ func initIntegrationTest() {
func prepareTestEnv(t testing.TB) {
assert.NoError(t, models.LoadFixtures())
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
assert.NoError(t, os.RemoveAll(models.LocalCopyPath()))
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
setting.RepoRootPath))
@ -259,12 +261,37 @@ func MakeRequest(t testing.TB, req *http.Request, expectedStatus int) *httptest.
recorder := httptest.NewRecorder()
mac.ServeHTTP(recorder, req)
if expectedStatus != NoExpectedStatus {
assert.EqualValues(t, expectedStatus, recorder.Code,
"Request: %s %s", req.Method, req.URL.String())
if !assert.EqualValues(t, expectedStatus, recorder.Code,
"Request: %s %s", req.Method, req.URL.String()) {
logUnexpectedResponse(t, recorder)
}
}
return recorder
}
// logUnexpectedResponse logs the contents of an unexpected response.
func logUnexpectedResponse(t testing.TB, recorder *httptest.ResponseRecorder) {
respBytes := recorder.Body.Bytes()
if len(respBytes) == 0 {
return
} else if len(respBytes) < 500 {
// if body is short, just log the whole thing
t.Log("Response:", string(respBytes))
return
}
// log the "flash" error message, if one exists
// we must create a new buffer, so that we don't "use up" resp.Body
htmlDoc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(respBytes))
if err != nil {
return // probably a non-HTML response
}
errMsg := htmlDoc.Find(".ui.negative.message").Text()
if len(errMsg) > 0 {
t.Log("A flash error message was found:", errMsg)
}
}
func DecodeJSON(t testing.TB, resp *httptest.ResponseRecorder, v interface{}) {
decoder := json.NewDecoder(resp.Body)
assert.NoError(t, decoder.Decode(v))
@ -276,9 +303,3 @@ func GetCSRF(t testing.TB, session *TestSession, urlStr string) string {
doc := NewHTMLParser(t, resp.Body)
return doc.GetCSRF()
}
func RedirectURL(t testing.TB, resp *httptest.ResponseRecorder) string {
urlSlice := resp.HeaderMap["Location"]
assert.NotEmpty(t, urlSlice, "No redirect URL founds")
return urlSlice[0]
}

View File

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
@ -122,7 +123,7 @@ func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content
})
resp = session.MakeRequest(t, req, http.StatusFound)
issueURL := RedirectURL(t, resp)
issueURL := test.RedirectURL(resp)
req = NewRequest(t, "GET", issueURL)
resp = session.MakeRequest(t, req, http.StatusOK)
@ -153,7 +154,7 @@ func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content,
})
resp = session.MakeRequest(t, req, http.StatusFound)
req = NewRequest(t, "GET", RedirectURL(t, resp))
req = NewRequest(t, "GET", test.RedirectURL(resp))
resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)

View File

@ -11,6 +11,7 @@ import (
"testing"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
api "code.gitea.io/sdk/gitea"
"github.com/stretchr/testify/assert"
@ -46,13 +47,15 @@ func TestRedirectsNoLogin(t *testing.T) {
prepareTestEnv(t)
var redirects = map[string]string{
"/user2/repo1/commits/master": "/user2/repo1/commits/branch/master",
"/user2/repo1/src/master": "/user2/repo1/src/branch/master",
"/user2/repo1/commits/master": "/user2/repo1/commits/branch/master",
"/user2/repo1/src/master": "/user2/repo1/src/branch/master",
"/user2/repo1/src/master/file.txt": "/user2/repo1/src/branch/master/file.txt",
"/user2/repo1/src/master/directory/file.txt": "/user2/repo1/src/branch/master/directory/file.txt",
}
for link, redirectLink := range redirects {
req := NewRequest(t, "GET", link)
resp := MakeRequest(t, req, http.StatusFound)
assert.EqualValues(t, path.Join(setting.AppSubURL, redirectLink), RedirectURL(t, resp))
assert.EqualValues(t, path.Join(setting.AppSubURL, redirectLink), test.RedirectURL(resp))
}
}

View File

@ -11,6 +11,8 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
@ -54,7 +56,7 @@ func TestPullMerge(t *testing.T) {
resp := testPullCreate(t, session, "user1", "repo1", "master")
elem := strings.Split(RedirectURL(t, resp), "/")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4])
}
@ -67,7 +69,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
resp := testPullCreate(t, session, "user1", "repo1", "feature/test")
elem := strings.Split(RedirectURL(t, resp), "/")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4])

View File

@ -9,6 +9,8 @@ import (
"net/http"
"testing"
"code.gitea.io/gitea/modules/test"
"github.com/Unknwon/i18n"
"github.com/stretchr/testify/assert"
)
@ -38,7 +40,7 @@ func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title st
resp = session.MakeRequest(t, req, http.StatusFound)
RedirectURL(t, resp) // check that redirect URL exists
test.RedirectURL(resp) // check that redirect URL exists
}
func checkLatestReleaseAndCount(t *testing.T, session *TestSession, repoURL, version, label string, count int) {

View File

@ -9,6 +9,8 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
@ -20,7 +22,7 @@ func TestRepoActivity(t *testing.T) {
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master")
elem := strings.Split(RedirectURL(t, resp), "/")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4])

View File

@ -10,6 +10,8 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/modules/test"
"github.com/Unknwon/i18n"
"github.com/stretchr/testify/assert"
)
@ -29,7 +31,7 @@ func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefSubU
if expectedStatus != http.StatusFound {
return ""
}
return RedirectURL(t, resp)
return test.RedirectURL(resp)
}
func TestCreateBranch(t *testing.T) {

View File

@ -10,6 +10,8 @@ import (
"testing"
"time"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
@ -47,7 +49,7 @@ func testViewTimetrackingControls(t *testing.T, session *TestSession, user, repo
if canTrackTime {
resp = session.MakeRequest(t, req, http.StatusSeeOther)
req = NewRequest(t, "GET", RedirectURL(t, resp))
req = NewRequest(t, "GET", test.RedirectURL(resp))
resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
@ -65,7 +67,7 @@ func testViewTimetrackingControls(t *testing.T, session *TestSession, user, repo
})
resp = session.MakeRequest(t, req, http.StatusSeeOther)
req = NewRequest(t, "GET", RedirectURL(t, resp))
req = NewRequest(t, "GET", test.RedirectURL(resp))
resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)

View File

@ -9,6 +9,7 @@ import (
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/test"
"github.com/Unknwon/i18n"
"github.com/stretchr/testify/assert"
@ -86,7 +87,7 @@ func TestRenameReservedUsername(t *testing.T) {
})
resp := session.MakeRequest(t, req, http.StatusFound)
req = NewRequest(t, "GET", RedirectURL(t, resp))
req = NewRequest(t, "GET", test.RedirectURL(resp))
resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Contains(t,

View File

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
"github.com/Unknwon/com"
@ -85,15 +86,9 @@ type Action struct {
Comment *Comment `xorm:"-"`
IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"`
RefName string
IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (a *Action) AfterLoad() {
a.Created = time.Unix(a.CreatedUnix, 0).Local()
IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
}
// GetOpType gets the ActionType of this action.
@ -229,7 +224,7 @@ func (a *Action) GetContent() string {
// GetCreate returns the action creation time.
func (a *Action) GetCreate() time.Time {
return a.Created
return a.CreatedUnix.AsTime()
}
// GetIssueInfos returns a list of issues associated with

View File

@ -6,7 +6,6 @@ package models
import (
"fmt"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
@ -26,14 +25,8 @@ const (
type Notice struct {
ID int64 `xorm:"pk autoincr"`
Type NoticeType
Description string `xorm:"TEXT"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (n *Notice) AfterLoad() {
n.Created = time.Unix(n.CreatedUnix, 0).Local()
Description string `xorm:"TEXT"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
}
// TrStr returns a translation format string.

View File

@ -10,11 +10,11 @@ import (
"mime/multipart"
"os"
"path"
"time"
gouuid "github.com/satori/go.uuid"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
// Attachment represent a attachment of issue/comment/release.
@ -25,24 +25,14 @@ type Attachment struct {
ReleaseID int64 `xorm:"INDEX"`
CommentID int64
Name string
DownloadCount int64 `xorm:"DEFAULT 0"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"created"`
}
// AfterLoad is invoked from XORM after setting the value of a field of
// this object.
func (a *Attachment) AfterLoad() {
a.Created = time.Unix(a.CreatedUnix, 0).Local()
DownloadCount int64 `xorm:"DEFAULT 0"`
CreatedUnix util.TimeStamp `xorm:"created"`
}
// IncreaseDownloadCount is update download count + 1
func (a *Attachment) IncreaseDownloadCount() error {
sess := x.NewSession()
defer sess.Close()
// Update download count.
if _, err := sess.Exec("UPDATE `attachment` SET download_count=download_count+1 WHERE id=?", a.ID); err != nil {
if _, err := x.Exec("UPDATE `attachment` SET download_count=download_count+1 WHERE id=?", a.ID); err != nil {
return fmt.Errorf("increase attachment count: %v", err)
}

View File

@ -29,12 +29,10 @@ type ProtectedBranch struct {
BranchName string `xorm:"UNIQUE(s)"`
CanPush bool `xorm:"NOT NULL DEFAULT false"`
EnableWhitelist bool
WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"updated"`
WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
CreatedUnix util.TimeStamp `xorm:"created"`
UpdatedUnix util.TimeStamp `xorm:"updated"`
}
// IsProtected returns if the branch is protected
@ -197,19 +195,13 @@ func (repo *Repository) DeleteProtectedBranch(id int64) (err error) {
// DeletedBranch struct
type DeletedBranch struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"UNIQUE(s) NOT NULL"`
Commit string `xorm:"UNIQUE(s) NOT NULL"`
DeletedByID int64 `xorm:"INDEX"`
DeletedBy *User `xorm:"-"`
Deleted time.Time `xorm:"-"`
DeletedUnix int64 `xorm:"INDEX created"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (deletedBranch *DeletedBranch) AfterLoad() {
deletedBranch.Deleted = time.Unix(deletedBranch.DeletedUnix, 0).Local()
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"UNIQUE(s) NOT NULL"`
Commit string `xorm:"UNIQUE(s) NOT NULL"`
DeletedByID int64 `xorm:"INDEX"`
DeletedBy *User `xorm:"-"`
DeletedUnix util.TimeStamp `xorm:"INDEX created"`
}
// AddDeletedBranch adds a deleted branch to the database

View File

@ -19,3 +19,25 @@
issue_id: 2
created_unix: 946684800
updated_unix: 946684800
-
id: 3
user_id: 2
repo_id: 1
status: 3 # pinned
source: 1 # issue
updated_by: 1
issue_id: 2
created_unix: 946684800
updated_unix: 946684800
-
id: 4
user_id: 2
repo_id: 1
status: 1 # unread
source: 1 # issue
updated_by: 1
issue_id: 2
created_unix: 946684800
updated_unix: 946684800

View File

@ -0,0 +1 @@
[] # empty

View File

@ -238,7 +238,7 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
var (
diff = &Diff{Files: make([]*DiffFile, 0)}
curFile *DiffFile
curFile = &DiffFile{}
curSection = &DiffSection{
Lines: make([]*DiffLine, 0, 10),
}

View File

@ -17,6 +17,7 @@ import (
"code.gitea.io/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"github.com/go-xorm/xorm"
"github.com/keybase/go-crypto/openpgp"
@ -26,17 +27,14 @@ import (
// GPGKey represents a GPG key.
type GPGKey struct {
ID int64 `xorm:"pk autoincr"`
OwnerID int64 `xorm:"INDEX NOT NULL"`
KeyID string `xorm:"INDEX CHAR(16) NOT NULL"`
PrimaryKeyID string `xorm:"CHAR(16)"`
Content string `xorm:"TEXT NOT NULL"`
Created time.Time `xorm:"-"`
CreatedUnix int64
Expired time.Time `xorm:"-"`
ExpiredUnix int64
Added time.Time `xorm:"-"`
AddedUnix int64
ID int64 `xorm:"pk autoincr"`
OwnerID int64 `xorm:"INDEX NOT NULL"`
KeyID string `xorm:"INDEX CHAR(16) NOT NULL"`
PrimaryKeyID string `xorm:"CHAR(16)"`
Content string `xorm:"TEXT NOT NULL"`
CreatedUnix util.TimeStamp `xorm:"created"`
ExpiredUnix util.TimeStamp
AddedUnix util.TimeStamp
SubsKey []*GPGKey `xorm:"-"`
Emails []*EmailAddress
CanSign bool
@ -47,17 +45,11 @@ type GPGKey struct {
// BeforeInsert will be invoked by XORM before inserting a record
func (key *GPGKey) BeforeInsert() {
key.AddedUnix = time.Now().Unix()
key.ExpiredUnix = key.Expired.Unix()
key.CreatedUnix = key.Created.Unix()
key.AddedUnix = util.TimeStampNow()
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (key *GPGKey) AfterLoad(session *xorm.Session) {
key.Added = time.Unix(key.AddedUnix, 0).Local()
key.Expired = time.Unix(key.ExpiredUnix, 0).Local()
key.Created = time.Unix(key.CreatedUnix, 0).Local()
err := session.Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey)
if err != nil {
log.Error(3, "Find Sub GPGkeys[%d]: %v", key.KeyID, err)
@ -163,8 +155,8 @@ func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, e
KeyID: pubkey.KeyIdString(),
PrimaryKeyID: primaryID,
Content: content,
Created: pubkey.CreationTime,
Expired: expiry,
CreatedUnix: util.TimeStamp(pubkey.CreationTime.Unix()),
ExpiredUnix: util.TimeStamp(expiry.Unix()),
CanSign: pubkey.CanSign(),
CanEncryptComms: pubkey.PubKeyAlgo.CanEncrypt(),
CanEncryptStorage: pubkey.PubKeyAlgo.CanEncrypt(),
@ -236,8 +228,8 @@ func parseGPGKey(ownerID int64, e *openpgp.Entity) (*GPGKey, error) {
KeyID: pubkey.KeyIdString(),
PrimaryKeyID: "",
Content: content,
Created: pubkey.CreationTime,
Expired: expiry,
CreatedUnix: util.TimeStamp(pubkey.CreationTime.Unix()),
ExpiredUnix: util.TimeStamp(expiry.Unix()),
Emails: emails,
SubsKey: subkeys,
CanSign: pubkey.CanSign(),

View File

@ -7,6 +7,8 @@ package models
import (
"testing"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@ -109,7 +111,7 @@ MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
key := &GPGKey{
KeyID: pubkey.KeyIdString(),
Content: content,
Created: pubkey.CreationTime,
CreatedUnix: util.TimeStamp(pubkey.CreationTime.Unix()),
CanSign: pubkey.CanSign(),
CanEncryptComms: pubkey.PubKeyAlgo.CanEncrypt(),
CanEncryptStorage: pubkey.PubKeyAlgo.CanEncrypt(),
@ -119,7 +121,7 @@ MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
cannotsignkey := &GPGKey{
KeyID: pubkey.KeyIdString(),
Content: content,
Created: pubkey.CreationTime,
CreatedUnix: util.TimeStamp(pubkey.CreationTime.Unix()),
CanSign: false,
CanEncryptComms: false,
CanEncryptStorage: false,

View File

@ -9,7 +9,6 @@ import (
"path"
"sort"
"strings"
"time"
api "code.gitea.io/sdk/gitea"
"github.com/Unknwon/com"
@ -45,31 +44,15 @@ type Issue struct {
NumComments int
Ref string
Deadline time.Time `xorm:"-"`
DeadlineUnix int64 `xorm:"INDEX"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
DeadlineUnix util.TimeStamp `xorm:"INDEX"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
Attachments []*Attachment `xorm:"-"`
Comments []*Comment `xorm:"-"`
Reactions ReactionList `xorm:"-"`
}
// BeforeUpdate is invoked from XORM before updating this object.
func (issue *Issue) BeforeUpdate() {
issue.DeadlineUnix = issue.Deadline.Unix()
}
// AfterLoad is invoked from XORM after setting the value of a field of
// this object.
func (issue *Issue) AfterLoad() {
issue.Deadline = time.Unix(issue.DeadlineUnix, 0).Local()
issue.Created = time.Unix(issue.CreatedUnix, 0).Local()
issue.Updated = time.Unix(issue.UpdatedUnix, 0).Local()
}
func (issue *Issue) loadRepo(e Engine) (err error) {
if issue.Repo == nil {
issue.Repo, err = getRepositoryByID(e, issue.RepoID)
@ -307,8 +290,8 @@ func (issue *Issue) APIFormat() *api.Issue {
Labels: apiLabels,
State: issue.State(),
Comments: issue.NumComments,
Created: issue.Created,
Updated: issue.Updated,
Created: issue.CreatedUnix.AsTime(),
Updated: issue.UpdatedUnix.AsTime(),
}
if issue.Milestone != nil {
@ -322,7 +305,7 @@ func (issue *Issue) APIFormat() *api.Issue {
HasMerged: issue.PullRequest.HasMerged,
}
if issue.PullRequest.HasMerged {
apiIssue.PullRequest.Merged = &issue.PullRequest.Merged
apiIssue.PullRequest.Merged = issue.PullRequest.MergedUnix.AsTimePtr()
}
}
@ -599,7 +582,7 @@ func updateIssueCols(e Engine, issue *Issue, cols ...string) error {
if _, err := e.ID(issue.ID).Cols(cols...).Update(issue); err != nil {
return err
}
UpdateIssueIndexer(issue.ID)
UpdateIssueIndexerCols(issue.ID, cols...)
return nil
}

View File

@ -7,7 +7,6 @@ package models
import (
"fmt"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/go-xorm/builder"
@ -17,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/util"
)
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
@ -104,10 +104,8 @@ type Comment struct {
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
// Reference issue in commit message
CommitSHA string `xorm:"VARCHAR(40)"`
@ -121,9 +119,6 @@ type Comment struct {
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (c *Comment) AfterLoad(session *xorm.Session) {
c.Created = time.Unix(c.CreatedUnix, 0).Local()
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
var err error
c.Attachments, err = getAttachmentsByCommentID(session, c.ID)
if err != nil {
@ -197,8 +192,8 @@ func (c *Comment) APIFormat() *api.Comment {
IssueURL: c.IssueURL(),
PRURL: c.PRURL(),
Body: c.Content,
Created: c.Created,
Updated: c.Updated,
Created: c.CreatedUnix.AsTime(),
Updated: c.UpdatedUnix.AsTime(),
}
}

View File

@ -33,9 +33,9 @@ func TestCreateComment(t *testing.T) {
assert.EqualValues(t, "Hello", comment.Content)
assert.EqualValues(t, issue.ID, comment.IssueID)
assert.EqualValues(t, doer.ID, comment.PosterID)
AssertInt64InRange(t, now, then, comment.CreatedUnix)
AssertInt64InRange(t, now, then, int64(comment.CreatedUnix))
AssertExistsAndLoadBean(t, comment) // assert actually added to DB
updatedIssue := AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue)
AssertInt64InRange(t, now, then, updatedIssue.UpdatedUnix)
AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
}

View File

@ -102,6 +102,26 @@ func (issue *Issue) update() indexer.IssueIndexerUpdate {
}
}
// updateNeededCols whether a change to the specified columns requires updating
// the issue indexer
func updateNeededCols(cols []string) bool {
for _, col := range cols {
switch col {
case "name", "content":
return true
}
}
return false
}
// UpdateIssueIndexerCols update an issue in the issue indexer, given changes
// to the specified columns
func UpdateIssueIndexerCols(issueID int64, cols ...string) {
if updateNeededCols(cols) {
UpdateIssueIndexer(issueID)
}
}
// UpdateIssueIndexer add/update an issue to the issue indexer
func UpdateIssueIndexer(issueID int64) {
select {

View File

@ -5,9 +5,8 @@
package models
import (
"time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/xorm"
@ -27,16 +26,14 @@ type Milestone struct {
Completeness int // Percentage(1-100).
IsOverDue bool `xorm:"-"`
DeadlineString string `xorm:"-"`
Deadline time.Time `xorm:"-"`
DeadlineUnix int64
ClosedDate time.Time `xorm:"-"`
ClosedDateUnix int64
DeadlineString string `xorm:"-"`
DeadlineUnix util.TimeStamp
ClosedDateUnix util.TimeStamp
}
// BeforeInsert is invoked from XORM before inserting an object of this type.
func (m *Milestone) BeforeInsert() {
m.DeadlineUnix = m.Deadline.Unix()
m.DeadlineUnix = util.TimeStampNow()
}
// BeforeUpdate is invoked from XORM before updating this object.
@ -46,26 +43,20 @@ func (m *Milestone) BeforeUpdate() {
} else {
m.Completeness = 0
}
m.DeadlineUnix = m.Deadline.Unix()
m.ClosedDateUnix = m.ClosedDate.Unix()
}
// AfterLoad is invoked from XORM after setting the value of a field of
// this object.
func (m *Milestone) AfterLoad() {
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
if m.Deadline.Year() == 9999 {
if m.DeadlineUnix.Year() == 9999 {
return
}
m.DeadlineString = m.Deadline.Format("2006-01-02")
if time.Now().Local().After(m.Deadline) {
m.DeadlineString = m.DeadlineUnix.Format("2006-01-02")
if util.TimeStampNow() >= m.DeadlineUnix {
m.IsOverDue = true
}
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
}
// State returns string representation of milestone status.
@ -87,10 +78,10 @@ func (m *Milestone) APIFormat() *api.Milestone {
ClosedIssues: m.NumClosedIssues,
}
if m.IsClosed {
apiMilestone.Closed = &m.ClosedDate
apiMilestone.Closed = m.ClosedDateUnix.AsTimePtr()
}
if m.Deadline.Year() < 9999 {
apiMilestone.Deadline = &m.Deadline
if m.DeadlineUnix.Year() < 9999 {
apiMilestone.Deadline = m.DeadlineUnix.AsTimePtr()
}
return apiMilestone
}
@ -174,31 +165,33 @@ func UpdateMilestone(m *Milestone) error {
return updateMilestone(x, m)
}
func countRepoMilestones(e Engine, repoID int64) int64 {
count, _ := e.
func countRepoMilestones(e Engine, repoID int64) (int64, error) {
return e.
Where("repo_id=?", repoID).
Count(new(Milestone))
return count
}
func countRepoClosedMilestones(e Engine, repoID int64) int64 {
closed, _ := e.
func countRepoClosedMilestones(e Engine, repoID int64) (int64, error) {
return e.
Where("repo_id=? AND is_closed=?", repoID, true).
Count(new(Milestone))
return closed
}
// CountRepoClosedMilestones returns number of closed milestones in given repository.
func CountRepoClosedMilestones(repoID int64) int64 {
func CountRepoClosedMilestones(repoID int64) (int64, error) {
return countRepoClosedMilestones(x, repoID)
}
// MilestoneStats returns number of open and closed milestones of given repository.
func MilestoneStats(repoID int64) (open int64, closed int64) {
open, _ = x.
func MilestoneStats(repoID int64) (open int64, closed int64, err error) {
open, err = x.
Where("repo_id=? AND is_closed=?", repoID, false).
Count(new(Milestone))
return open, CountRepoClosedMilestones(repoID)
if err != nil {
return 0, 0, nil
}
closed, err = CountRepoClosedMilestones(repoID)
return open, closed, err
}
// ChangeMilestoneStatus changes the milestone open/closed status.
@ -219,8 +212,17 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
return err
}
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
numMilestones, err := countRepoMilestones(sess, repo.ID)
if err != nil {
return err
}
numClosedMilestones, err := countRepoClosedMilestones(sess, repo.ID)
if err != nil {
return err
}
repo.NumMilestones = int(numMilestones)
repo.NumClosedMilestones = int(numClosedMilestones)
if _, err = sess.ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
return err
}
@ -291,7 +293,7 @@ func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilesto
}
}
return updateIssue(e, issue)
return updateIssueCols(e, issue, "milestone_id")
}
// ChangeMilestoneAssign changes assignment of milestone for issue.
@ -333,8 +335,17 @@ func DeleteMilestoneByRepoID(repoID, id int64) error {
return err
}
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
numMilestones, err := countRepoMilestones(sess, repo.ID)
if err != nil {
return err
}
numClosedMilestones, err := countRepoClosedMilestones(sess, repo.ID)
if err != nil {
return err
}
repo.NumMilestones = int(numMilestones)
repo.NumClosedMilestones = int(numClosedMilestones)
if _, err = sess.ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
return err
}

View File

@ -9,6 +9,7 @@ import (
"testing"
"time"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
"github.com/stretchr/testify/assert"
@ -28,7 +29,7 @@ func TestMilestone_APIFormat(t *testing.T) {
IsClosed: false,
NumOpenIssues: 5,
NumClosedIssues: 6,
Deadline: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
DeadlineUnix: util.TimeStamp(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC).Unix()),
}
assert.Equal(t, api.Milestone{
ID: milestone.ID,
@ -37,7 +38,7 @@ func TestMilestone_APIFormat(t *testing.T) {
Description: milestone.Content,
OpenIssues: milestone.NumOpenIssues,
ClosedIssues: milestone.NumClosedIssues,
Deadline: &milestone.Deadline,
Deadline: milestone.DeadlineUnix.AsTimePtr(),
}, *milestone.APIFormat())
}
@ -145,31 +146,42 @@ func TestCountRepoMilestones(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
test := func(repoID int64) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
assert.EqualValues(t, repo.NumMilestones, countRepoMilestones(x, repoID))
count, err := countRepoMilestones(x, repoID)
assert.NoError(t, err)
assert.EqualValues(t, repo.NumMilestones, count)
}
test(1)
test(2)
test(3)
assert.EqualValues(t, 0, countRepoMilestones(x, NonexistentID))
count, err := countRepoMilestones(x, NonexistentID)
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
}
func TestCountRepoClosedMilestones(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
test := func(repoID int64) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
assert.EqualValues(t, repo.NumClosedMilestones, CountRepoClosedMilestones(repoID))
count, err := CountRepoClosedMilestones(repoID)
assert.NoError(t, err)
assert.EqualValues(t, repo.NumClosedMilestones, count)
}
test(1)
test(2)
test(3)
assert.EqualValues(t, 0, countRepoMilestones(x, NonexistentID))
count, err := CountRepoClosedMilestones(NonexistentID)
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
}
func TestMilestoneStats(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
test := func(repoID int64) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
open, closed := MilestoneStats(repoID)
open, closed, err := MilestoneStats(repoID)
assert.NoError(t, err)
assert.EqualValues(t, repo.NumMilestones-repo.NumClosedMilestones, open)
assert.EqualValues(t, repo.NumClosedMilestones, closed)
}
@ -177,7 +189,8 @@ func TestMilestoneStats(t *testing.T) {
test(2)
test(3)
open, closed := MilestoneStats(NonexistentID)
open, closed, err := MilestoneStats(NonexistentID)
assert.NoError(t, err)
assert.EqualValues(t, 0, open)
assert.EqualValues(t, 0, closed)
}

View File

@ -7,9 +7,9 @@ package models
import (
"bytes"
"fmt"
"time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/go-xorm/builder"
"github.com/go-xorm/xorm"
@ -17,19 +17,13 @@ import (
// Reaction represents a reactions on issues and comments.
type Reaction struct {
ID int64 `xorm:"pk autoincr"`
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
User *User `xorm:"-"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (s *Reaction) AfterLoad() {
s.Created = time.Unix(s.CreatedUnix, 0).Local()
ID int64 `xorm:"pk autoincr"`
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
User *User `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
}
// FindReactionsOptions describes the conditions to Find reactions

View File

@ -7,26 +7,16 @@ package models
import (
"fmt"
"time"
"code.gitea.io/gitea/modules/util"
)
// Stopwatch represents a stopwatch for time tracking.
type Stopwatch struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"INDEX"`
UserID int64 `xorm:"INDEX"`
Created time.Time `xorm:"-"`
CreatedUnix int64
}
// BeforeInsert will be invoked by XORM before inserting a record
// representing this object.
func (s *Stopwatch) BeforeInsert() {
s.CreatedUnix = time.Now().Unix()
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (s *Stopwatch) AfterLoad() {
s.Created = time.Unix(s.CreatedUnix, 0).Local()
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"INDEX"`
UserID int64 `xorm:"INDEX"`
CreatedUnix util.TimeStamp `xorm:"created"`
}
func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool, err error) {
@ -61,7 +51,7 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
}
if exists {
// Create tracked time out of the time difference between start date and actual date
timediff := time.Now().Unix() - sw.CreatedUnix
timediff := time.Now().Unix() - int64(sw.CreatedUnix)
// Create TrackedTime
tt := &TrackedTime{
@ -92,7 +82,6 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
sw = &Stopwatch{
UserID: user.ID,
IssueID: issue.ID,
Created: time.Now(),
}
if _, err := x.Insert(sw); err != nil {

View File

@ -2,7 +2,8 @@ package models
import (
"testing"
"time"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@ -62,7 +63,7 @@ func TestCreateOrStopIssueStopwatch(t *testing.T) {
assert.NoError(t, CreateOrStopIssueStopwatch(user3, issue1))
sw := AssertExistsAndLoadBean(t, &Stopwatch{UserID: 3, IssueID: 1}).(*Stopwatch)
assert.Equal(t, true, sw.Created.Before(time.Now()))
assert.Equal(t, true, sw.CreatedUnix <= util.TimeStampNow())
assert.NoError(t, CreateOrStopIssueStopwatch(user2, issue2))
AssertNotExistsBean(t, &Stopwatch{UserID: 2, IssueID: 2})

View File

@ -166,5 +166,5 @@ func TestUpdateIssueCols(t *testing.T) {
updatedIssue := AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue)
assert.EqualValues(t, newTitle, updatedIssue.Title)
assert.EqualValues(t, prevContent, updatedIssue.Content)
AssertInt64InRange(t, now, then, updatedIssue.UpdatedUnix)
AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
}

View File

@ -7,6 +7,7 @@ package models
import (
"time"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/builder"
@ -24,7 +25,7 @@ type TrackedTime struct {
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (t *TrackedTime) AfterLoad() {
t.Created = time.Unix(t.CreatedUnix, 0).Local()
t.Created = time.Unix(t.CreatedUnix, 0).In(setting.UILocation)
}
// APIFormat converts TrackedTime to API format

View File

@ -4,42 +4,16 @@
package models
import (
"time"
)
import "code.gitea.io/gitea/modules/util"
// IssueWatch is connection request for receiving issue notification.
type IssueWatch struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"UNIQUE(watch) NOT NULL"`
IssueID int64 `xorm:"UNIQUE(watch) NOT NULL"`
IsWatching bool `xorm:"NOT NULL"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"NOT NULL"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"NOT NULL"`
}
// BeforeInsert is invoked from XORM before inserting an object of this type.
func (iw *IssueWatch) BeforeInsert() {
var (
t = time.Now()
u = t.Unix()
)
iw.Created = t
iw.CreatedUnix = u
iw.Updated = t
iw.UpdatedUnix = u
}
// BeforeUpdate is invoked from XORM before updating an object of this type.
func (iw *IssueWatch) BeforeUpdate() {
var (
t = time.Now()
u = t.Unix()
)
iw.Updated = t
iw.UpdatedUnix = u
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"UNIQUE(watch) NOT NULL"`
IssueID int64 `xorm:"UNIQUE(watch) NOT NULL"`
IsWatching bool `xorm:"NOT NULL"`
CreatedUnix util.TimeStamp `xorm:"created NOT NULL"`
UpdatedUnix util.TimeStamp `xorm:"updated NOT NULL"`
}
// CreateOrUpdateIssueWatch set watching for a user and issue

View File

@ -2,18 +2,18 @@ package models
import (
"errors"
"time"
"code.gitea.io/gitea/modules/util"
)
// LFSMetaObject stores metadata for LFS tracked files.
type LFSMetaObject struct {
ID int64 `xorm:"pk autoincr"`
Oid string `xorm:"UNIQUE(s) INDEX NOT NULL"`
Size int64 `xorm:"NOT NULL"`
RepositoryID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Existing bool `xorm:"-"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"created"`
ID int64 `xorm:"pk autoincr"`
Oid string `xorm:"UNIQUE(s) INDEX NOT NULL"`
Size int64 `xorm:"NOT NULL"`
RepositoryID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Existing bool `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"created"`
}
// LFSTokenResponse defines the JSON structure in which the JWT token is stored.
@ -105,8 +105,3 @@ func (repo *Repository) RemoveLFSMetaObjectByOid(oid string) error {
return sess.Commit()
}
// AfterLoad stores the LFSMetaObject creation time in the database as local time.
func (m *LFSMetaObject) AfterLoad() {
m.Created = time.Unix(m.CreatedUnix, 0).Local()
}

View File

@ -36,7 +36,7 @@ func (l *LFSLock) AfterLoad() {
}
func cleanPath(p string) string {
return strings.ToLower(path.Clean(p))
return path.Clean(p)
}
// APIFormat convert a Release to lfs.LFSLock
@ -73,8 +73,8 @@ func CreateLFSLock(lock *LFSLock) (*LFSLock, error) {
// GetLFSLock returns release by given path.
func GetLFSLock(repoID int64, path string) (*LFSLock, error) {
path = cleanPath(path)
rel := &LFSLock{RepoID: repoID, Path: path}
has, err := x.Get(rel)
rel := &LFSLock{RepoID: repoID}
has, err := x.Where("lower(path) = ?", strings.ToLower(path)).Get(rel)
if err != nil {
return nil, err
}

View File

@ -12,7 +12,6 @@ import (
"net/smtp"
"net/textproto"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/go-macaron/binding"
@ -23,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/auth/oauth2"
"code.gitea.io/gitea/modules/auth/pam"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
// LoginType represents an login type.
@ -147,10 +147,8 @@ type LoginSource struct {
IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
Cfg core.Conversion `xorm:"TEXT"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
}
// Cell2Int64 converts a xorm.Cell type to int64,
@ -183,12 +181,6 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
}
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (source *LoginSource) AfterLoad() {
source.Created = time.Unix(source.CreatedUnix, 0).Local()
source.Updated = time.Unix(source.UpdatedUnix, 0).Local()
}
// TypeName return name of this login source type.
func (source *LoginSource) TypeName() string {
return LoginNames[source.Type]

View File

@ -59,6 +59,10 @@ type Version struct {
Version int64
}
func emptyMigration(x *xorm.Engine) error {
return nil
}
// This is a sequence of migrations. Add new migrations to the bottom of the list.
// If you want to "retire" a migration, remove it from the top of the list and
// update minDBVersion accordingly
@ -127,17 +131,17 @@ var migrations = []Migration{
// v38 -> v39
NewMigration("remove commits and settings unit types", removeCommitsUnitType),
// v39 -> v40
NewMigration("adds time tracking and stopwatches", addTimetracking),
// v40 -> v41
NewMigration("migrate protected branch struct", migrateProtectedBranchStruct),
// v41 -> v42
NewMigration("add default value to user prohibit_login", addDefaultValueToUserProhibitLogin),
// v42 -> v43
NewMigration("add tags to releases and sync existing repositories", releaseAddColumnIsTagAndSyncTags),
// v43 -> v44
// v40 -> v41
NewMigration("fix protected branch can push value to false", fixProtectedBranchCanPushValue),
// v44 -> v45
// v41 -> v42
NewMigration("remove duplicate unit types", removeDuplicateUnitTypes),
// v42 -> v43
NewMigration("empty step", emptyMigration),
// v43 -> v44
NewMigration("empty step", emptyMigration),
// v44 -> v45
NewMigration("empty step", emptyMigration),
// v45 -> v46
NewMigration("remove index column from repo_unit table", removeIndexColumnFromRepoUnitTable),
// v46 -> v47
@ -147,8 +151,14 @@ var migrations = []Migration{
// v48 -> v49
NewMigration("add repo indexer status", addRepoIndexerStatus),
// v49 -> v50
NewMigration("add lfs lock table", addLFSLock),
NewMigration("adds time tracking and stopwatches", addTimetracking),
// v50 -> v51
NewMigration("migrate protected branch struct", migrateProtectedBranchStruct),
// v51 -> v52
NewMigration("add default value to user prohibit_login", addDefaultValueToUserProhibitLogin),
// v52 -> v53
NewMigration("add lfs lock table", addLFSLock),
// v53 -> v54
NewMigration("add reactions", addReactions),
// v51 -> v52
NewMigration("add issue_dependencies", addIssueDependencies),

View File

@ -6,69 +6,52 @@ package migrations
import (
"fmt"
"time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm"
)
func addTimetracking(x *xorm.Engine) error {
// RepoUnit describes all units of a repository
type RepoUnit struct {
ID int64
RepoID int64 `xorm:"INDEX(s)"`
Type int `xorm:"INDEX(s)"`
Index int
Config map[string]interface{} `xorm:"JSON"`
CreatedUnix int64 `xorm:"INDEX CREATED"`
Created time.Time `xorm:"-"`
}
// ReleaseV39 describes the added field for Release
type ReleaseV39 struct {
IsTag bool `xorm:"NOT NULL DEFAULT false"`
}
// Stopwatch see models/issue_stopwatch.go
type Stopwatch struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"INDEX"`
UserID int64 `xorm:"INDEX"`
Created time.Time `xorm:"-"`
CreatedUnix int64
}
// TableName will be invoked by XORM to customrize the table name
func (*ReleaseV39) TableName() string {
return "release"
}
// TrackedTime see models/issue_tracked_time.go
type TrackedTime struct {
ID int64 `xorm:"pk autoincr" json:"id"`
IssueID int64 `xorm:"INDEX" json:"issue_id"`
UserID int64 `xorm:"INDEX" json:"user_id"`
Created time.Time `xorm:"-" json:"created"`
CreatedUnix int64 `json:"-"`
Time int64 `json:"time"`
}
if err := x.Sync2(new(Stopwatch)); err != nil {
func releaseAddColumnIsTagAndSyncTags(x *xorm.Engine) error {
if err := x.Sync2(new(ReleaseV39)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
if err := x.Sync2(new(TrackedTime)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
//Updating existing issue units
units := make([]*RepoUnit, 0, 100)
err := x.Where("`type` = ?", V16UnitTypeIssues).Find(&units)
if err != nil {
return fmt.Errorf("Query repo units: %v", err)
}
for _, unit := range units {
if unit.Config == nil {
unit.Config = make(map[string]interface{})
// For the sake of SQLite3, we can't use x.Iterate here.
offset := 0
pageSize := 20
for {
repos := make([]*models.Repository, 0, pageSize)
if err := x.Table("repository").Asc("id").Limit(pageSize, offset).Find(&repos); err != nil {
return fmt.Errorf("select repos [offset: %d]: %v", offset, err)
}
if _, ok := unit.Config["EnableTimetracker"]; !ok {
unit.Config["EnableTimetracker"] = setting.Service.DefaultEnableTimetracking
for _, repo := range repos {
gitRepo, err := git.OpenRepository(repo.RepoPath())
if err != nil {
log.Warn("OpenRepository: %v", err)
continue
}
if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
log.Warn("SyncReleasesWithTags: %v", err)
}
}
if _, ok := unit.Config["AllowOnlyContributorsToTrackTime"]; !ok {
unit.Config["AllowOnlyContributorsToTrackTime"] = setting.Service.DefaultAllowOnlyContributorsToTrackTime
}
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
return err
if len(repos) < pageSize {
break
}
offset += pageSize
}
return nil
}

View File

@ -6,50 +6,21 @@ package migrations
import (
"fmt"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm"
)
func migrateProtectedBranchStruct(x *xorm.Engine) error {
func fixProtectedBranchCanPushValue(x *xorm.Engine) error {
type ProtectedBranch struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s)"`
BranchName string `xorm:"UNIQUE(s)"`
CanPush bool
Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
CanPush bool `xorm:"NOT NULL DEFAULT false"`
}
var pbs []ProtectedBranch
err := x.Find(&pbs)
if err != nil {
return err
if err := x.Sync2(new(ProtectedBranch)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
for _, pb := range pbs {
if pb.CanPush {
if _, err = x.ID(pb.ID).Delete(new(ProtectedBranch)); err != nil {
return err
}
}
}
switch {
case setting.UseSQLite3:
log.Warn("Unable to drop columns in SQLite")
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB:
if _, err := x.Exec("ALTER TABLE protected_branch DROP COLUMN can_push"); err != nil {
return fmt.Errorf("DROP COLUMN can_push: %v", err)
}
default:
log.Fatal(4, "Unrecognized DB")
}
return nil
_, err := x.Cols("can_push").Update(&ProtectedBranch{
CanPush: false,
})
return err
}

View File

@ -7,36 +7,63 @@ package migrations
import (
"fmt"
"code.gitea.io/gitea/models"
"github.com/go-xorm/xorm"
)
func addDefaultValueToUserProhibitLogin(x *xorm.Engine) (err error) {
user := &models.User{
ProhibitLogin: false,
func removeDuplicateUnitTypes(x *xorm.Engine) error {
// RepoUnit describes all units of a repository
type RepoUnit struct {
RepoID int64
Type int
}
if _, err := x.Where("`prohibit_login` IS NULL").Cols("prohibit_login").Update(user); err != nil {
// Enumerate all the unit types
const (
UnitTypeCode = iota + 1 // 1 code
UnitTypeIssues // 2 issues
UnitTypePullRequests // 3 PRs
UnitTypeReleases // 4 Releases
UnitTypeWiki // 5 Wiki
UnitTypeExternalWiki // 6 ExternalWiki
UnitTypeExternalTracker // 7 ExternalTracker
)
var externalIssueRepoUnits []RepoUnit
err := x.Where("type = ?", UnitTypeExternalTracker).Find(&externalIssueRepoUnits)
if err != nil {
return fmt.Errorf("Query repositories: %v", err)
}
var externalWikiRepoUnits []RepoUnit
err = x.Where("type = ?", UnitTypeExternalWiki).Find(&externalWikiRepoUnits)
if err != nil {
return fmt.Errorf("Query repositories: %v", err)
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
dialect := x.Dialect().DriverName()
switch dialect {
case "mysql":
_, err = x.Exec("ALTER TABLE user MODIFY `prohibit_login` tinyint(1) NOT NULL DEFAULT 0")
case "postgres":
_, err = x.Exec("ALTER TABLE \"user\" ALTER COLUMN `prohibit_login` SET NOT NULL, ALTER COLUMN `prohibit_login` SET DEFAULT false")
case "mssql":
// xorm already set DEFAULT 0 for data type BIT in mssql
_, err = x.Exec(`ALTER TABLE [user] ALTER COLUMN "prohibit_login" BIT NOT NULL`)
case "sqlite3":
for _, repoUnit := range externalIssueRepoUnits {
if _, err = sess.Delete(&RepoUnit{
RepoID: repoUnit.RepoID,
Type: UnitTypeIssues,
}); err != nil {
return fmt.Errorf("Delete repo unit: %v", err)
}
}
if err != nil {
return fmt.Errorf("Error changing user prohibit_login column definition: %v", err)
for _, repoUnit := range externalWikiRepoUnits {
if _, err = sess.Delete(&RepoUnit{
RepoID: repoUnit.RepoID,
Type: UnitTypeWiki,
}); err != nil {
return fmt.Errorf("Delete repo unit: %v", err)
}
}
return err
return sess.Commit()
}

View File

@ -1,57 +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 migrations
import (
"fmt"
"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm"
)
// ReleaseV39 describes the added field for Release
type ReleaseV39 struct {
IsTag bool `xorm:"NOT NULL DEFAULT false"`
}
// TableName will be invoked by XORM to customrize the table name
func (*ReleaseV39) TableName() string {
return "release"
}
func releaseAddColumnIsTagAndSyncTags(x *xorm.Engine) error {
if err := x.Sync2(new(ReleaseV39)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
// For the sake of SQLite3, we can't use x.Iterate here.
offset := 0
pageSize := 20
for {
repos := make([]*models.Repository, 0, pageSize)
if err := x.Table("repository").Asc("id").Limit(pageSize, offset).Find(&repos); err != nil {
return fmt.Errorf("select repos [offset: %d]: %v", offset, err)
}
for _, repo := range repos {
gitRepo, err := git.OpenRepository(repo.RepoPath())
if err != nil {
log.Warn("OpenRepository: %v", err)
continue
}
if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
log.Warn("SyncReleasesWithTags: %v", err)
}
}
if len(repos) < pageSize {
break
}
offset += pageSize
}
return nil
}

View File

@ -1,26 +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 migrations
import (
"fmt"
"github.com/go-xorm/xorm"
)
func fixProtectedBranchCanPushValue(x *xorm.Engine) error {
type ProtectedBranch struct {
CanPush bool `xorm:"NOT NULL DEFAULT false"`
}
if err := x.Sync2(new(ProtectedBranch)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
_, err := x.Cols("can_push").Update(&ProtectedBranch{
CanPush: false,
})
return err
}

View File

@ -1,69 +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 migrations
import (
"fmt"
"github.com/go-xorm/xorm"
)
func removeDuplicateUnitTypes(x *xorm.Engine) error {
// RepoUnit describes all units of a repository
type RepoUnit struct {
RepoID int64
Type int
}
// Enumerate all the unit types
const (
UnitTypeCode = iota + 1 // 1 code
UnitTypeIssues // 2 issues
UnitTypePullRequests // 3 PRs
UnitTypeReleases // 4 Releases
UnitTypeWiki // 5 Wiki
UnitTypeExternalWiki // 6 ExternalWiki
UnitTypeExternalTracker // 7 ExternalTracker
)
var externalIssueRepoUnits []RepoUnit
err := x.Where("type = ?", UnitTypeExternalTracker).Find(&externalIssueRepoUnits)
if err != nil {
return fmt.Errorf("Query repositories: %v", err)
}
var externalWikiRepoUnits []RepoUnit
err = x.Where("type = ?", UnitTypeExternalWiki).Find(&externalWikiRepoUnits)
if err != nil {
return fmt.Errorf("Query repositories: %v", err)
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
for _, repoUnit := range externalIssueRepoUnits {
if _, err = sess.Delete(&RepoUnit{
RepoID: repoUnit.RepoID,
Type: UnitTypeIssues,
}); err != nil {
return fmt.Errorf("Delete repo unit: %v", err)
}
}
for _, repoUnit := range externalWikiRepoUnits {
if _, err = sess.Delete(&RepoUnit{
RepoID: repoUnit.RepoID,
Type: UnitTypeWiki,
}); err != nil {
return fmt.Errorf("Delete repo unit: %v", err)
}
}
return sess.Commit()
}

View File

@ -5,10 +5,9 @@
package migrations
import (
"fmt"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm"
)
@ -18,7 +17,8 @@ func removeIndexColumnFromRepoUnitTable(x *xorm.Engine) (err error) {
log.Warn("Unable to drop columns in SQLite")
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB:
if _, err := x.Exec("ALTER TABLE repo_unit DROP COLUMN `index`"); err != nil {
return fmt.Errorf("DROP COLUMN index: %v", err)
// Ignoring this error in case we run this migration second time (after migration reordering)
log.Warn("DROP COLUMN index: %v", err)
}
default:
log.Fatal(4, "Unrecognized DB")

View File

@ -8,24 +8,66 @@ import (
"fmt"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm"
)
func addLFSLock(x *xorm.Engine) error {
// LFSLock see models/lfs_lock.go
type LFSLock struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX NOT NULL"`
Owner *models.User `xorm:"-"`
OwnerID int64 `xorm:"INDEX NOT NULL"`
Path string `xorm:"TEXT"`
Created time.Time `xorm:"created"`
func addTimetracking(x *xorm.Engine) error {
// RepoUnit describes all units of a repository
type RepoUnit struct {
ID int64
RepoID int64 `xorm:"INDEX(s)"`
Type int `xorm:"INDEX(s)"`
Config map[string]interface{} `xorm:"JSON"`
CreatedUnix int64 `xorm:"INDEX CREATED"`
Created time.Time `xorm:"-"`
}
if err := x.Sync2(new(LFSLock)); err != nil {
// Stopwatch see models/issue_stopwatch.go
type Stopwatch struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"INDEX"`
UserID int64 `xorm:"INDEX"`
Created time.Time `xorm:"-"`
CreatedUnix int64
}
// TrackedTime see models/issue_tracked_time.go
type TrackedTime struct {
ID int64 `xorm:"pk autoincr" json:"id"`
IssueID int64 `xorm:"INDEX" json:"issue_id"`
UserID int64 `xorm:"INDEX" json:"user_id"`
Created time.Time `xorm:"-" json:"created"`
CreatedUnix int64 `json:"-"`
Time int64 `json:"time"`
}
if err := x.Sync2(new(Stopwatch)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
if err := x.Sync2(new(TrackedTime)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
//Updating existing issue units
units := make([]*RepoUnit, 0, 100)
err := x.Where("`type` = ?", V16UnitTypeIssues).Find(&units)
if err != nil {
return fmt.Errorf("Query repo units: %v", err)
}
for _, unit := range units {
if unit.Config == nil {
unit.Config = make(map[string]interface{})
}
if _, ok := unit.Config["EnableTimetracker"]; !ok {
unit.Config["EnableTimetracker"] = setting.Service.DefaultEnableTimetracking
}
if _, ok := unit.Config["AllowOnlyContributorsToTrackTime"]; !ok {
unit.Config["AllowOnlyContributorsToTrackTime"] = setting.Service.DefaultAllowOnlyContributorsToTrackTime
}
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
return err
}
}
return nil
}

View File

@ -5,24 +5,51 @@
package migrations
import (
"fmt"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm"
)
func addReactions(x *xorm.Engine) error {
// Reaction see models/issue_reaction.go
type Reaction struct {
func migrateProtectedBranchStruct(x *xorm.Engine) error {
type ProtectedBranch struct {
ID int64 `xorm:"pk autoincr"`
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CreatedUnix int64 `xorm:"INDEX created"`
RepoID int64 `xorm:"UNIQUE(s)"`
BranchName string `xorm:"UNIQUE(s)"`
CanPush bool
Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
}
if err := x.Sync2(new(Reaction)); err != nil {
return fmt.Errorf("Sync2: %v", err)
var pbs []ProtectedBranch
err := x.Find(&pbs)
if err != nil {
return err
}
for _, pb := range pbs {
if pb.CanPush {
if _, err = x.ID(pb.ID).Delete(new(ProtectedBranch)); err != nil {
return err
}
}
}
switch {
case setting.UseSQLite3:
log.Warn("Unable to drop columns in SQLite")
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB:
if _, err := x.Exec("ALTER TABLE protected_branch DROP COLUMN can_push"); err != nil {
// Ignoring this error in case we run this migration second time (after migration reordering)
log.Warn("DROP COLUMN can_push (skipping): %v", err)
}
default:
log.Fatal(4, "Unrecognized DB")
}
return nil
}

View File

@ -5,56 +5,38 @@
package migrations
import (
"fmt"
"time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm"
)
func addIssueDependencies(x *xorm.Engine) (err error) {
type IssueDependency struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL"`
IssueID int64 `xorm:"NOT NULL"`
DependencyID int64 `xorm:"NOT NULL"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"updated"`
func addDefaultValueToUserProhibitLogin(x *xorm.Engine) (err error) {
user := &models.User{
ProhibitLogin: false,
}
if err = x.Sync(new(IssueDependency)); err != nil {
return fmt.Errorf("Error creating issue_dependency_table column definition: %v", err)
if _, err := x.Where("`prohibit_login` IS NULL").Cols("prohibit_login").Update(user); err != nil {
return err
}
// RepoUnit describes all units of a repository
type RepoUnit struct {
ID int64
RepoID int64 `xorm:"INDEX(s)"`
Type int `xorm:"INDEX(s)"`
Config map[string]interface{} `xorm:"JSON"`
CreatedUnix int64 `xorm:"INDEX CREATED"`
Created time.Time `xorm:"-"`
dialect := x.Dialect().DriverName()
switch dialect {
case "mysql":
_, err = x.Exec("ALTER TABLE user MODIFY `prohibit_login` tinyint(1) NOT NULL DEFAULT 0")
case "postgres":
_, err = x.Exec("ALTER TABLE \"user\" ALTER COLUMN `prohibit_login` SET NOT NULL, ALTER COLUMN `prohibit_login` SET DEFAULT false")
case "mssql":
// xorm already set DEFAULT 0 for data type BIT in mssql
_, err = x.Exec(`ALTER TABLE [user] ALTER COLUMN "prohibit_login" BIT NOT NULL`)
case "sqlite3":
}
//Updating existing issue units
units := make([]*RepoUnit, 0, 100)
err = x.Where("`type` = ?", V16UnitTypeIssues).Find(&units)
if err != nil {
return fmt.Errorf("Query repo units: %v", err)
}
for _, unit := range units {
if unit.Config == nil {
unit.Config = make(map[string]interface{})
}
if _, ok := unit.Config["EnableDependencies"]; !ok {
unit.Config["EnableDependencies"] = setting.Service.DefaultEnableDependencies
}
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
return err
}
// Ignoring this error in case we run this migration second time (after migration reordering)
log.Warn("Error changing user prohibit_login column definition (skipping): %v", err)
}
return err
return nil
}

31
models/migrations/v52.go Normal file
View File

@ -0,0 +1,31 @@
// 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 migrations
import (
"fmt"
"time"
"code.gitea.io/gitea/models"
"github.com/go-xorm/xorm"
)
func addLFSLock(x *xorm.Engine) error {
// LFSLock see models/lfs_lock.go
type LFSLock struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX NOT NULL"`
Owner *models.User `xorm:"-"`
OwnerID int64 `xorm:"INDEX NOT NULL"`
Path string `xorm:"TEXT"`
Created time.Time `xorm:"created"`
}
if err := x.Sync2(new(LFSLock)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
return nil
}

28
models/migrations/v53.go Normal file
View File

@ -0,0 +1,28 @@
// 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 migrations
import (
"fmt"
"github.com/go-xorm/xorm"
)
func addReactions(x *xorm.Engine) error {
// Reaction see models/issue_reaction.go
type Reaction struct {
ID int64 `xorm:"pk autoincr"`
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CreatedUnix int64 `xorm:"INDEX created"`
}
if err := x.Sync2(new(Reaction)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
return nil
}

1
models/migrations/v54.go Normal file
View File

@ -0,0 +1 @@
package migrations

View File

@ -6,7 +6,8 @@ package models
import (
"fmt"
"time"
"code.gitea.io/gitea/modules/util"
)
type (
@ -51,32 +52,8 @@ type Notification struct {
Issue *Issue `xorm:"-"`
Repository *Repository `xorm:"-"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX NOT NULL"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX NOT NULL"`
}
// BeforeInsert runs while inserting a record
func (n *Notification) BeforeInsert() {
var (
now = time.Now()
nowUnix = now.Unix()
)
n.Created = now
n.CreatedUnix = nowUnix
n.Updated = now
n.UpdatedUnix = nowUnix
}
// BeforeUpdate runs while updating a record
func (n *Notification) BeforeUpdate() {
var (
now = time.Now()
nowUnix = now.Unix()
)
n.Updated = now
n.UpdatedUnix = nowUnix
CreatedUnix util.TimeStamp `xorm:"created INDEX NOT NULL"`
UpdatedUnix util.TimeStamp `xorm:"updated INDEX NOT NULL"`
}
// CreateOrUpdateIssueNotifications creates an issue notification
@ -212,6 +189,7 @@ func getIssueNotification(e Engine, userID, issueID int64) (*Notification, error
func NotificationsForUser(user *User, statuses []NotificationStatus, page, perPage int) ([]*Notification, error) {
return notificationsForUser(x, user, statuses, page, perPage)
}
func notificationsForUser(e Engine, user *User, statuses []NotificationStatus, page, perPage int) (notifications []*Notification, err error) {
if len(statuses) == 0 {
return
@ -311,3 +289,13 @@ func getNotificationByID(notificationID int64) (*Notification, error) {
return notification, nil
}
// UpdateNotificationStatuses updates the statuses of all of a user's notifications that are of the currentStatus type to the desiredStatus
func UpdateNotificationStatuses(user *User, currentStatus NotificationStatus, desiredStatus NotificationStatus) error {
n := &Notification{Status: desiredStatus, UpdatedBy: user.ID}
_, err := x.
Where("user_id = ? AND status = ?", user.ID, currentStatus).
Cols("status", "updated_by", "updated_unix").
Update(n)
return err
}

View File

@ -31,9 +31,11 @@ func TestNotificationsForUser(t *testing.T) {
statuses := []NotificationStatus{NotificationStatusRead, NotificationStatusUnread}
notfs, err := NotificationsForUser(user, statuses, 1, 10)
assert.NoError(t, err)
if assert.Len(t, notfs, 1) {
if assert.Len(t, notfs, 2) {
assert.EqualValues(t, 2, notfs[0].ID)
assert.EqualValues(t, user.ID, notfs[0].UserID)
assert.EqualValues(t, 4, notfs[1].ID)
assert.EqualValues(t, user.ID, notfs[1].UserID)
}
}
@ -57,12 +59,12 @@ func TestNotification_GetIssue(t *testing.T) {
func TestGetNotificationCount(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
cnt, err := GetNotificationCount(user, NotificationStatusUnread)
user := AssertExistsAndLoadBean(t, &User{ID: 1}).(*User)
cnt, err := GetNotificationCount(user, NotificationStatusRead)
assert.NoError(t, err)
assert.EqualValues(t, 0, cnt)
cnt, err = GetNotificationCount(user, NotificationStatusRead)
cnt, err = GetNotificationCount(user, NotificationStatusUnread)
assert.NoError(t, err)
assert.EqualValues(t, 1, cnt)
}
@ -79,3 +81,21 @@ func TestSetNotificationStatus(t *testing.T) {
assert.Error(t, SetNotificationStatus(1, user, NotificationStatusRead))
assert.Error(t, SetNotificationStatus(NonexistentID, user, NotificationStatusRead))
}
func TestUpdateNotificationStatuses(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
notfUnread := AssertExistsAndLoadBean(t,
&Notification{UserID: user.ID, Status: NotificationStatusUnread}).(*Notification)
notfRead := AssertExistsAndLoadBean(t,
&Notification{UserID: user.ID, Status: NotificationStatusRead}).(*Notification)
notfPinned := AssertExistsAndLoadBean(t,
&Notification{UserID: user.ID, Status: NotificationStatusPinned}).(*Notification)
assert.NoError(t, UpdateNotificationStatuses(user, NotificationStatusUnread, NotificationStatusRead))
AssertExistsAndLoadBean(t,
&Notification{ID: notfUnread.ID, Status: NotificationStatusRead})
AssertExistsAndLoadBean(t,
&Notification{ID: notfRead.ID, Status: NotificationStatusRead})
AssertExistsAndLoadBean(t,
&Notification{ID: notfPinned.ID, Status: NotificationStatusPinned})
}

View File

@ -453,7 +453,12 @@ func RemoveOrgUser(orgID, userID int64) error {
return err
}
if t.NumMembers == 1 {
return ErrLastOrgOwner{UID: userID}
if err := t.GetMembers(); err != nil {
return err
}
if t.Members[0].ID == userID {
return ErrLastOrgOwner{UID: userID}
}
}
}

View File

@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
"github.com/Unknwon/com"
@ -67,27 +68,11 @@ type PullRequest struct {
BaseBranch string
MergeBase string `xorm:"VARCHAR(40)"`
HasMerged bool `xorm:"INDEX"`
MergedCommitID string `xorm:"VARCHAR(40)"`
MergerID int64 `xorm:"INDEX"`
Merger *User `xorm:"-"`
Merged time.Time `xorm:"-"`
MergedUnix int64 `xorm:"INDEX"`
}
// BeforeUpdate is invoked from XORM before updating an object of this type.
func (pr *PullRequest) BeforeUpdate() {
pr.MergedUnix = pr.Merged.Unix()
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
// Note: don't try to get Issue because will end up recursive querying.
func (pr *PullRequest) AfterLoad() {
if !pr.HasMerged {
return
}
pr.Merged = time.Unix(pr.MergedUnix, 0).Local()
HasMerged bool `xorm:"INDEX"`
MergedCommitID string `xorm:"VARCHAR(40)"`
MergerID int64 `xorm:"INDEX"`
Merger *User `xorm:"-"`
MergedUnix util.TimeStamp `xorm:"updated INDEX"`
}
// Note: don't try to get Issue because will end up recursive querying.
@ -194,8 +179,8 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
Base: apiBaseBranchInfo,
Head: apiHeadBranchInfo,
MergeBase: pr.MergeBase,
Created: &pr.Issue.Created,
Updated: &pr.Issue.Updated,
Created: pr.Issue.CreatedUnix.AsTimePtr(),
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
}
if pr.Status != PullRequestStatusChecking {
@ -203,7 +188,7 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
apiPullRequest.Mergeable = mergeable
}
if pr.HasMerged {
apiPullRequest.Merged = &pr.Merged
apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
apiPullRequest.MergedCommitID = &pr.MergedCommitID
apiPullRequest.MergedBy = pr.Merger.APIFormat()
}
@ -330,7 +315,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
return fmt.Errorf("GetBranchCommit: %v", err)
}
pr.Merged = time.Now()
pr.MergedUnix = util.TimeStampNow()
pr.Merger = doer
pr.MergerID = doer.ID
@ -396,7 +381,7 @@ func (pr *PullRequest) setMerged() (err error) {
if pr.HasMerged {
return fmt.Errorf("PullRequest[%d] already merged", pr.Index)
}
if pr.MergedCommitID == "" || pr.Merged.IsZero() || pr.Merger == nil {
if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
return fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
}
@ -442,7 +427,7 @@ func (pr *PullRequest) manuallyMerged() bool {
}
if commit != nil {
pr.MergedCommitID = commit.ID.String()
pr.Merged = commit.Author.When
pr.MergedUnix = util.TimeStamp(commit.Author.When.Unix())
pr.Status = PullRequestStatusManuallyMerged
merger, _ := GetUserByEmail(commit.Author.Email)

View File

@ -8,11 +8,11 @@ import (
"fmt"
"sort"
"strings"
"time"
"code.gitea.io/git"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/builder"
)
@ -30,28 +30,13 @@ type Release struct {
Title string
Sha1 string `xorm:"VARCHAR(40)"`
NumCommits int64
NumCommitsBehind int64 `xorm:"-"`
Note string `xorm:"TEXT"`
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
IsTag bool `xorm:"NOT NULL DEFAULT false"`
Attachments []*Attachment `xorm:"-"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX"`
}
// BeforeInsert is invoked from XORM before inserting an object of this type.
func (r *Release) BeforeInsert() {
if r.CreatedUnix == 0 {
r.CreatedUnix = time.Now().Unix()
}
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (r *Release) AfterLoad() {
r.Created = time.Unix(r.CreatedUnix, 0).Local()
NumCommitsBehind int64 `xorm:"-"`
Note string `xorm:"TEXT"`
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
IsTag bool `xorm:"NOT NULL DEFAULT false"`
Attachments []*Attachment `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"created INDEX"`
}
func (r *Release) loadAttributes(e Engine) error {
@ -104,8 +89,8 @@ func (r *Release) APIFormat() *api.Release {
ZipURL: r.ZipURL(),
IsDraft: r.IsDraft,
IsPrerelease: r.IsPrerelease,
CreatedAt: r.Created,
PublishedAt: r.Created,
CreatedAt: r.CreatedUnix.AsTime(),
PublishedAt: r.CreatedUnix.AsTime(),
Publisher: r.Publisher.APIFormat(),
}
}
@ -144,7 +129,7 @@ func createTag(gitRepo *git.Repository, rel *Release) error {
}
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = commit.Author.When.Unix()
rel.CreatedUnix = util.TimeStamp(commit.Author.When.Unix())
rel.NumCommits, err = commit.CommitsCount()
if err != nil {
return fmt.Errorf("CommitsCount: %v", err)
@ -345,7 +330,7 @@ func (rs *releaseSorter) Less(i, j int) bool {
if diffNum != 0 {
return diffNum > 0
}
return rs.rels[i].Created.After(rs.rels[j].Created)
return rs.rels[i].CreatedUnix > rs.rels[j].CreatedUnix
}
func (rs *releaseSorter) Swap(i, j int) {

View File

@ -27,6 +27,7 @@ import (
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
"github.com/Unknwon/cae/zip"
@ -211,10 +212,8 @@ type Repository struct {
Size int64 `xorm:"NOT NULL DEFAULT 0"`
IndexerStatus *RepoIndexerStatus `xorm:"-"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
@ -227,8 +226,6 @@ func (repo *Repository) AfterLoad() {
repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls
repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
repo.Created = time.Unix(repo.CreatedUnix, 0).Local()
repo.Updated = time.Unix(repo.UpdatedUnix, 0)
}
// MustOwner always returns a valid *User object to avoid
@ -309,8 +306,8 @@ func (repo *Repository) innerAPIFormat(mode AccessMode, isParent bool) *api.Repo
Watchers: repo.NumWatches,
OpenIssues: repo.NumOpenIssues,
DefaultBranch: repo.DefaultBranch,
Created: repo.Created,
Updated: repo.Updated,
Created: repo.CreatedUnix.AsTime(),
Updated: repo.UpdatedUnix.AsTime(),
Permissions: permission,
}
}
@ -757,12 +754,17 @@ func (repo *Repository) DescriptionHTML() template.HTML {
return template.HTML(descPattern.ReplaceAllStringFunc(markup.Sanitize(repo.Description), sanitize))
}
// LocalCopyPath returns the local repository copy path
func (repo *Repository) LocalCopyPath() string {
// LocalCopyPath returns the local repository copy path.
func LocalCopyPath() string {
if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) {
return path.Join(setting.Repository.Local.LocalCopyPath, com.ToStr(repo.ID))
return setting.Repository.Local.LocalCopyPath
}
return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath, com.ToStr(repo.ID))
return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath)
}
// LocalCopyPath returns the local repository copy path for the given repo.
func (repo *Repository) LocalCopyPath() string {
return path.Join(LocalCopyPath(), com.ToStr(repo.ID))
}
// UpdateLocalCopyBranch pulls latest changes of given branch from repoPath to localPath.
@ -1006,10 +1008,10 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
if opts.IsMirror {
if _, err = x.InsertOne(&Mirror{
RepoID: repo.ID,
Interval: setting.Mirror.DefaultInterval,
EnablePrune: true,
NextUpdate: time.Now().Add(setting.Mirror.DefaultInterval),
RepoID: repo.ID,
Interval: setting.Mirror.DefaultInterval,
EnablePrune: true,
NextUpdateUnix: util.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval),
}); err != nil {
return repo, fmt.Errorf("InsertOne: %v", err)
}

View File

@ -100,10 +100,6 @@ func populateRepoIndexer() error {
}
}
type updateBatch struct {
updates []indexer.RepoIndexerUpdate
}
func updateRepoIndexer(repo *Repository) error {
changes, err := getRepoChanges(repo)
if err != nil {
@ -163,6 +159,10 @@ func addUpdate(filename string, repo *Repository, batch *indexer.Batch) error {
return err
} else if stat.Size() > setting.Indexer.MaxIndexerFileSize {
return nil
} else if stat.IsDir() {
// file could actually be a directory, if it is the root of a submodule.
// We do not index submodule contents, so don't do anything.
return nil
}
fileContents, err := ioutil.ReadFile(filepath)
if err != nil {

View File

@ -31,10 +31,8 @@ type Mirror struct {
Interval time.Duration
EnablePrune bool `xorm:"NOT NULL DEFAULT true"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX"`
NextUpdate time.Time `xorm:"-"`
NextUpdateUnix int64 `xorm:"INDEX"`
UpdatedUnix util.TimeStamp `xorm:"INDEX"`
NextUpdateUnix util.TimeStamp `xorm:"INDEX"`
address string `xorm:"-"`
}
@ -42,16 +40,8 @@ type Mirror struct {
// BeforeInsert will be invoked by XORM before inserting a record
func (m *Mirror) BeforeInsert() {
if m != nil {
m.UpdatedUnix = time.Now().Unix()
m.NextUpdateUnix = m.NextUpdate.Unix()
}
}
// BeforeUpdate is invoked from XORM before updating this object.
func (m *Mirror) BeforeUpdate() {
if m != nil {
m.UpdatedUnix = m.Updated.Unix()
m.NextUpdateUnix = m.NextUpdate.Unix()
m.UpdatedUnix = util.TimeStampNow()
m.NextUpdateUnix = util.TimeStampNow()
}
}
@ -66,14 +56,11 @@ func (m *Mirror) AfterLoad(session *xorm.Session) {
if err != nil {
log.Error(3, "getRepositoryByID[%d]: %v", m.ID, err)
}
m.Updated = time.Unix(m.UpdatedUnix, 0).Local()
m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local()
}
// ScheduleNextUpdate calculates and sets next update time.
func (m *Mirror) ScheduleNextUpdate() {
m.NextUpdate = time.Now().Add(m.Interval)
m.NextUpdateUnix = util.TimeStampNow().AddDuration(m.Interval)
}
func remoteAddress(repoPath string) (string, error) {
@ -193,7 +180,7 @@ func (m *Mirror) runSync() bool {
}
}
m.Updated = time.Now()
m.UpdatedUnix = util.TimeStampNow()
return true
}

View File

@ -6,7 +6,8 @@ package models
import (
"encoding/json"
"time"
"code.gitea.io/gitea/modules/util"
"github.com/Unknwon/com"
"github.com/go-xorm/core"
@ -19,8 +20,7 @@ type RepoUnit struct {
RepoID int64 `xorm:"INDEX(s)"`
Type UnitType `xorm:"INDEX(s)"`
Config core.Conversion `xorm:"TEXT"`
CreatedUnix int64 `xorm:"INDEX CREATED"`
Created time.Time `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"INDEX CREATED"`
}
// UnitConfig describes common unit config
@ -106,11 +106,6 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
}
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (r *RepoUnit) AfterLoad() {
r.Created = time.Unix(r.CreatedUnix, 0).Local()
}
// Unit returns Unit
func (r *RepoUnit) Unit() Unit {
return Units[r.Type]

View File

@ -25,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
const (
@ -54,20 +55,16 @@ type PublicKey struct {
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"updated"`
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"created"`
UpdatedUnix util.TimeStamp `xorm:"updated"`
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (key *PublicKey) AfterLoad() {
key.Created = time.Unix(key.CreatedUnix, 0).Local()
key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
key.HasUsed = key.Updated.After(key.Created)
key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
key.HasUsed = key.UpdatedUnix > key.CreatedUnix
key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > util.TimeStampNow()
}
// OmitEmail returns content of public key without email address.
@ -484,7 +481,7 @@ func UpdatePublicKeyUpdated(id int64) error {
}
_, err := x.ID(id).Cols("updated_unix").Update(&PublicKey{
UpdatedUnix: time.Now().Unix(),
UpdatedUnix: util.TimeStampNow(),
})
if err != nil {
return err
@ -603,20 +600,16 @@ type DeployKey struct {
Fingerprint string
Content string `xorm:"-"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"updated"`
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"created"`
UpdatedUnix util.TimeStamp `xorm:"updated"`
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (key *DeployKey) AfterLoad() {
key.Created = time.Unix(key.CreatedUnix, 0).Local()
key.Updated = time.Unix(key.UpdatedUnix, 0).Local()
key.HasUsed = key.Updated.After(key.Created)
key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
key.HasUsed = key.UpdatedUnix > key.CreatedUnix
key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > util.TimeStampNow()
}
// GetContent gets associated public key content.
@ -743,6 +736,12 @@ func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
return key, nil
}
// UpdateDeployKeyCols updates deploy key information in the specified columns.
func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
_, err := x.ID(key.ID).Cols(cols...).Update(key)
return err
}
// UpdateDeployKey updates deploy key information.
func UpdateDeployKey(key *DeployKey) error {
_, err := x.ID(key.ID).AllCols().Update(key)

View File

@ -8,11 +8,11 @@ import (
"container/list"
"fmt"
"strings"
"time"
"code.gitea.io/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/xorm"
@ -65,17 +65,8 @@ type CommitStatus struct {
Creator *User `xorm:"-"`
CreatorID int64
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
}
// AfterLoad is invoked from XORM after setting the value of a field of
// this object.
func (status *CommitStatus) AfterLoad() {
status.Created = time.Unix(status.CreatedUnix, 0).Local()
status.Updated = time.Unix(status.UpdatedUnix, 0).Local()
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
}
func (status *CommitStatus) loadRepo(e Engine) (err error) {
@ -106,8 +97,8 @@ func (status *CommitStatus) APIURL() string {
func (status *CommitStatus) APIFormat() *api.Status {
status.loadRepo(x)
apiStatus := &api.Status{
Created: status.Created,
Updated: status.Created,
Created: status.CreatedUnix.AsTime(),
Updated: status.CreatedUnix.AsTime(),
State: api.StatusState(status.State),
TargetURL: status.TargetURL,
Description: status.Description,

View File

@ -10,6 +10,7 @@ import (
gouuid "github.com/satori/go.uuid"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/util"
)
// AccessToken represents a personal access token.
@ -19,20 +20,16 @@ type AccessToken struct {
Name string
Sha1 string `xorm:"UNIQUE VARCHAR(40)"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (t *AccessToken) AfterLoad() {
t.Created = time.Unix(t.CreatedUnix, 0).Local()
t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
t.HasUsed = t.Updated.After(t.Created)
t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
t.HasUsed = t.UpdatedUnix > t.CreatedUnix
t.HasRecentActivity = t.UpdatedUnix.AddDuration(7*24*time.Hour) > util.TimeStampNow()
}
// NewAccessToken creates new access token.

View File

@ -8,13 +8,13 @@ import (
"crypto/md5"
"crypto/subtle"
"encoding/base64"
"time"
"github.com/Unknwon/com"
"github.com/pquerna/otp/totp"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
// TwoFactor represents a two-factor authentication token.
@ -23,17 +23,8 @@ type TwoFactor struct {
UID int64 `xorm:"UNIQUE"`
Secret string
ScratchToken string
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (t *TwoFactor) AfterLoad() {
t.Created = time.Unix(t.CreatedUnix, 0).Local()
t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
}
// GenerateScratchToken recreates the scratch token the user is using.

View File

@ -15,9 +15,9 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" // for the test engine
"github.com/stretchr/testify/assert"
"gopkg.in/testfixtures.v2"
"net/url"
)
// NonexistentID an ID that will never exist
@ -29,9 +29,10 @@ var giteaRoot string
// MainTest a reusable TestMain(..) function for unit tests that need to use a
// test database. Creates the test database, and sets necessary settings.
func MainTest(m *testing.M, pathToGiteaRoot string) {
var err error
giteaRoot = pathToGiteaRoot
fixturesDir := filepath.Join(pathToGiteaRoot, "models", "fixtures")
if err := createTestEngine(fixturesDir); err != nil {
if err = createTestEngine(fixturesDir); err != nil {
fmt.Fprintf(os.Stderr, "Error creating test engine: %v\n", err)
os.Exit(1)
}
@ -42,6 +43,13 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
setting.SSH.Domain = "try.gitea.io"
setting.RepoRootPath = filepath.Join(os.TempDir(), "repos")
setting.AppDataPath = filepath.Join(os.TempDir(), "appdata")
setting.AppWorkPath = pathToGiteaRoot
setting.StaticRootPath = pathToGiteaRoot
setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
if err != nil {
fmt.Fprintf(os.Stderr, "Error url.Parse: %v\n", err)
os.Exit(1)
}
os.Exit(m.Run())
}
@ -141,6 +149,14 @@ func AssertNotExistsBean(t *testing.T, bean interface{}, conditions ...interface
assert.False(t, exists)
}
// AssertExistsIf asserts that a bean exists or does not exist, depending on
// what is expected.
func AssertExistsIf(t *testing.T, expected bool, bean interface{}, conditions ...interface{}) {
exists, err := loadBeanIfExists(bean, conditions...)
assert.NoError(t, err)
assert.Equal(t, expected, exists)
}
// AssertSuccessfulInsert assert that beans is successfully inserted
func AssertSuccessfulInsert(t *testing.T, beans ...interface{}) {
_, err := x.Insert(beans...)

View File

@ -14,6 +14,7 @@ import (
"code.gitea.io/git"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
// env keys for git hooks need
@ -158,8 +159,7 @@ func pushUpdateAddTag(repo *Repository, gitRepo *git.Repository, tagName string)
IsDraft: false,
IsPrerelease: false,
IsTag: true,
Created: createdAt,
CreatedUnix: createdAt.Unix(),
CreatedUnix: util.TimeStamp(createdAt.Unix()),
}
if author != nil {
rel.PublisherID = author.ID
@ -170,8 +170,7 @@ func pushUpdateAddTag(repo *Repository, gitRepo *git.Repository, tagName string)
}
} else {
rel.Sha1 = commit.ID.String()
rel.Created = createdAt
rel.CreatedUnix = createdAt.Unix()
rel.CreatedUnix = util.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount
rel.IsDraft = false
if rel.IsTag && author != nil {

View File

@ -94,12 +94,9 @@ type User struct {
Rands string `xorm:"VARCHAR(10)"`
Salt string `xorm:"VARCHAR(10)"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
LastLogin time.Time `xorm:"-"`
LastLoginUnix int64 `xorm:"INDEX"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
LastLoginUnix util.TimeStamp `xorm:"INDEX"`
// Remember visibility choice for convenience, true for private
LastRepoVisibility bool
@ -145,7 +142,7 @@ func (u *User) BeforeUpdate() {
// SetLastLogin set time to last login
func (u *User) SetLastLogin() {
u.LastLoginUnix = time.Now().Unix()
u.LastLoginUnix = util.TimeStampNow()
}
// UpdateDiffViewStyle updates the users diff view style
@ -154,13 +151,6 @@ func (u *User) UpdateDiffViewStyle(style string) error {
return UpdateUserCols(u, "diff_view_style")
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (u *User) AfterLoad() {
u.Created = time.Unix(u.CreatedUnix, 0).Local()
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
u.LastLogin = time.Unix(u.LastLoginUnix, 0).Local()
}
// getEmail returns an noreply email, if the user has set to keep his
// email address private, otherwise the primary email address.
func (u *User) getEmail() string {

View File

@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
api "code.gitea.io/sdk/gitea"
gouuid "github.com/satori/go.uuid"
@ -105,10 +106,8 @@ type Webhook struct {
Meta string `xorm:"TEXT"` // store hook-specific attributes
LastStatus HookStatus // Last delivery status
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"INDEX updated"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
}
// AfterLoad updates the webhook object upon setting a column
@ -117,9 +116,6 @@ func (w *Webhook) AfterLoad() {
if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
log.Error(3, "Unmarshal[%d]: %v", w.ID, err)
}
w.Created = time.Unix(w.CreatedUnix, 0).Local()
w.Updated = time.Unix(w.UpdatedUnix, 0).Local()
}
// GetSlackHook returns slack metadata

View File

@ -7,7 +7,6 @@ package auth
import (
"reflect"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/go-macaron/binding"
@ -19,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
)
@ -59,7 +59,7 @@ func SignedInID(ctx *macaron.Context, sess session.Store) int64 {
}
return 0
}
t.Updated = time.Now()
t.UpdatedUnix = util.TimeStampNow()
if err = models.UpdateAccessToken(t); err != nil {
log.Error(4, "UpdateAccessToken: %v", err)
}

View File

@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/Unknwon/com"
"github.com/Unknwon/i18n"
"github.com/gogits/chardet"
@ -357,11 +358,15 @@ func timeSincePro(then, now time.Time, lang string) string {
}
func timeSince(then, now time.Time, lang string) string {
return timeSinceUnix(then.Unix(), now.Unix(), lang)
}
func timeSinceUnix(then, now int64, lang string) string {
lbl := "tool.ago"
diff := now.Unix() - then.Unix()
if then.After(now) {
diff := now - then
if then > now {
lbl = "tool.from_now"
diff = then.Unix() - now.Unix()
diff = then - now
}
if diff <= 0 {
return i18n.Tr(lang, "tool.now")
@ -387,6 +392,17 @@ func htmlTimeSince(then, now time.Time, lang string) template.HTML {
timeSince(then, now, lang)))
}
// TimeSinceUnix calculates the time interval and generate user-friendly string.
func TimeSinceUnix(then util.TimeStamp, lang string) template.HTML {
return htmlTimeSinceUnix(then, util.TimeStamp(time.Now().Unix()), lang)
}
func htmlTimeSinceUnix(then, now util.TimeStamp, lang string) template.HTML {
return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`,
then.Format(setting.TimeFormat),
timeSinceUnix(int64(then), int64(now), lang)))
}
// Storage space size types
const (
Byte = 1

View File

@ -435,7 +435,6 @@ func RepoAssignment() macaron.Handler {
return
}
}
ctx.Data["IsForkedRepo"] = repo.IsFork
// People who have push access or have forked repository can propose a new pull request.
if ctx.Repo.IsWriter() || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) {
@ -626,7 +625,11 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
if refType == RepoRefLegacy {
// redirect from old URL scheme to new URL scheme
ctx.Redirect(path.Join(setting.AppSubURL, strings.TrimSuffix(ctx.Req.URL.String(), ctx.Params("*")), ctx.Repo.BranchNameSubURL()))
ctx.Redirect(path.Join(
setting.AppSubURL,
strings.TrimSuffix(ctx.Req.URL.String(), ctx.Params("*")),
ctx.Repo.BranchNameSubURL(),
ctx.Repo.TreePath))
return
}
}

View File

@ -68,12 +68,12 @@ type ObjectError struct {
// ObjectLink builds a URL linking to the object.
func (v *RequestVars) ObjectLink() string {
return setting.AppURL + path.Join(v.User, v.Repo, "info/lfs/objects", v.Oid)
return setting.AppURL + path.Join(v.User, v.Repo+".git", "info/lfs/objects", v.Oid)
}
// VerifyLink builds a URL for verifying the object.
func (v *RequestVars) VerifyLink() string {
return setting.AppURL + path.Join(v.User, v.Repo, "info/lfs/verify")
return setting.AppURL + path.Join(v.User, v.Repo+".git", "info/lfs/verify")
}
// link provides a structure used to build a hypermedia representation of an HTTP link.

View File

@ -531,6 +531,9 @@ var (
IterateBufferSize int
ExternalMarkupParsers []MarkupParser
// UILocation is the location on the UI, so that we can display the time on UI.
// Currently only show the default time.Local, it could be added to app.ini after UI is ready
UILocation = time.Local
)
// DateLang transforms standard language locale name to corresponding value in datetime plugin.

View File

@ -65,14 +65,15 @@ func NewFuncMap() []template.FuncMap {
"LoadTimes": func(startTime time.Time) string {
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
},
"AvatarLink": base.AvatarLink,
"Safe": Safe,
"SafeJS": SafeJS,
"Str2html": Str2html,
"TimeSince": base.TimeSince,
"RawTimeSince": base.RawTimeSince,
"FileSize": base.FileSize,
"Subtract": base.Subtract,
"AvatarLink": base.AvatarLink,
"Safe": Safe,
"SafeJS": SafeJS,
"Str2html": Str2html,
"TimeSince": base.TimeSince,
"TimeSinceUnix": base.TimeSinceUnix,
"RawTimeSince": base.RawTimeSince,
"FileSize": base.FileSize,
"Subtract": base.Subtract,
"Add": func(a, b int) int {
return a + b
},

View File

@ -9,13 +9,14 @@ import (
"net/url"
"testing"
"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"github.com/go-macaron/session"
_ "github.com/mattn/go-sqlite3" // for the test engine
"github.com/stretchr/testify/assert"
"gopkg.in/macaron.v1"
"net/http/httptest"
)
// MockContext mock context for unit tests
@ -44,6 +45,7 @@ func MockContext(t *testing.T, path string) *context.Context {
func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
ctx.Repo = &context.Repository{}
ctx.Repo.Repository = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repoID}).(*models.Repository)
ctx.Repo.RepoLink = ctx.Repo.Repository.Link()
}
// LoadUser load a user into a test context.
@ -51,6 +53,15 @@ func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
ctx.User = models.AssertExistsAndLoadBean(t, &models.User{ID: userID}).(*models.User)
}
// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
// already been populated.
func LoadGitRepo(t *testing.T, ctx *context.Context) {
assert.NoError(t, ctx.Repo.Repository.GetOwner())
var err error
ctx.Repo.GitRepo, err = git.OpenRepository(ctx.Repo.Repository.RepoPath())
assert.NoError(t, err)
}
type mockLocale struct{}
func (l mockLocale) Language() string {
@ -62,32 +73,21 @@ func (l mockLocale) Tr(s string, _ ...interface{}) string {
}
type mockResponseWriter struct {
status int
size int
}
func (rw *mockResponseWriter) Header() http.Header {
return map[string][]string{}
httptest.ResponseRecorder
size int
}
func (rw *mockResponseWriter) Write(b []byte) (int, error) {
rw.size += len(b)
return len(b), nil
}
func (rw *mockResponseWriter) WriteHeader(status int) {
rw.status = status
}
func (rw *mockResponseWriter) Flush() {
return rw.ResponseRecorder.Write(b)
}
func (rw *mockResponseWriter) Status() int {
return rw.status
return rw.ResponseRecorder.Code
}
func (rw *mockResponseWriter) Written() bool {
return rw.status > 0
return rw.ResponseRecorder.Code > 0
}
func (rw *mockResponseWriter) Size() int {

14
modules/test/utils.go Normal file
View File

@ -0,0 +1,14 @@
// 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 test
import (
"net/http"
)
// RedirectURL returns the redirect URL of a http response.
func RedirectURL(resp http.ResponseWriter) string {
return resp.Header().Get("Location")
}

View File

@ -0,0 +1,61 @@
// 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 util
import (
"time"
"code.gitea.io/gitea/modules/setting"
)
// TimeStamp defines a timestamp
type TimeStamp int64
// TimeStampNow returns now int64
func TimeStampNow() TimeStamp {
return TimeStamp(time.Now().Unix())
}
// Add adds seconds and return sum
func (ts TimeStamp) Add(seconds int64) TimeStamp {
return ts + TimeStamp(seconds)
}
// AddDuration adds time.Duration and return sum
func (ts TimeStamp) AddDuration(interval time.Duration) TimeStamp {
return ts + TimeStamp(interval/time.Second)
}
// Year returns the time's year
func (ts TimeStamp) Year() int {
return ts.AsTime().Year()
}
// AsTime convert timestamp as time.Time in Local locale
func (ts TimeStamp) AsTime() (tm time.Time) {
tm = time.Unix(int64(ts), 0).In(setting.UILocation)
return
}
// AsTimePtr convert timestamp as *time.Time in Local locale
func (ts TimeStamp) AsTimePtr() *time.Time {
tm := time.Unix(int64(ts), 0).In(setting.UILocation)
return &tm
}
// Format formats timestamp as
func (ts TimeStamp) Format(f string) string {
return ts.AsTime().Format(f)
}
// FormatLong formats as RFC1123Z
func (ts TimeStamp) FormatLong() string {
return ts.Format(time.RFC1123Z)
}
// FormatShort formats as short
func (ts TimeStamp) FormatShort() string {
return ts.Format("Jan 02, 2006")
}

View File

@ -1569,6 +1569,7 @@ no_read = You do not have any read notifications.
pin = Pin notification
mark_as_read = Mark as read
mark_as_unread = Mark as unread
mark_all_as_read = Mark all as read
[gpg]
error.extract_sign = Failed to extract signature

View File

@ -489,6 +489,8 @@ mirror_last_synced=Dernière synchronisation
watchers=Observateurs
stargazers=Fans
forks=Bifurcations
pick_reaction=Choisissez votre réaction
reactions_more=et %d de plus
form.reach_limit_of_creation=Vous avez déjà atteint la limite des %d dépôts.
form.name_reserved=Le dépôt "%s" a un nom réservé.
@ -539,6 +541,7 @@ pulls=Demandes d'ajout
labels=Étiquettes
milestones=Jalons
commits=Révisions
commit=Commit
releases=Versions
file_raw=Brut
file_history=Historique
@ -804,6 +807,7 @@ wiki.new_page_button=Nouvelle Page
wiki.delete_page_button=Supprimer la page
wiki.delete_page_notice_1=Cela supprimera la page <code>"%s"</code>. Êtes-vous sûr ?
wiki.page_already_exists=Une page de wiki avec le même nom existe déjà.
wiki.reserved_page=Le nom de page Wiki %s est réservé, veuillez choisir un autre nom.
wiki.pages=Pages
wiki.last_updated=Dernière mise à jour: %s
@ -978,6 +982,7 @@ settings.slack_token=Jeton
settings.slack_domain=Domaine
settings.slack_channel=Canal
settings.add_discord_hook_desc=Ajouter l'intégration de <a href="%s">Discord</a> à votre dépôt.
settings.add_dingtalk_hook_desc=Intégrer <a href="%s">Dingtalk</a> à votre dépôt.
settings.deploy_keys=Clés de déploiement
settings.add_deploy_key=Ajouter une clé de déploiement
settings.deploy_key_desc=Les clés de déploiement ont un accès en lecture seule. Elles sont différentes des clés SSH personnelles.
@ -1542,6 +1547,7 @@ no_read=Vous n'avez aucune notification lue.
pin=Epingler la notification
mark_as_read=Marquer comme lu
mark_as_unread=Marquer comme non lue
mark_all_as_read=Tout marquer comme lu
[gpg]
error.extract_sign=Impossible d'extraire la signature

View File

@ -1547,6 +1547,7 @@ no_read=Nincsen olvasott értesítés.
pin=Értesítés kitűzése
mark_as_read=Megjelölés olvasottként
mark_as_unread=Megjelölés olvasatlanként
mark_all_as_read=Összes üzenet megjelölése olvasottként
[gpg]
error.extract_sign=Nem sikerült kinyerni az aláírást

View File

@ -489,6 +489,8 @@ mirror_last_synced=Pēdējo reizi sinhronizēts
watchers=Novērotāji
stargazers=Zvaigžņdevēji
forks=Atdalītie repozitoriji
pick_reaction=Izvēlieties reakciju
reactions_more=un vēl %d
form.reach_limit_of_creation=Ir sasniegts Jums noteiktais %d repozitoriju ierobežojums.
form.name_reserved=Repozitorija nosaukums '%s' ir jau rezervēts.
@ -539,6 +541,7 @@ pulls=Izmaiņu pieprasījumi
labels=Etiķetes
milestones=Atskaites punkti
commits=Revīzijas
commit=Revīzija
releases=Laidieni
file_raw=Neapstrādāts
file_history=Vēsture
@ -1544,6 +1547,7 @@ no_read=Jums nav neviena izlasīta paziņojuma.
pin=Piespraust paziņojumu
mark_as_read=Atzīmēt kā izlasītu
mark_as_unread=Atzīmēt kā nelasītu
mark_all_as_read=Atzīmēt visus kā izlasītus
[gpg]
error.extract_sign=Neizdevās izgūt parakstu

View File

@ -139,7 +139,6 @@ invalid_repo_path=Ścieżka repozytoriów nie jest poprawna: %v
run_user_not_match=Użytkownik aplikacji nie jest aktualnym użytkownikiem: %s -> %s
save_config_failed=Nie udało się zapisać konfiguracji: %v
invalid_admin_setting=Nieprawidłowe ustawienia konta admina: %v
install_success=Witaj! Dziękujemy za wybranie Gitea. Miłej zabawy. Trzymaj się!
invalid_log_root_path=Ścieżka dla logów jest niepoprawna: %v
default_keep_email_private=Domyślnie ukrywaj adresy e-mail
default_keep_email_private_popup=To jest domyślne ustawienie widoczności adresu e-mail użytkowników. Włączone spowoduje, że adres e-mail wszystkich nowych użytkowników zostanie domyślnie ukryty.
@ -201,8 +200,6 @@ non_local_account=Nie lokalne konta nie mogą zmieniać haseł przez webowy inte
verify=Potwierdź
scratch_code=Scratch kod
use_scratch_code=Użyj scratch kod
twofa_scratch_used=Użyłeś/aś swojego kodu zdrapki. Przekierowano Cię do strony z ustawieniami autoryzacji dwuetapowej, gdzie możesz usunąć usunąć swoje urządzenie lub wygenerować nowy kod zdrapkę.
twofa_passcode_incorrect=Twój kod autoryzacji jest niepoprawny. Jeśli zapodziałeś/aś swoje urządzenie, użyj swojego kodu zdrapki do zalogowania.
twofa_scratch_token_incorrect=Scratch token nie jest poprawny.
login_userpass=Użytkownik / Hasło
login_openid=OpenID
@ -434,11 +431,9 @@ twofa_disable_note=W razie potrzeby można wyłączyć uwierzytelnianie dwuetapo
twofa_disable_desc=Wyłączenie dwuetapowej autoryzacji sprawi, że Twoje konto będzie mniej bezpieczne. Czy na pewno chcesz kontynuować?
regenerate_scratch_token_desc=Jeśli zgubiłeś lub zużyłeś swój scratch token możesz go wygenerować tutaj.
twofa_disabled=Dwuetapowa autoryzacja została wyłączona.
scan_this_image=Zeskanuj ten obraz za pomocą swojej aplikacji autoryzacyjnej:
or_enter_secret=Lub wprowadź sekret: %s
then_enter_passcode=I podaj kod autoryzacji otrzymany z aplikacji:
passcode_invalid=Kod dostępu jest nieprawidłowy. Spróbuj ponownie.
twofa_enrolled=Twoje konto ma teraz włączoną autoryzację dwuetapową. Koniecznie zachowaj swój kod zdrapkę (%s), ponieważ będzie pokazany tylko raz!
manage_account_links=Zarządzaj połączonymi kontami
manage_account_links_desc=Zewnętrzne konta połączone z tym kontem
@ -460,7 +455,6 @@ owner=Właściciel
repo_name=Nazwa repozytorium
repo_name_helper=Dobra nazwa repozytorium jest utworzona z krótkich, łatwych do zapamiętania i unikalnych słów kluczowych.
visibility=Widoczność
visiblity_helper=Te repozytorium jest <span class="ui red text">prywatne</span>
visiblity_helper_forced=Administrator systemu wymaga, żeby wszystkie nowe repozytoria były <span class="ui red text">prywatne</span>
visiblity_fork_helper=(Zmiana tej wartości wpłynie na wszystkie forki)
clone_helper=Potrzebujesz pomocy z klonowaniem? Odwiedź <a target="_blank" rel="noopener" href="%s">Pomoc</a>!
@ -494,7 +488,6 @@ form.name_pattern_not_allowed=Wzorzec nazwy repozytorium „%s” jest niedozwol
need_auth=Wymaga autoryzacji
migrate_type=Typ migracji
migrate_type_helper=Te repozytorium będzie <span class="text blue">kopią lustrzaną</span>
migrate_repo=Przenieś repozytorium
migrate.clone_address=Sklonuj adres
migrate.clone_address_desc=To może być adres HTTP/HTTPS/GIT lub ścieżka lokalna serwera.
@ -550,7 +543,6 @@ editor.edit_file=Edytuj plik
editor.preview_changes=Podgląd zmian
editor.cannot_edit_non_text_files=Nie można edytować plików binarnych przez interfejs webowy
editor.edit_this_file=Edytuj ten plik
editor.must_be_on_a_branch=Musisz być na gałęzi aby zgłosić lub zaproponować zmiany tego pliku
editor.fork_before_edit=Musisz sforkować to repozytorium przed edycją tego pliku
editor.delete_this_file=Usuń ten plik
editor.must_have_write_access=Musisz mieć uprawnienia do zapisu, aby zgłosić lub zaproponować zmiany do tego pliku
@ -1264,7 +1256,6 @@ auths.domain=Domena
auths.host=Serwer
auths.port=Port
auths.bind_password=Hasło Bind
auths.bind_password_helper=Uwaga: Te hasło jest przechowywane bez szyfrowania. Zdecydowanie zalecane jest użycie konta z uprawnieniami tylko do odczytu.
auths.user_base=Baza wyszukiwania
auths.user_dn=DN użytkownika
auths.attribute_username=Atrybut nazwy użytkownika
@ -1446,7 +1437,6 @@ notices.type=Typ
notices.type_1=Repozytorium
notices.desc=Opis
notices.op=Operacja
notices.delete_success=Powiadomienia systemowe zostały usunięte.
[action]
create_repo=tworzy repozytorium <a href="%s">%s</a>

View File

@ -489,6 +489,8 @@ mirror_last_synced=Última sincronização
watchers=Observadores
stargazers=Usuários que estrelaram
forks=Forks
pick_reaction=Escolha sua reação
reactions_more=e %d mais
form.reach_limit_of_creation=Você já atingiu o seu limite de %d repositórios.
form.name_reserved=O nome de repositório '%s' é reservado e não pode ser usado.
@ -539,6 +541,7 @@ pulls=Pull Requests
labels=Etiquetas
milestones=Marcos
commits=Commits
commit=Commit
releases=Versões
file_raw=Original
file_history=Histórico
@ -752,7 +755,6 @@ pulls.is_checking=A verificação do conflito ainda está em progresso, por favo
pulls.can_auto_merge_desc=O merge deste pull request pode ser aplicado automaticamente.
pulls.cannot_auto_merge_desc=O merge deste pull request não pode ser aplicado automaticamente pois há conflitos.
pulls.cannot_auto_merge_helper=Por favor, aplique o merge manualmente para resolver os conflitos.
pulls.merge_pull_request=Solicitação de merge de Pull Request
pulls.open_unmerged_pull_exists=`Você não pode executar a operação de reabrir porque já existe um pull request aberto (#%d) do mesmo repositório com as mesmas informações de merge e está esperando pelo merge.`
milestones.new=Novo marco
@ -804,6 +806,7 @@ wiki.new_page_button=Nova página
wiki.delete_page_button=Excluir página
wiki.delete_page_notice_1=Isso vai deletar a página <code>"%s"</code>. Por favor, verifique se você quer mesmo deletar esta página.
wiki.page_already_exists=já existe uma página de wiki com o mesmo nome.
wiki.reserved_page=O nome %s para página wiki está reservado, por favor, selecione um nome diferente.
wiki.pages=Páginas
wiki.last_updated=Última atualização %s
@ -1543,6 +1546,7 @@ no_read=Você não possui nenhuma notificação lida.
pin=Fixar notificação
mark_as_read=Marcar como lida
mark_as_unread=Marcar como não lida
mark_all_as_read=Marcar todas como lidas
[gpg]
error.extract_sign=Falha ao extrair assinatura

View File

@ -489,6 +489,8 @@ mirror_last_synced=Последняя синхронизация
watchers=Наблюдатели
stargazers=Звездочеты
forks=Форки
pick_reaction=Оставьте свою оценку!
reactions_more=и ещё %d
form.reach_limit_of_creation=Вы уже достигли ваш предел %d репозиториев.
form.name_reserved=Имя репозитория '%s' зарезервировано.
@ -529,16 +531,17 @@ bare_message=В репозитории нет файлов.
code=Код
code.desc=Хранилище кода с историей изменений
branch=Ветка
branch=ветка
tree=Дерево
filter_branch_and_tag=Фильтр по ветке или тегу
branches=Ветки
branches=веток
tags=Теги
issues=Задачи
pulls=Pull Request'ы
labels=Метки
milestones=Этапы
commits=Коммиты
commits=коммитов
commit=коммит
releases=Релизы
file_raw=Исходник
file_history=История
@ -1544,6 +1547,7 @@ no_read=У вас нет прочитанных уведомлений.
pin=Прикрепить уведомление
mark_as_read=Отметить как прочитанное
mark_as_unread=Пометить как непрочитанное
mark_all_as_read=Пометить все как прочитанные
[gpg]
error.extract_sign=Не удалось извлечь подпись

View File

@ -405,6 +405,7 @@ key_state_desc=7 天内使用过该密钥
token_state_desc=7 天内使用过该密钥
show_openid=在个人信息上显示
hide_openid=在个人信息上隐藏
ssh_disabled=SSH 被禁用
manage_social=管理关联社交帐户
social_desc=这是相关联的社会帐户的列表。出于安全考虑,请确保你认识的所有这些条目,因为它们可以用于登录到您的帐户。
@ -488,6 +489,8 @@ mirror_last_synced=上次同步时间:
watchers=关注者
stargazers=称赞者
forks=派生仓库
pick_reaction=选择你的表情
reactions_more=再加载 %d
form.reach_limit_of_creation=你已经达到了您的 %d 仓库的限制。
form.name_reserved=仓库名称 '%s' 是被保留的。
@ -538,6 +541,7 @@ pulls=合并请求
labels=标签
milestones=里程碑
commits=提交
commit=提交
releases=版本发布
file_raw=原始文件
file_history=文件历史
@ -803,6 +807,7 @@ wiki.new_page_button=新的页面
wiki.delete_page_button=删除页面
wiki.delete_page_notice_1=此操作将删除页面 <code>"%s"</code>。请确保您想要删除此页。
wiki.page_already_exists=相同名称的 Wiki 页面已经存在。
wiki.reserved_page=wiki 页面名称 %s 是保留的, 请选择其他名称。
wiki.pages=所有页面
wiki.last_updated=最后更新于 %s
@ -813,8 +818,8 @@ activity.period.halfweekly=3 天
activity.period.weekly=1周
activity.period.monthly=1 个月
activity.overview=概览
activity.active_prs_count_1=<strong>%d</strong>活动请求
activity.active_prs_count_n=<strong>%d</strong>活动请求
activity.active_prs_count_1=<strong>%d</strong> 合并请求
activity.active_prs_count_n=<strong>%d</strong> 合并请求
activity.merged_prs_count_1=合并请求
activity.merged_prs_count_n=合并请求
activity.opened_prs_count_1=新合并请求
@ -827,8 +832,8 @@ activity.title.prs_merged_by=%[2]s 由 %[1]s 合并
activity.title.prs_opened_by=%[2]s 创建了 %[1]s
activity.merged_prs_label=已合并
activity.opened_prs_label=已创建
activity.active_issues_count_1=<strong>%d</strong>活动工单
activity.active_issues_count_n=<strong>%d</strong>活动工单
activity.active_issues_count_1=<strong>%d</strong> 工单
activity.active_issues_count_n=<strong>%d</strong> 工单
activity.closed_issues_count_1=已关闭的工单
activity.closed_issues_count_n=已关闭的工单
activity.title.issues_1=%d 工单
@ -977,6 +982,7 @@ settings.slack_token=令牌
settings.slack_domain=域名
settings.slack_channel=频道
settings.add_discord_hook_desc=为您的仓库增加 <a href="%s">Discord</a> 集成。
settings.add_dingtalk_hook_desc=为您的仓库增加 <a href="%s">钉钉</a> 集成。
settings.deploy_keys=管理部署密钥
settings.add_deploy_key=添加部署密钥
settings.deploy_key_desc=部署密钥仅具有只读权限,它在功能上和个人用户的公开密钥有本质区别。
@ -1050,7 +1056,7 @@ release.prerelease_helper=我们会告知用户不建议将本次发布投入生
release.cancel=取消
release.publish=发布版本
release.save_draft=保存草稿
release.edit_release=编辑发布信息
release.edit_release=保存此次发布
release.delete_release=删除此次发布
release.deletion=删除版本发布操作
release.deletion_desc=删除该版本发布将会移除相应的 Git 标签。是否继续?
@ -1541,6 +1547,7 @@ no_read=您没有任何已读的通知。
pin=Pin 通知
mark_as_read=标记为已读
mark_as_unread=标记为未读
mark_all_as_read=全部标记为已读
[gpg]
error.extract_sign=无法提取签名

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