1
0
mirror of https://github.com/go-gitea/gitea.git synced 2024-11-04 08:17:24 -05:00

Merge master & resolve conflicts

This commit is contained in:
Manush Dodunekov 2020-01-12 18:06:10 +01:00
commit 095bfa6139
71 changed files with 1617 additions and 954 deletions

View File

@ -558,6 +558,6 @@ pr:
golangci-lint:
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
export BINARY="golangci-lint"; \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.20.0; \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.22.2; \
fi
golangci-lint run --timeout 5m

130
cmd/doctor.go Normal file
View File

@ -0,0 +1,130 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"bufio"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
)
// CmdDoctor represents the available doctor sub-command.
var CmdDoctor = cli.Command{
Name: "doctor",
Usage: "Diagnose the problems",
Description: "A command to diagnose the problems of current gitea instance according the given configuration.",
Action: runDoctor,
}
type check struct {
title string
f func(ctx *cli.Context) ([]string, error)
}
// checklist represents list for all checks
var checklist = []check{
{
title: "Check if OpenSSH authorized_keys file id correct",
f: runDoctorLocationMoved,
},
// more checks please append here
}
func runDoctor(ctx *cli.Context) error {
err := initDB()
fmt.Println("Using app.ini at", setting.CustomConf)
if err != nil {
fmt.Println(err)
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
return nil
}
for i, check := range checklist {
fmt.Println("[", i+1, "]", check.title)
if messages, err := check.f(ctx); err != nil {
fmt.Println("Error:", err)
} else if len(messages) > 0 {
for _, message := range messages {
fmt.Println("-", message)
}
} else {
fmt.Println("OK.")
}
fmt.Println()
}
return nil
}
func exePath() (string, error) {
file, err := exec.LookPath(os.Args[0])
if err != nil {
return "", err
}
return filepath.Abs(file)
}
func runDoctorLocationMoved(ctx *cli.Context) ([]string, error) {
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
return nil, nil
}
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
f, err := os.Open(fPath)
if err != nil {
return nil, err
}
defer f.Close()
var firstline string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
firstline = strings.TrimSpace(scanner.Text())
if len(firstline) == 0 || firstline[0] == '#' {
continue
}
break
}
// command="/Volumes/data/Projects/gitea/gitea/gitea --config
if len(firstline) > 0 {
exp := regexp.MustCompile(`^[ \t]*(?:command=")([^ ]+) --config='([^']+)' serv key-([^"]+)",(?:[^ ]+) ssh-rsa ([^ ]+) ([^ ]+)[ \t]*$`)
// command="/home/user/gitea --config='/home/user/etc/app.ini' serv key-999",option-1,option-2,option-n ssh-rsa public-key-value key-name
res := exp.FindStringSubmatch(firstline)
if res == nil {
return nil, errors.New("Unknow authorized_keys format")
}
giteaPath := res[1] // => /home/user/gitea
iniPath := res[2] // => /home/user/etc/app.ini
p, err := exePath()
if err != nil {
return nil, err
}
p, err = filepath.Abs(p)
if err != nil {
return nil, err
}
if len(giteaPath) > 0 && giteaPath != p {
return []string{fmt.Sprintf("Gitea exe path wants %s but %s on %s", p, giteaPath, fPath)}, nil
}
if len(iniPath) > 0 && iniPath != setting.CustomConf {
return []string{fmt.Sprintf("Gitea config path wants %s but %s on %s", setting.CustomConf, iniPath, fPath)}, nil
}
}
return nil, nil
}

View File

@ -8,10 +8,12 @@ import (
"bufio"
"bytes"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
@ -58,6 +60,85 @@ var (
}
)
type delayWriter struct {
internal io.Writer
buf *bytes.Buffer
timer *time.Timer
}
func newDelayWriter(internal io.Writer, delay time.Duration) *delayWriter {
timer := time.NewTimer(delay)
return &delayWriter{
internal: internal,
buf: &bytes.Buffer{},
timer: timer,
}
}
func (d *delayWriter) Write(p []byte) (n int, err error) {
if d.buf != nil {
select {
case <-d.timer.C:
_, err := d.internal.Write(d.buf.Bytes())
if err != nil {
return 0, err
}
d.buf = nil
return d.internal.Write(p)
default:
return d.buf.Write(p)
}
}
return d.internal.Write(p)
}
func (d *delayWriter) WriteString(s string) (n int, err error) {
if d.buf != nil {
select {
case <-d.timer.C:
_, err := d.internal.Write(d.buf.Bytes())
if err != nil {
return 0, err
}
d.buf = nil
return d.internal.Write([]byte(s))
default:
return d.buf.WriteString(s)
}
}
return d.internal.Write([]byte(s))
}
func (d *delayWriter) Close() error {
if d == nil {
return nil
}
stopped := d.timer.Stop()
if stopped {
return nil
}
select {
case <-d.timer.C:
default:
}
if d.buf == nil {
return nil
}
_, err := d.internal.Write(d.buf.Bytes())
d.buf = nil
return err
}
type nilWriter struct{}
func (n *nilWriter) Write(p []byte) (int, error) {
return len(p), nil
}
func (n *nilWriter) WriteString(s string) (int, error) {
return len(s), nil
}
func runHookPreReceive(c *cli.Context) error {
if os.Getenv(models.EnvIsInternal) == "true" {
return nil
@ -101,6 +182,18 @@ Gitea or set your environment appropriately.`, "")
total := 0
lastline := 0
var out io.Writer
out = &nilWriter{}
if setting.Git.VerbosePush {
if setting.Git.VerbosePushDelay > 0 {
dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
defer dWriter.Close()
out = dWriter
} else {
out = os.Stdout
}
}
for scanner.Scan() {
// TODO: support news feeds for wiki
if isWiki {
@ -124,12 +217,10 @@ Gitea or set your environment appropriately.`, "")
newCommitIDs[count] = newCommitID
refFullNames[count] = refFullName
count++
fmt.Fprintf(os.Stdout, "*")
os.Stdout.Sync()
fmt.Fprintf(out, "*")
if count >= hookBatchSize {
fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
os.Stdout.Sync()
fmt.Fprintf(out, " Checking %d branches\n", count)
hookOptions.OldCommitIDs = oldCommitIDs
hookOptions.NewCommitIDs = newCommitIDs
@ -147,12 +238,10 @@ Gitea or set your environment appropriately.`, "")
lastline = 0
}
} else {
fmt.Fprintf(os.Stdout, ".")
os.Stdout.Sync()
fmt.Fprintf(out, ".")
}
if lastline >= hookBatchSize {
fmt.Fprintf(os.Stdout, "\n")
os.Stdout.Sync()
fmt.Fprintf(out, "\n")
lastline = 0
}
}
@ -162,8 +251,7 @@ Gitea or set your environment appropriately.`, "")
hookOptions.NewCommitIDs = newCommitIDs[:count]
hookOptions.RefFullNames = refFullNames[:count]
fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
os.Stdout.Sync()
fmt.Fprintf(out, " Checking %d branches\n", count)
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
switch statusCode {
@ -173,14 +261,11 @@ Gitea or set your environment appropriately.`, "")
fail(msg, "")
}
} else if lastline > 0 {
fmt.Fprintf(os.Stdout, "\n")
os.Stdout.Sync()
fmt.Fprintf(out, "\n")
lastline = 0
}
fmt.Fprintf(os.Stdout, "Checked %d references in total\n", total)
os.Stdout.Sync()
fmt.Fprintf(out, "Checked %d references in total\n", total)
return nil
}
@ -206,6 +291,19 @@ Gitea or set your environment appropriately.`, "")
}
}
var out io.Writer
var dWriter *delayWriter
out = &nilWriter{}
if setting.Git.VerbosePush {
if setting.Git.VerbosePushDelay > 0 {
dWriter = newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
defer dWriter.Close()
out = dWriter
} else {
out = os.Stdout
}
}
// the environment setted on serv command
repoUser := os.Getenv(models.EnvRepoUsername)
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
@ -241,7 +339,7 @@ Gitea or set your environment appropriately.`, "")
continue
}
fmt.Fprintf(os.Stdout, ".")
fmt.Fprintf(out, ".")
oldCommitIDs[count] = string(fields[0])
newCommitIDs[count] = string(fields[1])
refFullNames[count] = string(fields[2])
@ -250,16 +348,15 @@ Gitea or set your environment appropriately.`, "")
}
count++
total++
os.Stdout.Sync()
if count >= hookBatchSize {
fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
os.Stdout.Sync()
fmt.Fprintf(out, " Processing %d references\n", count)
hookOptions.OldCommitIDs = oldCommitIDs
hookOptions.NewCommitIDs = newCommitIDs
hookOptions.RefFullNames = refFullNames
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
if resp == nil {
_ = dWriter.Close()
hookPrintResults(results)
fail("Internal Server Error", err)
}
@ -277,9 +374,9 @@ Gitea or set your environment appropriately.`, "")
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
}
}
fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
os.Stdout.Sync()
fmt.Fprintf(out, "Processed %d references in total\n", total)
_ = dWriter.Close()
hookPrintResults(results)
return nil
}
@ -288,19 +385,18 @@ Gitea or set your environment appropriately.`, "")
hookOptions.NewCommitIDs = newCommitIDs[:count]
hookOptions.RefFullNames = refFullNames[:count]
fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
os.Stdout.Sync()
fmt.Fprintf(out, " Processing %d references\n", count)
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
if resp == nil {
_ = dWriter.Close()
hookPrintResults(results)
fail("Internal Server Error", err)
}
wasEmpty = wasEmpty || resp.RepoWasEmpty
results = append(results, resp.Results...)
fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
os.Stdout.Sync()
fmt.Fprintf(out, "Processed %d references in total\n", total)
if wasEmpty && masterPushed {
// We need to tell the repo to reset the default branch to master
@ -309,7 +405,7 @@ Gitea or set your environment appropriately.`, "")
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
}
}
_ = dWriter.Close()
hookPrintResults(results)
return nil

View File

@ -522,6 +522,8 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
- `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view.
- `GC_ARGS`: **\<empty\>**: Arguments for command `git gc`, e.g. `--aggressive --auto`. See more on http://git-scm.com/docs/git-gc/
- `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1
- `VERBOSE_PUSH`: **true**: Print status information about pushes as they are being processed.
- `VERBOSE_PUSH_DELAY`: **5s**: Only print verbose information if push takes longer than this delay.
## Git - Timeout settings (`git.timeout`)
- `DEFAUlT`: **360**: Git operations default timeout seconds.

View File

@ -289,3 +289,28 @@ This command is idempotent.
#### convert
Converts an existing MySQL database from utf8 to utf8mb4.
#### doctor
Diagnose the problems of current gitea instance according the given configuration.
Currently there are a check list below:
- Check if OpenSSH authorized_keys file id correct
When your gitea instance support OpenSSH, your gitea instance binary path will be written to `authorized_keys`
when there is any public key added or changed on your gitea instance.
Sometimes if you moved or renamed your gitea binary when upgrade and you haven't run `Update the '.ssh/authorized_keys' file with Gitea SSH keys. (Not needed for the built-in SSH server.)` on your Admin Panel. Then all pull/push via SSH will not be work.
This check will help you to check if it works well.
For contributors, if you want to add more checks, you can wrie ad new function like `func(ctx *cli.Context) ([]string, error)` and
append it to `doctor.go`.
```go
var checklist = []check{
{
title: "Check if OpenSSH authorized_keys file id correct",
f: runDoctorLocationMoved,
},
// more checks please append here
}
```
This function will receive a command line context and return a list of details about the problems or error.

View File

@ -7,6 +7,7 @@ package integrations
import (
"fmt"
"net/http"
"net/url"
"testing"
"code.gitea.io/gitea/models"
@ -120,3 +121,47 @@ func TestAPIEditIssue(t *testing.T) {
assert.Equal(t, body, issueAfter.Content)
assert.Equal(t, title, issueAfter.Title)
}
func TestAPISearchIssue(t *testing.T) {
defer prepareTestEnv(t)()
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session)
link, _ := url.Parse("/api/v1/repos/issues/search")
req := NewRequest(t, "GET", link.String())
resp := session.MakeRequest(t, req, http.StatusOK)
var apiIssues []*api.Issue
DecodeJSON(t, resp, &apiIssues)
assert.Len(t, apiIssues, 8)
query := url.Values{}
query.Add("token", token)
link.RawQuery = query.Encode()
req = NewRequest(t, "GET", link.String())
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
assert.Len(t, apiIssues, 8)
query.Add("state", "closed")
link.RawQuery = query.Encode()
req = NewRequest(t, "GET", link.String())
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
assert.Len(t, apiIssues, 2)
query.Set("state", "all")
link.RawQuery = query.Encode()
req = NewRequest(t, "GET", link.String())
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
assert.Len(t, apiIssues, 10) //there are more but 10 is page item limit
query.Add("page", "2")
link.RawQuery = query.Encode()
req = NewRequest(t, "GET", link.String())
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
assert.Len(t, apiIssues, 0)
}

View File

@ -136,3 +136,17 @@ func TestAPIOrgDeny(t *testing.T) {
MakeRequest(t, req, http.StatusNotFound)
})
}
func TestAPIGetAll(t *testing.T) {
defer prepareTestEnv(t)()
req := NewRequestf(t, "GET", "/api/v1/orgs")
resp := MakeRequest(t, req, http.StatusOK)
var apiOrgList []*api.Organization
DecodeJSON(t, resp, &apiOrgList)
assert.Len(t, apiOrgList, 7)
assert.Equal(t, "org25", apiOrgList[0].FullName)
assert.Equal(t, "public", apiOrgList[0].Visibility)
}

View File

@ -68,6 +68,7 @@ arguments - which can alternatively be run by running the subcommand web.`
cmd.CmdMigrate,
cmd.CmdKeys,
cmd.CmdConvert,
cmd.CmdDoctor,
}
// Now adjust these commands to add our global configuration options

View File

@ -145,7 +145,7 @@ func (a *Action) GetActAvatar() string {
// GetRepoUserName returns the name of the action repository owner.
func (a *Action) GetRepoUserName() string {
a.loadRepo()
return a.Repo.MustOwner().Name
return a.Repo.OwnerName
}
// ShortRepoUserName returns the name of the action repository owner

View File

@ -1,6 +1,7 @@
-
id: 1
owner_id: 2
owner_name: user2
lower_name: repo1
name: repo1
is_private: false
@ -16,6 +17,7 @@
-
id: 2
owner_id: 2
owner_name: user2
lower_name: repo2
name: repo2
is_private: true
@ -30,6 +32,7 @@
-
id: 3
owner_id: 3
owner_name: user3
lower_name: repo3
name: repo3
is_private: true
@ -43,6 +46,7 @@
-
id: 4
owner_id: 5
owner_name: user5
lower_name: repo4
name: repo4
is_private: false
@ -56,6 +60,7 @@
-
id: 5
owner_id: 3
owner_name: user3
lower_name: repo5
name: repo5
is_private: true
@ -70,6 +75,7 @@
-
id: 6
owner_id: 10
owner_name: user10
lower_name: repo6
name: repo6
is_private: true
@ -83,6 +89,7 @@
-
id: 7
owner_id: 10
owner_name: user10
lower_name: repo7
name: repo7
is_private: true
@ -96,6 +103,7 @@
-
id: 8
owner_id: 10
owner_name: user10
lower_name: repo8
name: repo8
is_private: false
@ -109,6 +117,7 @@
-
id: 9
owner_id: 11
owner_name: user11
lower_name: repo9
name: repo9
is_private: false
@ -122,6 +131,7 @@
-
id: 10
owner_id: 12
owner_name: user12
lower_name: repo10
name: repo10
is_private: false
@ -137,6 +147,7 @@
id: 11
fork_id: 10
owner_id: 13
owner_name: user13
lower_name: repo11
name: repo11
is_private: false
@ -150,6 +161,7 @@
-
id: 12
owner_id: 14
owner_name: user14
lower_name: test_repo_12
name: test_repo_12
is_private: false
@ -163,6 +175,7 @@
-
id: 13
owner_id: 14
owner_name: user14
lower_name: test_repo_13
name: test_repo_13
is_private: true
@ -176,6 +189,7 @@
-
id: 14
owner_id: 14
owner_name: user14
lower_name: test_repo_14
name: test_repo_14
description: test_description_14
@ -190,6 +204,7 @@
-
id: 15
owner_id: 2
owner_name: user2
lower_name: repo15
name: repo15
is_empty: true
@ -198,6 +213,7 @@
-
id: 16
owner_id: 2
owner_name: user2
lower_name: repo16
name: repo16
is_private: true
@ -211,6 +227,7 @@
-
id: 17
owner_id: 15
owner_name: user15
lower_name: big_test_public_1
name: big_test_public_1
is_private: false
@ -226,6 +243,7 @@
-
id: 18
owner_id: 15
owner_name: user15
lower_name: big_test_public_2
name: big_test_public_2
is_private: false
@ -240,6 +258,7 @@
-
id: 19
owner_id: 15
owner_name: user15
lower_name: big_test_private_1
name: big_test_private_1
is_private: true
@ -254,6 +273,7 @@
-
id: 20
owner_id: 15
owner_name: user15
lower_name: big_test_private_2
name: big_test_private_2
is_private: true
@ -268,6 +288,7 @@
-
id: 21
owner_id: 16
owner_name: user16
lower_name: big_test_public_3
name: big_test_public_3
is_private: false
@ -282,6 +303,7 @@
-
id: 22
owner_id: 16
owner_name: user16
lower_name: big_test_private_3
name: big_test_private_3
is_private: true
@ -296,6 +318,7 @@
-
id: 23
owner_id: 17
owner_name: user17
lower_name: big_test_public_4
name: big_test_public_4
is_private: false
@ -310,6 +333,7 @@
-
id: 24
owner_id: 17
owner_name: user17
lower_name: big_test_private_4
name: big_test_private_4
is_private: true
@ -324,6 +348,7 @@
-
id: 25
owner_id: 20
owner_name: user20
lower_name: big_test_public_mirror_5
name: big_test_public_mirror_5
is_private: false
@ -339,6 +364,7 @@
-
id: 26
owner_id: 20
owner_name: user20
lower_name: big_test_private_mirror_5
name: big_test_private_mirror_5
is_private: true
@ -354,6 +380,7 @@
-
id: 27
owner_id: 19
owner_name: user19
lower_name: big_test_public_mirror_6
name: big_test_public_mirror_6
is_private: false
@ -370,6 +397,7 @@
-
id: 28
owner_id: 19
owner_name: user19
lower_name: big_test_private_mirror_6
name: big_test_private_mirror_6
is_private: true
@ -387,6 +415,7 @@
id: 29
fork_id: 27
owner_id: 20
owner_name: user20
lower_name: big_test_public_fork_7
name: big_test_public_fork_7
is_private: false
@ -402,6 +431,7 @@
id: 30
fork_id: 28
owner_id: 20
owner_name: user20
lower_name: big_test_private_fork_7
name: big_test_private_fork_7
is_private: true
@ -416,6 +446,7 @@
-
id: 31
owner_id: 2
owner_name: user2
lower_name: repo20
name: repo20
num_stars: 0
@ -427,6 +458,7 @@
-
id: 32 # org public repo
owner_id: 3
owner_name: user3
lower_name: repo21
name: repo21
is_private: false
@ -439,6 +471,7 @@
-
id: 33
owner_id: 2
owner_name: user2
lower_name: utf8
name: utf8
is_private: false
@ -447,6 +480,7 @@
-
id: 34
owner_id: 21
owner_name: user21
lower_name: golang
name: golang
is_private: false
@ -459,6 +493,7 @@
-
id: 35
owner_id: 21
owner_name: user21
lower_name: graphql
name: graphql
is_private: false
@ -471,6 +506,7 @@
-
id: 36
owner_id: 2
owner_name: user2
lower_name: commits_search_test
name: commits_search_test
is_private: false
@ -483,6 +519,7 @@
-
id: 37
owner_id: 2
owner_name: user2
lower_name: git_hooks_test
name: git_hooks_test
is_private: false
@ -495,6 +532,7 @@
-
id: 38
owner_id: 22
owner_name: limited_org
lower_name: public_repo_on_limited_org
name: public_repo_on_limited_org
is_private: false
@ -507,6 +545,7 @@
-
id: 39
owner_id: 22
owner_name: limited_org
lower_name: private_repo_on_limited_org
name: private_repo_on_limited_org
is_private: true
@ -519,6 +558,7 @@
-
id: 40
owner_id: 23
owner_name: limited_org
lower_name: public_repo_on_private_org
name: public_repo_on_private_org
is_private: false
@ -531,6 +571,7 @@
-
id: 41
owner_id: 23
owner_name: limited_org
lower_name: private_repo_on_private_org
name: private_repo_on_private_org
is_private: true
@ -542,6 +583,7 @@
-
id: 42
owner_id: 2
owner_name: user2
lower_name: glob
name: glob
is_private: false
@ -554,6 +596,7 @@
-
id: 43
owner_id: 26
owner_name: org26
lower_name: repo26
name: repo26
is_private: true
@ -566,6 +609,7 @@
-
id: 44
owner_id: 27
owner_name: user27
lower_name: template1
name: template1
is_private: false
@ -579,6 +623,7 @@
-
id: 45
owner_id: 27
owner_name: user27
lower_name: template2
name: template2
is_private: false
@ -592,6 +637,7 @@
-
id: 46
owner_id: 26
owner_name: org26
lower_name: repo_external_tracker
name: repo_external_tracker
is_private: false
@ -604,6 +650,7 @@
-
id: 47
owner_id: 26
owner_name: org26
lower_name: repo_external_tracker_numeric
name: repo_external_tracker_numeric
is_private: false
@ -616,6 +663,7 @@
-
id: 48
owner_id: 26
owner_name: org26
lower_name: repo_external_tracker_alpha
name: repo_external_tracker_alpha
is_private: false

View File

@ -43,7 +43,7 @@ func FullPushingEnvironment(author, committer *User, repo *Repository, repoName
"GIT_COMMITTER_NAME="+committerSig.Name,
"GIT_COMMITTER_EMAIL="+committerSig.Email,
EnvRepoName+"="+repoName,
EnvRepoUsername+"="+repo.MustOwnerName(),
EnvRepoUsername+"="+repo.OwnerName,
EnvRepoIsWiki+"="+isWiki,
EnvPusherName+"="+committer.Name,
EnvPusherID+"="+fmt.Sprintf("%d", committer.ID),

View File

@ -22,9 +22,9 @@ var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
// GetLabelTemplateFile loads the label template file by given name,
// then parses and returns a list of name-color pairs and optionally description.
func GetLabelTemplateFile(name string) ([][3]string, error) {
data, err := getRepoInitFile("label", name)
data, err := GetRepoInitFile("label", name)
if err != nil {
return nil, fmt.Errorf("getRepoInitFile: %v", err)
return nil, fmt.Errorf("GetRepoInitFile: %v", err)
}
lines := strings.Split(string(data), "\n")
@ -175,8 +175,8 @@ func initalizeLabels(e Engine, repoID int64, labelTemplate string) error {
}
// InitalizeLabels adds a label set to a repository using a template
func InitalizeLabels(repoID int64, labelTemplate string) error {
return initalizeLabels(x, repoID, labelTemplate)
func InitalizeLabels(ctx DBContext, repoID int64, labelTemplate string) error {
return initalizeLabels(ctx.e, repoID, labelTemplate)
}
func newLabel(e Engine, label *Label) error {

View File

@ -295,6 +295,8 @@ var migrations = []Migration{
// v119 -> v120
NewMigration("Fix migrated repositories' git service type", fixMigratedRepositoryServiceType),
// v120 -> v121
NewMigration("Add owner_name on table repository", addOwnerNameOnRepository),
// v121 -> v122
NewMigration("add is_restricted column for users table", addIsRestricted),
}

View File

@ -4,14 +4,17 @@
package migrations
import "xorm.io/xorm"
import (
"xorm.io/xorm"
)
func addIsRestricted(x *xorm.Engine) error {
// User see models/user.go
type User struct {
ID int64 `xorm:"pk autoincr"`
IsRestricted bool `xorm:"NOT NULL DEFAULT false"`
func addOwnerNameOnRepository(x *xorm.Engine) error {
type Repository struct {
OwnerName string
}
return x.Sync2(new(User))
if err := x.Sync2(new(Repository)); err != nil {
return err
}
_, err := x.Exec("UPDATE repository SET owner_name = (SELECT name FROM `user` WHERE `user`.id = repository.owner_id)")
return err
}

17
models/migrations/v121.go Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import "xorm.io/xorm"
func addIsRestricted(x *xorm.Engine) error {
// User see models/user.go
type User struct {
ID int64 `xorm:"pk autoincr"`
IsRestricted bool `xorm:"NOT NULL DEFAULT false"`
}
return x.Sync2(new(User))
}

View File

@ -5,12 +5,9 @@
package models
import (
"fmt"
"strings"
"testing"
"code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
)
@ -377,133 +374,3 @@ func TestUsersInTeamsCount(t *testing.T) {
test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2) // userid 2,4
test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
}
func TestIncludesAllRepositoriesTeams(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
testTeamRepositories := func(teamID int64, repoIds []int64) {
team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
assert.NoError(t, team.GetRepositories(), "%s: GetRepositories", team.Name)
assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name)
for i, rid := range repoIds {
if rid > 0 {
assert.True(t, team.HasRepository(rid), "%s: HasRepository(%d) %d", rid, i)
}
}
}
// Get an admin user.
user, err := GetUserByID(1)
assert.NoError(t, err, "GetUserByID")
// Create org.
org := &User{
Name: "All repo",
IsActive: true,
Type: UserTypeOrganization,
Visibility: structs.VisibleTypePublic,
}
assert.NoError(t, CreateOrganization(org, user), "CreateOrganization")
// Check Owner team.
ownerTeam, err := org.GetOwnerTeam()
assert.NoError(t, err, "GetOwnerTeam")
assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories")
// Create repos.
repoIds := make([]int64, 0)
for i := 0; i < 3; i++ {
r, err := CreateRepository(user, org, CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
assert.NoError(t, err, "CreateRepository %d", i)
if r != nil {
repoIds = append(repoIds, r.ID)
}
}
// Get fresh copy of Owner team after creating repos.
ownerTeam, err = org.GetOwnerTeam()
assert.NoError(t, err, "GetOwnerTeam")
// Create teams and check repositories.
teams := []*Team{
ownerTeam,
{
OrgID: org.ID,
Name: "team one",
Authorize: AccessModeRead,
IncludesAllRepositories: true,
},
{
OrgID: org.ID,
Name: "team 2",
Authorize: AccessModeRead,
IncludesAllRepositories: false,
},
{
OrgID: org.ID,
Name: "team three",
Authorize: AccessModeWrite,
IncludesAllRepositories: true,
},
{
OrgID: org.ID,
Name: "team 4",
Authorize: AccessModeWrite,
IncludesAllRepositories: false,
},
}
teamRepos := [][]int64{
repoIds,
repoIds,
{},
repoIds,
{},
}
for i, team := range teams {
if i > 0 { // first team is Owner.
assert.NoError(t, NewTeam(team), "%s: NewTeam", team.Name)
}
testTeamRepositories(team.ID, teamRepos[i])
}
// Update teams and check repositories.
teams[3].IncludesAllRepositories = false
teams[4].IncludesAllRepositories = true
teamRepos[4] = repoIds
for i, team := range teams {
assert.NoError(t, UpdateTeam(team, false, true), "%s: UpdateTeam", team.Name)
testTeamRepositories(team.ID, teamRepos[i])
}
// Create repo and check teams repositories.
org.Teams = nil // Reset teams to allow their reloading.
r, err := CreateRepository(user, org, CreateRepoOptions{Name: "repo-last"})
assert.NoError(t, err, "CreateRepository last")
if r != nil {
repoIds = append(repoIds, r.ID)
}
teamRepos[0] = repoIds
teamRepos[1] = repoIds
teamRepos[4] = repoIds
for i, team := range teams {
testTeamRepositories(team.ID, teamRepos[i])
}
// Remove repo and check teams repositories.
assert.NoError(t, DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository")
teamRepos[0] = repoIds[1:]
teamRepos[1] = repoIds[1:]
teamRepos[3] = repoIds[1:3]
teamRepos[4] = repoIds[1:]
for i, team := range teams {
testTeamRepositories(team.ID, teamRepos[i])
}
// Wipe created items.
for i, rid := range repoIds {
if i > 0 { // first repo already deleted.
assert.NoError(t, DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i)
}
}
assert.NoError(t, DeleteOrganization(org), "DeleteOrganization")
}

View File

@ -70,7 +70,7 @@ func (pr *PullRequest) MustHeadUserName() string {
log.Error("LoadHeadRepo: %v", err)
return ""
}
return pr.HeadRepo.MustOwnerName()
return pr.HeadRepo.OwnerName
}
// Note: don't try to get Issue because will end up recursive querying.

View File

@ -6,7 +6,6 @@
package models
import (
"bytes"
"context"
"crypto/md5"
"errors"
@ -38,7 +37,6 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"github.com/mcuadros/go-version"
"github.com/unknwon/com"
"xorm.io/builder"
)
@ -149,9 +147,9 @@ const (
// Repository represents a git repository.
type Repository struct {
ID int64 `xorm:"pk autoincr"`
OwnerID int64 `xorm:"UNIQUE(s) index"`
OwnerName string `xorm:"-"`
ID int64 `xorm:"pk autoincr"`
OwnerID int64 `xorm:"UNIQUE(s) index"`
OwnerName string
Owner *User `xorm:"-"`
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"INDEX NOT NULL"`
@ -252,17 +250,9 @@ func (repo *Repository) MustOwner() *User {
return repo.mustOwner(x)
}
// MustOwnerName always returns valid owner name to avoid
// conceptually impossible error handling.
// It returns "error" and logs error details when error
// occurs.
func (repo *Repository) MustOwnerName() string {
return repo.mustOwnerName(x)
}
// FullName returns the repository full name
func (repo *Repository) FullName() string {
return repo.MustOwnerName() + "/" + repo.Name
return repo.OwnerName + "/" + repo.Name
}
// HTMLURL returns the repository HTML URL
@ -294,7 +284,7 @@ func (repo *Repository) GetCommitsCountCacheKey(contextName string, isRef bool)
func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) *api.Repository {
var parent *api.Repository
cloneLink := repo.cloneLink(e, false)
cloneLink := repo.cloneLink(false)
permission := &api.Permission{
Admin: mode >= AccessModeAdmin,
Push: mode >= AccessModeWrite,
@ -356,6 +346,8 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
allowSquash = config.AllowSquash
}
repo.mustOwner(e)
return &api.Repository{
ID: repo.ID,
Owner: repo.Owner.APIFormat(),
@ -533,46 +525,11 @@ func (repo *Repository) mustOwner(e Engine) *User {
return repo.Owner
}
func (repo *Repository) getOwnerName(e Engine) error {
if len(repo.OwnerName) > 0 {
return nil
}
if repo.Owner != nil {
repo.OwnerName = repo.Owner.Name
return nil
}
u := new(User)
has, err := e.ID(repo.OwnerID).Cols("name").Get(u)
if err != nil {
return err
} else if !has {
return ErrUserNotExist{repo.OwnerID, "", 0}
}
repo.OwnerName = u.Name
return nil
}
// GetOwnerName returns the repository owner name
func (repo *Repository) GetOwnerName() error {
return repo.getOwnerName(x)
}
func (repo *Repository) mustOwnerName(e Engine) string {
if err := repo.getOwnerName(e); err != nil {
log.Error("Error loading repository owner name: %v", err)
return "error"
}
return repo.OwnerName
}
// ComposeMetas composes a map of metas for properly rendering issue links and external issue trackers.
func (repo *Repository) ComposeMetas() map[string]string {
if repo.RenderingMetas == nil {
metas := map[string]string{
"user": repo.MustOwner().Name,
"user": repo.OwnerName,
"repo": repo.Name,
"repoPath": repo.RepoPath(),
}
@ -588,6 +545,7 @@ func (repo *Repository) ComposeMetas() map[string]string {
}
}
repo.MustOwner()
if repo.Owner.IsOrganization() {
teams := make([]string, 0, 5)
_ = x.Table("team_repo").
@ -597,7 +555,7 @@ func (repo *Repository) ComposeMetas() map[string]string {
OrderBy("team.lower_name").
Find(&teams)
metas["teams"] = "," + strings.Join(teams, ",") + ","
metas["org"] = repo.Owner.LowerName
metas["org"] = strings.ToLower(repo.OwnerName)
}
repo.RenderingMetas = metas
@ -711,13 +669,9 @@ func (repo *Repository) getTemplateRepo(e Engine) (err error) {
return err
}
func (repo *Repository) repoPath(e Engine) string {
return RepoPath(repo.mustOwnerName(e), repo.Name)
}
// RepoPath returns the repository path
func (repo *Repository) RepoPath() string {
return repo.repoPath(x)
return RepoPath(repo.OwnerName, repo.Name)
}
// GitConfigPath returns the path to a repository's git config/ directory
@ -742,7 +696,7 @@ func (repo *Repository) Link() string {
// ComposeCompareURL returns the repository comparison URL
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID)
return fmt.Sprintf("%s/compare/%s...%s", repo.FullName(), oldCommitID, newCommitID)
}
// UpdateDefaultBranch updates the default branch
@ -757,9 +711,9 @@ func (repo *Repository) IsOwnedBy(userID int64) bool {
}
func (repo *Repository) updateSize(e Engine) error {
size, err := util.GetDirectorySize(repo.repoPath(e))
size, err := util.GetDirectorySize(repo.RepoPath())
if err != nil {
return fmt.Errorf("UpdateSize: %v", err)
return fmt.Errorf("updateSize: %v", err)
}
repo.Size = size
@ -768,8 +722,8 @@ func (repo *Repository) updateSize(e Engine) error {
}
// UpdateSize updates the repository size, calculating it using util.GetDirectorySize
func (repo *Repository) UpdateSize() error {
return repo.updateSize(x)
func (repo *Repository) UpdateSize(ctx DBContext) error {
return repo.updateSize(ctx.e)
}
// CanUserFork returns true if specified user can fork repository.
@ -912,7 +866,7 @@ func ComposeHTTPSCloneURL(owner, repo string) string {
return fmt.Sprintf("%s%s/%s.git", setting.AppURL, url.PathEscape(owner), url.PathEscape(repo))
}
func (repo *Repository) cloneLink(e Engine, isWiki bool) *CloneLink {
func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
repoName := repo.Name
if isWiki {
repoName += ".wiki"
@ -923,22 +877,21 @@ func (repo *Repository) cloneLink(e Engine, isWiki bool) *CloneLink {
sshUser = setting.SSH.BuiltinServerUser
}
repo.Owner = repo.mustOwner(e)
cl := new(CloneLink)
if setting.SSH.Port != 22 {
cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", sshUser, setting.SSH.Domain, setting.SSH.Port, repo.Owner.Name, repoName)
cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", sshUser, setting.SSH.Domain, setting.SSH.Port, repo.OwnerName, repoName)
} else if setting.Repository.UseCompatSSHURI {
cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, setting.SSH.Domain, repo.Owner.Name, repoName)
cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, setting.SSH.Domain, repo.OwnerName, repoName)
} else {
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", sshUser, setting.SSH.Domain, repo.Owner.Name, repoName)
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", sshUser, setting.SSH.Domain, repo.OwnerName, repoName)
}
cl.HTTPS = ComposeHTTPSCloneURL(repo.Owner.Name, repoName)
cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
return cl
}
// CloneLink returns clone URLs of repository.
func (repo *Repository) CloneLink() (cl *CloneLink) {
return repo.cloneLink(x, false)
return repo.cloneLink(false)
}
// CheckCreateRepository check if could created a repository
@ -1011,64 +964,6 @@ func createDelegateHooks(repoPath string) (err error) {
return nil
}
// initRepoCommit temporarily changes with work directory.
func initRepoCommit(tmpPath string, repo *Repository, u *User) (err error) {
commitTimeStr := time.Now().Format(time.RFC3339)
sig := u.NewGitSig()
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
"GIT_AUTHOR_NAME="+sig.Name,
"GIT_AUTHOR_EMAIL="+sig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr,
"GIT_COMMITTER_NAME="+sig.Name,
"GIT_COMMITTER_EMAIL="+sig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
if stdout, err := git.NewCommand("add", "--all").
SetDescription(fmt.Sprintf("initRepoCommit (git add): %s", tmpPath)).
RunInDir(tmpPath); err != nil {
log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err)
return fmt.Errorf("git add --all: %v", err)
}
binVersion, err := git.BinVersion()
if err != nil {
return fmt.Errorf("Unable to get git version: %v", err)
}
args := []string{
"commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", "Initial commit",
}
if version.Compare(binVersion, "1.7.9", ">=") {
sign, keyID := SignInitialCommit(tmpPath, u)
if sign {
args = append(args, "-S"+keyID)
} else if version.Compare(binVersion, "2.0.0", ">=") {
args = append(args, "--no-gpg-sign")
}
}
if stdout, err := git.NewCommand(args...).
SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)).
RunInDirWithEnv(tmpPath, env); err != nil {
log.Error("Failed to commit: %v: Stdout: %s\nError: %v", args, stdout, err)
return fmt.Errorf("git commit: %v", err)
}
if stdout, err := git.NewCommand("push", "origin", "master").
SetDescription(fmt.Sprintf("initRepoCommit (git push): %s", tmpPath)).
RunInDirWithEnv(tmpPath, InternalPushingEnvironment(u, repo)); err != nil {
log.Error("Failed to push back to master: Stdout: %s\nError: %v", stdout, err)
return fmt.Errorf("git push: %v", err)
}
return nil
}
// CreateRepoOptions contains the create repository options
type CreateRepoOptions struct {
Name string
@ -1085,7 +980,8 @@ type CreateRepoOptions struct {
Status RepositoryStatus
}
func getRepoInitFile(tp, name string) ([]byte, error) {
// GetRepoInitFile returns repository init files
func GetRepoInitFile(tp, name string) ([]byte, error) {
cleanedName := strings.TrimLeft(path.Clean("/"+name), "/")
relPath := path.Join("options", tp, cleanedName)
@ -1109,140 +1005,6 @@ func getRepoInitFile(tp, name string) ([]byte, error) {
}
}
func prepareRepoCommit(e Engine, repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
commitTimeStr := time.Now().Format(time.RFC3339)
authorSig := repo.Owner.NewGitSig()
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
"GIT_AUTHOR_NAME="+authorSig.Name,
"GIT_AUTHOR_EMAIL="+authorSig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr,
"GIT_COMMITTER_NAME="+authorSig.Name,
"GIT_COMMITTER_EMAIL="+authorSig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
// Clone to temporary path and do the init commit.
if stdout, err := git.NewCommand("clone", repoPath, tmpDir).
SetDescription(fmt.Sprintf("initRepository (git clone): %s to %s", repoPath, tmpDir)).
RunInDirWithEnv("", env); err != nil {
log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git clone: %v", err)
}
// README
data, err := getRepoInitFile("readme", opts.Readme)
if err != nil {
return fmt.Errorf("getRepoInitFile[%s]: %v", opts.Readme, err)
}
cloneLink := repo.cloneLink(e, false)
match := map[string]string{
"Name": repo.Name,
"Description": repo.Description,
"CloneURL.SSH": cloneLink.SSH,
"CloneURL.HTTPS": cloneLink.HTTPS,
}
if err = ioutil.WriteFile(filepath.Join(tmpDir, "README.md"),
[]byte(com.Expand(string(data), match)), 0644); err != nil {
return fmt.Errorf("write README.md: %v", err)
}
// .gitignore
if len(opts.Gitignores) > 0 {
var buf bytes.Buffer
names := strings.Split(opts.Gitignores, ",")
for _, name := range names {
data, err = getRepoInitFile("gitignore", name)
if err != nil {
return fmt.Errorf("getRepoInitFile[%s]: %v", name, err)
}
buf.WriteString("# ---> " + name + "\n")
buf.Write(data)
buf.WriteString("\n")
}
if buf.Len() > 0 {
if err = ioutil.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0644); err != nil {
return fmt.Errorf("write .gitignore: %v", err)
}
}
}
// LICENSE
if len(opts.License) > 0 {
data, err = getRepoInitFile("license", opts.License)
if err != nil {
return fmt.Errorf("getRepoInitFile[%s]: %v", opts.License, err)
}
if err = ioutil.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0644); err != nil {
return fmt.Errorf("write LICENSE: %v", err)
}
}
return nil
}
func checkInitRepository(repoPath string) (err error) {
// Somehow the directory could exist.
if com.IsExist(repoPath) {
return fmt.Errorf("initRepository: path already exists: %s", repoPath)
}
// Init git bare new repository.
if err = git.InitRepository(repoPath, true); err != nil {
return fmt.Errorf("InitRepository: %v", err)
} else if err = createDelegateHooks(repoPath); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
}
return nil
}
// InitRepository initializes README and .gitignore if needed.
func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts CreateRepoOptions) (err error) {
if err = checkInitRepository(repoPath); err != nil {
return err
}
// Initialize repository according to user's choice.
if opts.AutoInit {
tmpDir, err := ioutil.TempDir(os.TempDir(), "gitea-"+repo.Name)
if err != nil {
return fmt.Errorf("Failed to create temp dir for repository %s: %v", repo.repoPath(e), err)
}
defer os.RemoveAll(tmpDir)
if err = prepareRepoCommit(e, repo, tmpDir, repoPath, opts); err != nil {
return fmt.Errorf("prepareRepoCommit: %v", err)
}
// Apply changes and commit.
if err = initRepoCommit(tmpDir, repo, u); err != nil {
return fmt.Errorf("initRepoCommit: %v", err)
}
}
// Re-fetch the repository from database before updating it (else it would
// override changes that were done earlier with sql)
if repo, err = getRepositoryByID(e, repo.ID); err != nil {
return fmt.Errorf("getRepositoryByID: %v", err)
}
if !opts.AutoInit {
repo.IsEmpty = true
}
repo.DefaultBranch = "master"
if err = updateRepository(e, repo, false); err != nil {
return fmt.Errorf("updateRepository: %v", err)
}
return nil
}
var (
reservedRepoNames = []string{".", ".."}
reservedRepoPatterns = []string{"*.git", "*.wiki"}
@ -1253,22 +1015,23 @@ func IsUsableRepoName(name string) error {
return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
}
func createRepository(e Engine, doer, u *User, repo *Repository) (err error) {
// CreateRepository creates a repository for the user/organization.
func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error) {
if err = IsUsableRepoName(repo.Name); err != nil {
return err
}
has, err := isRepositoryExist(e, u, repo.Name)
has, err := isRepositoryExist(ctx.e, u, repo.Name)
if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
return ErrRepoAlreadyExist{u.Name, repo.Name}
}
if _, err = e.Insert(repo); err != nil {
if _, err = ctx.e.Insert(repo); err != nil {
return err
}
if err = deleteRepoRedirect(e, u.ID, repo.Name); err != nil {
if err = deleteRepoRedirect(ctx.e, u.ID, repo.Name); err != nil {
return err
}
@ -1297,20 +1060,19 @@ func createRepository(e Engine, doer, u *User, repo *Repository) (err error) {
Type: tp,
})
}
}
if _, err = e.Insert(&units); err != nil {
if _, err = ctx.e.Insert(&units); err != nil {
return err
}
// Remember visibility preference.
u.LastRepoVisibility = repo.IsPrivate
if err = updateUserCols(e, u, "last_repo_visibility"); err != nil {
if err = updateUserCols(ctx.e, u, "last_repo_visibility"); err != nil {
return fmt.Errorf("updateUser: %v", err)
}
if _, err = e.Incr("num_repos").ID(u.ID).Update(new(User)); err != nil {
if _, err = ctx.e.Incr("num_repos").ID(u.ID).Update(new(User)); err != nil {
return fmt.Errorf("increment user total_repos: %v", err)
}
u.NumRepos++
@ -1322,106 +1084,41 @@ func createRepository(e Engine, doer, u *User, repo *Repository) (err error) {
}
for _, t := range u.Teams {
if t.IncludesAllRepositories {
if err := t.addRepository(e, repo); err != nil {
if err := t.addRepository(ctx.e, repo); err != nil {
return fmt.Errorf("addRepository: %v", err)
}
}
}
if isAdmin, err := isUserRepoAdmin(e, repo, doer); err != nil {
if isAdmin, err := isUserRepoAdmin(ctx.e, repo, doer); err != nil {
return fmt.Errorf("isUserRepoAdmin: %v", err)
} else if !isAdmin {
// Make creator repo admin if it wan't assigned automatically
if err = repo.addCollaborator(e, doer); err != nil {
if err = repo.addCollaborator(ctx.e, doer); err != nil {
return fmt.Errorf("AddCollaborator: %v", err)
}
if err = repo.changeCollaborationAccessMode(e, doer.ID, AccessModeAdmin); err != nil {
if err = repo.changeCollaborationAccessMode(ctx.e, doer.ID, AccessModeAdmin); err != nil {
return fmt.Errorf("ChangeCollaborationAccessMode: %v", err)
}
}
} else if err = repo.recalculateAccesses(e); err != nil {
} else if err = repo.recalculateAccesses(ctx.e); err != nil {
// Organization automatically called this in addRepository method.
return fmt.Errorf("recalculateAccesses: %v", err)
}
if setting.Service.AutoWatchNewRepos {
if err = watchRepo(e, doer.ID, repo.ID, true); err != nil {
if err = watchRepo(ctx.e, doer.ID, repo.ID, true); err != nil {
return fmt.Errorf("watchRepo: %v", err)
}
}
if err = copyDefaultWebhooksToRepo(e, repo.ID); err != nil {
if err = copyDefaultWebhooksToRepo(ctx.e, repo.ID); err != nil {
return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
}
return nil
}
// CreateRepository creates a repository for the user/organization.
func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err error) {
if !doer.IsAdmin && !u.CanCreateRepo() {
return nil, ErrReachLimitOfRepo{u.MaxRepoCreation}
}
repo := &Repository{
OwnerID: u.ID,
Owner: u,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
OriginalURL: opts.OriginalURL,
OriginalServiceType: opts.GitServiceType,
IsPrivate: opts.IsPrivate,
IsFsckEnabled: !opts.IsMirror,
CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
Status: opts.Status,
IsEmpty: !opts.AutoInit,
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return nil, err
}
if err = createRepository(sess, doer, u, repo); err != nil {
return nil, err
}
// No need for init mirror.
if !opts.IsMirror {
repoPath := RepoPath(u.Name, repo.Name)
if err = initRepository(sess, repoPath, u, repo, opts); err != nil {
if err2 := os.RemoveAll(repoPath); err2 != nil {
log.Error("initRepository: %v", err)
return nil, fmt.Errorf(
"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)
}
return nil, fmt.Errorf("initRepository: %v", err)
}
// Initialize Issue Labels if selected
if len(opts.IssueLabels) > 0 {
if err = initalizeLabels(sess, repo.ID, opts.IssueLabels); err != nil {
return nil, fmt.Errorf("initalizeLabels: %v", err)
}
}
if stdout, err := git.NewCommand("update-server-info").
SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)).
RunInDir(repoPath); err != nil {
log.Error("CreateRepitory(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
return nil, fmt.Errorf("CreateRepository(git update-server-info): %v", err)
}
}
if err = sess.Commit(); err != nil {
return nil, err
}
return repo, err
}
func countRepositories(userID int64, private bool) int64 {
sess := x.Where("id > 0")
@ -1458,6 +1155,12 @@ func RepoPath(userName, repoName string) string {
return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".git")
}
// IncrementRepoForkNum increment repository fork number
func IncrementRepoForkNum(ctx DBContext, repoID int64) error {
_, err := ctx.e.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", repoID)
return err
}
// TransferOwnership transfers all corresponding setting from old user to new one.
func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error {
newOwner, err := GetUserByName(newOwnerName)
@ -1485,6 +1188,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
// new owner.
repo.OwnerID = newOwner.ID
repo.Owner = newOwner
repo.OwnerName = newOwner.Name
// Update repository.
if _, err := sess.ID(repo.ID).Update(repo); err != nil {
@ -1683,7 +1387,7 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
}
// Create/Remove git-daemon-export-ok for git-daemon...
daemonExportFile := path.Join(repo.repoPath(e), `git-daemon-export-ok`)
daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
if repo.IsPrivate && com.IsExist(daemonExportFile) {
if err = os.Remove(daemonExportFile); err != nil {
log.Error("Failed to remove %s: %v", daemonExportFile, err)
@ -1715,6 +1419,11 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
return nil
}
// UpdateRepositoryCtx updates a repository with db context
func UpdateRepositoryCtx(ctx DBContext, repo *Repository, visibilityChanged bool) error {
return updateRepository(ctx.e, repo, visibilityChanged)
}
// UpdateRepository updates a repository
func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) {
sess := x.NewSession()
@ -1905,7 +1614,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
}
// FIXME: Remove repository files should be executed after transaction succeed.
repoPath := repo.repoPath(sess)
repoPath := repo.RepoPath()
removeAllWithNotice(sess, "Delete repository files", repoPath)
err = repo.deleteWiki(sess)
@ -2030,6 +1739,11 @@ func GetRepositoryByID(id int64) (*Repository, error) {
return getRepositoryByID(x, id)
}
// GetRepositoryByIDCtx returns the repository by given id if exists.
func GetRepositoryByIDCtx(ctx DBContext, id int64) (*Repository, error) {
return getRepositoryByID(ctx.e, id)
}
// GetRepositoriesMapByIDs returns the repositories by given id slice.
func GetRepositoriesMapByIDs(ids []int64) (map[int64]*Repository, error) {
var repos = make(map[int64]*Repository, len(ids))
@ -2290,7 +2004,7 @@ func GitGcRepos() error {
SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName())).
RunInDirTimeout(
time.Duration(setting.Git.Timeout.GC)*time.Second,
RepoPath(repo.Owner.Name, repo.Name)); err != nil {
repo.RepoPath()); err != nil {
log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
return fmt.Errorf("Repository garbage collection failed: Error: %v", err)
}
@ -2479,20 +2193,16 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
}
// CopyLFS copies LFS data from one repo to another
func CopyLFS(newRepo, oldRepo *Repository) error {
return copyLFS(x, newRepo, oldRepo)
}
func copyLFS(e Engine, newRepo, oldRepo *Repository) error {
func CopyLFS(ctx DBContext, newRepo, oldRepo *Repository) error {
var lfsObjects []*LFSMetaObject
if err := e.Where("repository_id=?", oldRepo.ID).Find(&lfsObjects); err != nil {
if err := ctx.e.Where("repository_id=?", oldRepo.ID).Find(&lfsObjects); err != nil {
return err
}
for _, v := range lfsObjects {
v.ID = 0
v.RepositoryID = newRepo.ID
if _, err := e.Insert(v); err != nil {
if _, err := ctx.e.Insert(v); err != nil {
return err
}
}
@ -2500,80 +2210,6 @@ func copyLFS(e Engine, newRepo, oldRepo *Repository) error {
return nil
}
// ForkRepository forks a repository
func ForkRepository(doer, owner *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
forkedRepo, err := oldRepo.GetUserFork(owner.ID)
if err != nil {
return nil, err
}
if forkedRepo != nil {
return nil, ErrForkAlreadyExist{
Uname: owner.Name,
RepoName: oldRepo.FullName(),
ForkName: forkedRepo.FullName(),
}
}
repo := &Repository{
OwnerID: owner.ID,
Owner: owner,
Name: name,
LowerName: strings.ToLower(name),
Description: desc,
DefaultBranch: oldRepo.DefaultBranch,
IsPrivate: oldRepo.IsPrivate,
IsEmpty: oldRepo.IsEmpty,
IsFork: true,
ForkID: oldRepo.ID,
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return nil, err
}
if err = createRepository(sess, doer, owner, repo); err != nil {
return nil, err
}
if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.ID); err != nil {
return nil, err
}
repoPath := RepoPath(owner.Name, repo.Name)
if stdout, err := git.NewCommand(
"clone", "--bare", oldRepo.repoPath(sess), repoPath).
SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", oldRepo.FullName(), repo.FullName())).
RunInDirTimeout(10*time.Minute, ""); err != nil {
log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, oldRepo, stdout, err)
return nil, fmt.Errorf("git clone: %v", err)
}
if stdout, err := git.NewCommand("update-server-info").
SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())).
RunInDir(repoPath); err != nil {
log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
return nil, fmt.Errorf("git update-server-info: %v", err)
}
if err = createDelegateHooks(repoPath); err != nil {
return nil, fmt.Errorf("createDelegateHooks: %v", err)
}
//Commit repo to get Fork ID
err = sess.Commit()
if err != nil {
return nil, err
}
if err = repo.UpdateSize(); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
return repo, CopyLFS(repo, oldRepo)
}
// GetForks returns all the forks of the repository
func (repo *Repository) GetForks() ([]*Repository, error) {
forks := make([]*Repository, 0, repo.NumForks)

View File

@ -5,14 +5,8 @@
package models
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
@ -71,186 +65,6 @@ func (gt GiteaTemplate) Globs() []glob.Glob {
return gt.globs
}
func checkGiteaTemplate(tmpDir string) (*GiteaTemplate, error) {
gtPath := filepath.Join(tmpDir, ".gitea", "template")
if _, err := os.Stat(gtPath); os.IsNotExist(err) {
return nil, nil
} else if err != nil {
return nil, err
}
content, err := ioutil.ReadFile(gtPath)
if err != nil {
return nil, err
}
gt := &GiteaTemplate{
Path: gtPath,
Content: content,
}
return gt, nil
}
func generateRepoCommit(e Engine, repo, templateRepo, generateRepo *Repository, tmpDir string) error {
commitTimeStr := time.Now().Format(time.RFC3339)
authorSig := repo.Owner.NewGitSig()
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
"GIT_AUTHOR_NAME="+authorSig.Name,
"GIT_AUTHOR_EMAIL="+authorSig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr,
"GIT_COMMITTER_NAME="+authorSig.Name,
"GIT_COMMITTER_EMAIL="+authorSig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
// Clone to temporary path and do the init commit.
templateRepoPath := templateRepo.repoPath(e)
if err := git.Clone(templateRepoPath, tmpDir, git.CloneRepoOptions{
Depth: 1,
}); err != nil {
return fmt.Errorf("git clone: %v", err)
}
if err := os.RemoveAll(path.Join(tmpDir, ".git")); err != nil {
return fmt.Errorf("remove git dir: %v", err)
}
// Variable expansion
gt, err := checkGiteaTemplate(tmpDir)
if err != nil {
return fmt.Errorf("checkGiteaTemplate: %v", err)
}
if gt != nil {
if err := os.Remove(gt.Path); err != nil {
return fmt.Errorf("remove .giteatemplate: %v", err)
}
// Avoid walking tree if there are no globs
if len(gt.Globs()) > 0 {
tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/"
if err := filepath.Walk(tmpDirSlash, func(path string, info os.FileInfo, walkErr error) error {
if walkErr != nil {
return walkErr
}
if info.IsDir() {
return nil
}
base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash)
for _, g := range gt.Globs() {
if g.Match(base) {
content, err := ioutil.ReadFile(path)
if err != nil {
return err
}
if err := ioutil.WriteFile(path,
[]byte(generateExpansion(string(content), templateRepo, generateRepo)),
0644); err != nil {
return err
}
break
}
}
return nil
}); err != nil {
return err
}
}
}
if err := git.InitRepository(tmpDir, false); err != nil {
return err
}
repoPath := repo.repoPath(e)
if stdout, err := git.NewCommand("remote", "add", "origin", repoPath).
SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", templateRepoPath, tmpDir)).
RunInDirWithEnv(tmpDir, env); err != nil {
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git remote add: %v", err)
}
return initRepoCommit(tmpDir, repo, repo.Owner)
}
// generateRepository initializes repository from template
func generateRepository(e Engine, repo, templateRepo, generateRepo *Repository) (err error) {
tmpDir, err := ioutil.TempDir(os.TempDir(), "gitea-"+repo.Name)
if err != nil {
return fmt.Errorf("Failed to create temp dir for repository %s: %v", repo.repoPath(e), err)
}
defer func() {
if err := os.RemoveAll(tmpDir); err != nil {
log.Error("RemoveAll: %v", err)
}
}()
if err = generateRepoCommit(e, repo, templateRepo, generateRepo, tmpDir); err != nil {
return fmt.Errorf("generateRepoCommit: %v", err)
}
// re-fetch repo
if repo, err = getRepositoryByID(e, repo.ID); err != nil {
return fmt.Errorf("getRepositoryByID: %v", err)
}
repo.DefaultBranch = "master"
if err = updateRepository(e, repo, false); err != nil {
return fmt.Errorf("updateRepository: %v", err)
}
return nil
}
// GenerateRepository generates a repository from a template
func GenerateRepository(ctx DBContext, doer, owner *User, templateRepo *Repository, opts GenerateRepoOptions) (_ *Repository, err error) {
generateRepo := &Repository{
OwnerID: owner.ID,
Owner: owner,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
IsPrivate: opts.Private,
IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
IsFsckEnabled: templateRepo.IsFsckEnabled,
TemplateID: templateRepo.ID,
}
if err = createRepository(ctx.e, doer, owner, generateRepo); err != nil {
return nil, err
}
repoPath := RepoPath(owner.Name, generateRepo.Name)
if err = checkInitRepository(repoPath); err != nil {
return generateRepo, err
}
return generateRepo, nil
}
// GenerateGitContent generates git content from a template repository
func GenerateGitContent(ctx DBContext, templateRepo, generateRepo *Repository) error {
if err := generateRepository(ctx.e, generateRepo, templateRepo, generateRepo); err != nil {
return err
}
if err := generateRepo.updateSize(ctx.e); err != nil {
return fmt.Errorf("failed to update size for repository: %v", err)
}
if err := copyLFS(ctx.e, generateRepo, templateRepo); err != nil {
return fmt.Errorf("failed to copy LFS: %v", err)
}
return nil
}
// GenerateTopics generates topics from a template repository
func GenerateTopics(ctx DBContext, templateRepo, generateRepo *Repository) error {
for _, topic := range templateRepo.Topics {
@ -263,13 +77,13 @@ func GenerateTopics(ctx DBContext, templateRepo, generateRepo *Repository) error
// GenerateGitHooks generates git hooks from a template repository
func GenerateGitHooks(ctx DBContext, templateRepo, generateRepo *Repository) error {
generateGitRepo, err := git.OpenRepository(generateRepo.repoPath(ctx.e))
generateGitRepo, err := git.OpenRepository(generateRepo.RepoPath())
if err != nil {
return err
}
defer generateGitRepo.Close()
templateGitRepo, err := git.OpenRepository(templateRepo.repoPath(ctx.e))
templateGitRepo, err := git.OpenRepository(templateRepo.RepoPath())
if err != nil {
return err
}
@ -352,36 +166,3 @@ func GenerateIssueLabels(ctx DBContext, templateRepo, generateRepo *Repository)
}
return nil
}
func generateExpansion(src string, templateRepo, generateRepo *Repository) string {
return os.Expand(src, func(key string) string {
switch key {
case "REPO_NAME":
return generateRepo.Name
case "TEMPLATE_NAME":
return templateRepo.Name
case "REPO_DESCRIPTION":
return generateRepo.Description
case "TEMPLATE_DESCRIPTION":
return templateRepo.Description
case "REPO_OWNER":
return generateRepo.MustOwnerName()
case "TEMPLATE_OWNER":
return templateRepo.MustOwnerName()
case "REPO_LINK":
return generateRepo.Link()
case "TEMPLATE_LINK":
return templateRepo.Link()
case "REPO_HTTPS_URL":
return generateRepo.CloneLink().HTTPS
case "TEMPLATE_HTTPS_URL":
return templateRepo.CloneLink().HTTPS
case "REPO_SSH_URL":
return generateRepo.CloneLink().SSH
case "TEMPLATE_SSH_URL":
return templateRepo.CloneLink().SSH
default:
return key
}
})
}

View File

@ -62,13 +62,13 @@ func (repo *Repository) GetIndexerStatus() error {
// UpdateIndexerStatus updates indexer status
func (repo *Repository) UpdateIndexerStatus(sha string) error {
if err := repo.GetIndexerStatus(); err != nil {
return fmt.Errorf("UpdateIndexerStatus: Unable to getIndexerStatus for repo: %s/%s Error: %v", repo.MustOwnerName(), repo.Name, err)
return fmt.Errorf("UpdateIndexerStatus: Unable to getIndexerStatus for repo: %s Error: %v", repo.FullName(), err)
}
if len(repo.IndexerStatus.CommitSha) == 0 {
repo.IndexerStatus.CommitSha = sha
_, err := x.Insert(repo.IndexerStatus)
if err != nil {
return fmt.Errorf("UpdateIndexerStatus: Unable to insert repoIndexerStatus for repo: %s/%s Sha: %s Error: %v", repo.MustOwnerName(), repo.Name, sha, err)
return fmt.Errorf("UpdateIndexerStatus: Unable to insert repoIndexerStatus for repo: %s Sha: %s Error: %v", repo.FullName(), sha, err)
}
return nil
}
@ -76,7 +76,7 @@ func (repo *Repository) UpdateIndexerStatus(sha string) error {
_, err := x.ID(repo.IndexerStatus.ID).Cols("commit_sha").
Update(repo.IndexerStatus)
if err != nil {
return fmt.Errorf("UpdateIndexerStatus: Unable to update repoIndexerStatus for repo: %s/%s Sha: %s Error: %v", repo.MustOwnerName(), repo.Name, sha, err)
return fmt.Errorf("UpdateIndexerStatus: Unable to update repoIndexerStatus for repo: %s Sha: %s Error: %v", repo.FullName(), sha, err)
}
return nil
}

View File

@ -164,10 +164,6 @@ func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permiss
return
}
if repo.Owner == nil {
repo.mustOwner(e)
}
var isCollaborator bool
if user != nil {
isCollaborator, err = repo.isCollaborator(e, user.ID)
@ -176,6 +172,10 @@ func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permiss
}
}
if err = repo.getOwner(e); err != nil {
return
}
// Prevent strangers from checking out public repo of private orginization
// Allow user if they are collaborator of a repo within a private orginization but not a member of the orginization itself
if repo.Owner.IsOrganization() && !HasOrgVisible(repo.Owner, user) && !isCollaborator {

View File

@ -22,6 +22,7 @@ func TestMetas(t *testing.T) {
repo := &Repository{Name: "testRepo"}
repo.Owner = &User{Name: "testOwner"}
repo.OwnerName = repo.Owner.Name
repo.Units = nil
@ -132,19 +133,6 @@ func TestGetUserFork(t *testing.T) {
assert.Nil(t, repo)
}
func TestForkRepository(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
// user 13 has already forked repo10
user := AssertExistsAndLoadBean(t, &User{ID: 13}).(*User)
repo := AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository)
fork, err := ForkRepository(user, user, repo, "test", "test")
assert.Nil(t, fork)
assert.Error(t, err)
assert.True(t, IsErrForkAlreadyExist(err))
}
func TestRepoAPIURL(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
repo := AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository)

View File

@ -8,8 +8,6 @@ import (
"encoding/json"
"fmt"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migrations/base"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
@ -169,57 +167,16 @@ func FindTasks(opts FindTaskOptions) ([]*Task, error) {
return tasks, err
}
// CreateTask creates a task on database
func CreateTask(task *Task) error {
return createTask(x, task)
}
func createTask(e Engine, task *Task) error {
_, err := e.Insert(task)
return err
}
// CreateMigrateTask creates a migrate task
func CreateMigrateTask(doer, u *User, opts base.MigrateOptions) (*Task, error) {
bs, err := json.Marshal(&opts)
if err != nil {
return nil, err
}
var task = Task{
DoerID: doer.ID,
OwnerID: u.ID,
Type: structs.TaskTypeMigrateRepo,
Status: structs.TaskStatusQueue,
PayloadContent: string(bs),
}
if err := createTask(x, &task); err != nil {
return nil, err
}
repo, err := CreateRepository(doer, u, CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
OriginalURL: opts.OriginalURL,
GitServiceType: opts.GitServiceType,
IsPrivate: opts.Private,
IsMirror: opts.Mirror,
Status: RepositoryBeingMigrated,
})
if err != nil {
task.EndTime = timeutil.TimeStampNow()
task.Status = structs.TaskStatusFailed
err2 := task.UpdateCols("end_time", "status")
if err2 != nil {
log.Error("UpdateCols Failed: %v", err2.Error())
}
return nil, err
}
task.RepoID = repo.ID
if err = task.UpdateCols("repo_id"); err != nil {
return nil, err
}
return &task, nil
}
// FinishMigrateTask updates database when migrate task finished
func FinishMigrateTask(task *Task) error {
task.Status = structs.TaskStatusFinished

View File

@ -504,7 +504,7 @@ func (u *User) ValidatePassword(passwd string) bool {
// IsPasswordSet checks if the password is set or left empty
func (u *User) IsPasswordSet() bool {
return len(u.Passwd) > 0
return !u.ValidatePassword("")
}
// UploadAvatar saves custom avatar for user.
@ -1470,7 +1470,7 @@ type SearchUserOptions struct {
UID int64
OrderBy SearchOrderBy
Page int
Private bool // Include private orgs in search
Visible []structs.VisibleType
Actor *User // The user doing the search
PageSize int // Can be smaller than or equal to setting.UI.ExplorePagingNum
IsActive util.OptionalBool
@ -1493,8 +1493,9 @@ func (opts *SearchUserOptions) toConds() builder.Cond {
cond = cond.And(keywordCond)
}
if !opts.Private {
// user not logged in and so they won't be allowed to see non-public orgs
if len(opts.Visible) > 0 {
cond = cond.And(builder.In("visibility", opts.Visible))
} else {
cond = cond.And(builder.In("visibility", structs.VisibleTypePublic))
}

View File

@ -1,4 +1,5 @@
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@ -13,7 +14,7 @@ import (
// WikiCloneLink returns clone URLs of repository wiki.
func (repo *Repository) WikiCloneLink() *CloneLink {
return repo.cloneLink(x, true)
return repo.cloneLink(true)
}
// WikiPath returns wiki data path by given user and repository name.
@ -23,7 +24,7 @@ func WikiPath(userName, repoName string) string {
// WikiPath returns wiki data path for given repository.
func (repo *Repository) WikiPath() string {
return WikiPath(repo.MustOwnerName(), repo.Name)
return WikiPath(repo.OwnerName, repo.Name)
}
// HasWiki returns true if repository has wiki.

View File

@ -256,7 +256,7 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
redirectPath := strings.Replace(
ctx.Req.URL.Path,
fmt.Sprintf("%s/%s", ownerName, previousRepoName),
fmt.Sprintf("%s/%s", repo.MustOwnerName(), repo.Name),
repo.FullName(),
1,
)
if ctx.Req.URL.RawQuery != "" {

View File

@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migrations/base"
"code.gitea.io/gitea/modules/repository"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
@ -100,7 +101,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
var r *models.Repository
if opts.MigrateToRepoID <= 0 {
r, err = models.CreateRepository(g.doer, owner, models.CreateRepoOptions{
r, err = repo_module.CreateRepository(g.doer, owner, models.CreateRepoOptions{
Name: g.repoName,
Description: repo.Description,
OriginalURL: repo.OriginalURL,

View File

@ -6,6 +6,7 @@ package queue
import (
"context"
"sync"
"time"
"code.gitea.io/gitea/modules/log"
@ -33,6 +34,7 @@ type PersistableChannelQueueConfiguration struct {
type PersistableChannelQueue struct {
*ChannelQueue
delayedStarter
lock sync.Mutex
closed chan struct{}
}

View File

@ -28,7 +28,6 @@ type WrappedQueueConfiguration struct {
}
type delayedStarter struct {
lock sync.Mutex
internal Queue
underlying Type
cfg interface{}
@ -62,7 +61,6 @@ func (q *delayedStarter) setInternal(atShutdown func(context.Context, func()), h
queue, err := NewQueue(q.underlying, handle, q.cfg, exemplar)
if err == nil {
q.internal = queue
q.lock.Unlock()
break
}
if err.Error() != "resource temporarily unavailable" {
@ -90,6 +88,7 @@ func (q *delayedStarter) setInternal(atShutdown func(context.Context, func()), h
// WrappedQueue wraps a delayed starting queue
type WrappedQueue struct {
delayedStarter
lock sync.Mutex
handle HandlerFunc
exemplar interface{}
channel chan Data

View File

@ -458,7 +458,7 @@ func PushUpdate(repo *models.Repository, branch string, opts PushUpdateOptions)
}
defer gitRepo.Close()
if err = repo.UpdateSize(); err != nil {
if err = repo.UpdateSize(models.DefaultDBContext()); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
@ -498,7 +498,7 @@ func PushUpdates(repo *models.Repository, optsList []*PushUpdateOptions) error {
if err != nil {
return fmt.Errorf("OpenRepository: %v", err)
}
if err = repo.UpdateSize(); err != nil {
if err = repo.UpdateSize(models.DefaultDBContext()); err != nil {
log.Error("Failed to update size for repository: %v", err)
}

View File

@ -0,0 +1,77 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repository
import (
"fmt"
"os"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
// CreateRepository creates a repository for the user/organization.
func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *models.Repository, err error) {
if !doer.IsAdmin && !u.CanCreateRepo() {
return nil, models.ErrReachLimitOfRepo{
Limit: u.MaxRepoCreation,
}
}
repo := &models.Repository{
OwnerID: u.ID,
Owner: u,
OwnerName: u.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
OriginalURL: opts.OriginalURL,
OriginalServiceType: opts.GitServiceType,
IsPrivate: opts.IsPrivate,
IsFsckEnabled: !opts.IsMirror,
CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
Status: opts.Status,
IsEmpty: !opts.AutoInit,
}
err = models.WithTx(func(ctx models.DBContext) error {
if err = models.CreateRepository(ctx, doer, u, repo); err != nil {
return err
}
// No need for init mirror.
if !opts.IsMirror {
repoPath := models.RepoPath(u.Name, repo.Name)
if err = initRepository(ctx, repoPath, u, repo, opts); err != nil {
if err2 := os.RemoveAll(repoPath); err2 != nil {
log.Error("initRepository: %v", err)
return fmt.Errorf(
"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)
}
return fmt.Errorf("initRepository: %v", err)
}
// Initialize Issue Labels if selected
if len(opts.IssueLabels) > 0 {
if err = models.InitalizeLabels(ctx, repo.ID, opts.IssueLabels); err != nil {
return fmt.Errorf("initalizeLabels: %v", err)
}
}
if stdout, err := git.NewCommand("update-server-info").
SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)).
RunInDir(repoPath); err != nil {
log.Error("CreateRepitory(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
return fmt.Errorf("CreateRepository(git update-server-info): %v", err)
}
}
return nil
})
return repo, err
}

View File

@ -0,0 +1,145 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repository
import (
"fmt"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
)
func TestIncludesAllRepositoriesTeams(t *testing.T) {
assert.NoError(t, models.PrepareTestDatabase())
testTeamRepositories := func(teamID int64, repoIds []int64) {
team := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
assert.NoError(t, team.GetRepositories(), "%s: GetRepositories", team.Name)
assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name)
for i, rid := range repoIds {
if rid > 0 {
assert.True(t, team.HasRepository(rid), "%s: HasRepository(%d) %d", rid, i)
}
}
}
// Get an admin user.
user, err := models.GetUserByID(1)
assert.NoError(t, err, "GetUserByID")
// Create org.
org := &models.User{
Name: "All repo",
IsActive: true,
Type: models.UserTypeOrganization,
Visibility: structs.VisibleTypePublic,
}
assert.NoError(t, models.CreateOrganization(org, user), "CreateOrganization")
// Check Owner team.
ownerTeam, err := org.GetOwnerTeam()
assert.NoError(t, err, "GetOwnerTeam")
assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories")
// Create repos.
repoIds := make([]int64, 0)
for i := 0; i < 3; i++ {
r, err := CreateRepository(user, org, models.CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
assert.NoError(t, err, "CreateRepository %d", i)
if r != nil {
repoIds = append(repoIds, r.ID)
}
}
// Get fresh copy of Owner team after creating repos.
ownerTeam, err = org.GetOwnerTeam()
assert.NoError(t, err, "GetOwnerTeam")
// Create teams and check repositories.
teams := []*models.Team{
ownerTeam,
{
OrgID: org.ID,
Name: "team one",
Authorize: models.AccessModeRead,
IncludesAllRepositories: true,
},
{
OrgID: org.ID,
Name: "team 2",
Authorize: models.AccessModeRead,
IncludesAllRepositories: false,
},
{
OrgID: org.ID,
Name: "team three",
Authorize: models.AccessModeWrite,
IncludesAllRepositories: true,
},
{
OrgID: org.ID,
Name: "team 4",
Authorize: models.AccessModeWrite,
IncludesAllRepositories: false,
},
}
teamRepos := [][]int64{
repoIds,
repoIds,
{},
repoIds,
{},
}
for i, team := range teams {
if i > 0 { // first team is Owner.
assert.NoError(t, models.NewTeam(team), "%s: NewTeam", team.Name)
}
testTeamRepositories(team.ID, teamRepos[i])
}
// Update teams and check repositories.
teams[3].IncludesAllRepositories = false
teams[4].IncludesAllRepositories = true
teamRepos[4] = repoIds
for i, team := range teams {
assert.NoError(t, models.UpdateTeam(team, false, true), "%s: UpdateTeam", team.Name)
testTeamRepositories(team.ID, teamRepos[i])
}
// Create repo and check teams repositories.
org.Teams = nil // Reset teams to allow their reloading.
r, err := CreateRepository(user, org, models.CreateRepoOptions{Name: "repo-last"})
assert.NoError(t, err, "CreateRepository last")
if r != nil {
repoIds = append(repoIds, r.ID)
}
teamRepos[0] = repoIds
teamRepos[1] = repoIds
teamRepos[4] = repoIds
for i, team := range teams {
testTeamRepositories(team.ID, teamRepos[i])
}
// Remove repo and check teams repositories.
assert.NoError(t, models.DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository")
teamRepos[0] = repoIds[1:]
teamRepos[1] = repoIds[1:]
teamRepos[3] = repoIds[1:3]
teamRepos[4] = repoIds[1:]
for i, team := range teams {
testTeamRepositories(team.ID, teamRepos[i])
}
// Wipe created items.
for i, rid := range repoIds {
if i > 0 { // first repo already deleted.
assert.NoError(t, models.DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i)
}
}
assert.NoError(t, models.DeleteOrganization(org), "DeleteOrganization")
}

View File

@ -0,0 +1,87 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repository
import (
"fmt"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
)
// ForkRepository forks a repository
func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, desc string) (_ *models.Repository, err error) {
forkedRepo, err := oldRepo.GetUserFork(owner.ID)
if err != nil {
return nil, err
}
if forkedRepo != nil {
return nil, models.ErrForkAlreadyExist{
Uname: owner.Name,
RepoName: oldRepo.FullName(),
ForkName: forkedRepo.FullName(),
}
}
repo := &models.Repository{
OwnerID: owner.ID,
Owner: owner,
OwnerName: owner.Name,
Name: name,
LowerName: strings.ToLower(name),
Description: desc,
DefaultBranch: oldRepo.DefaultBranch,
IsPrivate: oldRepo.IsPrivate,
IsEmpty: oldRepo.IsEmpty,
IsFork: true,
ForkID: oldRepo.ID,
}
oldRepoPath := oldRepo.RepoPath()
err = models.WithTx(func(ctx models.DBContext) error {
if err = models.CreateRepository(ctx, doer, owner, repo); err != nil {
return err
}
if err = models.IncrementRepoForkNum(ctx, oldRepo.ID); err != nil {
return err
}
repoPath := models.RepoPath(owner.Name, repo.Name)
if stdout, err := git.NewCommand(
"clone", "--bare", oldRepoPath, repoPath).
SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", oldRepo.FullName(), repo.FullName())).
RunInDirTimeout(10*time.Minute, ""); err != nil {
log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, oldRepo, stdout, err)
return fmt.Errorf("git clone: %v", err)
}
if stdout, err := git.NewCommand("update-server-info").
SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())).
RunInDir(repoPath); err != nil {
log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
return fmt.Errorf("git update-server-info: %v", err)
}
if err = models.CreateDelegateHooks(repoPath); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
}
return nil
})
if err != nil {
return nil, err
}
ctx := models.DefaultDBContext()
if err = repo.UpdateSize(ctx); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
return repo, models.CopyLFS(ctx, repo, oldRepo)
}

View File

@ -0,0 +1,25 @@
// 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 repository
import (
"testing"
"code.gitea.io/gitea/models"
"github.com/stretchr/testify/assert"
)
func TestForkRepository(t *testing.T) {
assert.NoError(t, models.PrepareTestDatabase())
// user 13 has already forked repo10
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 13}).(*models.User)
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository)
fork, err := ForkRepository(user, user, repo, "test", "test")
assert.Nil(t, fork)
assert.Error(t, err)
assert.True(t, models.IsErrForkAlreadyExist(err))
}

View File

@ -0,0 +1,230 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repository
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
)
func generateExpansion(src string, templateRepo, generateRepo *models.Repository) string {
return os.Expand(src, func(key string) string {
switch key {
case "REPO_NAME":
return generateRepo.Name
case "TEMPLATE_NAME":
return templateRepo.Name
case "REPO_DESCRIPTION":
return generateRepo.Description
case "TEMPLATE_DESCRIPTION":
return templateRepo.Description
case "REPO_OWNER":
return generateRepo.OwnerName
case "TEMPLATE_OWNER":
return templateRepo.OwnerName
case "REPO_LINK":
return generateRepo.Link()
case "TEMPLATE_LINK":
return templateRepo.Link()
case "REPO_HTTPS_URL":
return generateRepo.CloneLink().HTTPS
case "TEMPLATE_HTTPS_URL":
return templateRepo.CloneLink().HTTPS
case "REPO_SSH_URL":
return generateRepo.CloneLink().SSH
case "TEMPLATE_SSH_URL":
return templateRepo.CloneLink().SSH
default:
return key
}
})
}
func checkGiteaTemplate(tmpDir string) (*models.GiteaTemplate, error) {
gtPath := filepath.Join(tmpDir, ".gitea", "template")
if _, err := os.Stat(gtPath); os.IsNotExist(err) {
return nil, nil
} else if err != nil {
return nil, err
}
content, err := ioutil.ReadFile(gtPath)
if err != nil {
return nil, err
}
gt := &models.GiteaTemplate{
Path: gtPath,
Content: content,
}
return gt, nil
}
func generateRepoCommit(repo, templateRepo, generateRepo *models.Repository, tmpDir string) error {
commitTimeStr := time.Now().Format(time.RFC3339)
authorSig := repo.Owner.NewGitSig()
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
"GIT_AUTHOR_NAME="+authorSig.Name,
"GIT_AUTHOR_EMAIL="+authorSig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr,
"GIT_COMMITTER_NAME="+authorSig.Name,
"GIT_COMMITTER_EMAIL="+authorSig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
// Clone to temporary path and do the init commit.
templateRepoPath := templateRepo.RepoPath()
if err := git.Clone(templateRepoPath, tmpDir, git.CloneRepoOptions{
Depth: 1,
}); err != nil {
return fmt.Errorf("git clone: %v", err)
}
if err := os.RemoveAll(path.Join(tmpDir, ".git")); err != nil {
return fmt.Errorf("remove git dir: %v", err)
}
// Variable expansion
gt, err := checkGiteaTemplate(tmpDir)
if err != nil {
return fmt.Errorf("checkGiteaTemplate: %v", err)
}
if err := os.Remove(gt.Path); err != nil {
return fmt.Errorf("remove .giteatemplate: %v", err)
}
// Avoid walking tree if there are no globs
if len(gt.Globs()) > 0 {
tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/"
if err := filepath.Walk(tmpDirSlash, func(path string, info os.FileInfo, walkErr error) error {
if walkErr != nil {
return walkErr
}
if info.IsDir() {
return nil
}
base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash)
for _, g := range gt.Globs() {
if g.Match(base) {
content, err := ioutil.ReadFile(path)
if err != nil {
return err
}
if err := ioutil.WriteFile(path,
[]byte(generateExpansion(string(content), templateRepo, generateRepo)),
0644); err != nil {
return err
}
break
}
}
return nil
}); err != nil {
return err
}
}
if err := git.InitRepository(tmpDir, false); err != nil {
return err
}
repoPath := repo.RepoPath()
if stdout, err := git.NewCommand("remote", "add", "origin", repoPath).
SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", templateRepoPath, tmpDir)).
RunInDirWithEnv(tmpDir, env); err != nil {
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git remote add: %v", err)
}
return initRepoCommit(tmpDir, repo, repo.Owner)
}
func generateGitContent(ctx models.DBContext, repo, templateRepo, generateRepo *models.Repository) (err error) {
tmpDir, err := ioutil.TempDir(os.TempDir(), "gitea-"+repo.Name)
if err != nil {
return fmt.Errorf("Failed to create temp dir for repository %s: %v", repo.RepoPath(), err)
}
defer func() {
if err := os.RemoveAll(tmpDir); err != nil {
log.Error("RemoveAll: %v", err)
}
}()
if err = generateRepoCommit(repo, templateRepo, generateRepo, tmpDir); err != nil {
return fmt.Errorf("generateRepoCommit: %v", err)
}
// re-fetch repo
if repo, err = models.GetRepositoryByIDCtx(ctx, repo.ID); err != nil {
return fmt.Errorf("getRepositoryByID: %v", err)
}
repo.DefaultBranch = "master"
if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
return fmt.Errorf("updateRepository: %v", err)
}
return nil
}
// GenerateGitContent generates git content from a template repository
func GenerateGitContent(ctx models.DBContext, templateRepo, generateRepo *models.Repository) error {
if err := generateGitContent(ctx, generateRepo, templateRepo, generateRepo); err != nil {
return err
}
if err := generateRepo.UpdateSize(ctx); err != nil {
return fmt.Errorf("failed to update size for repository: %v", err)
}
if err := models.CopyLFS(ctx, generateRepo, templateRepo); err != nil {
return fmt.Errorf("failed to copy LFS: %v", err)
}
return nil
}
// GenerateRepository generates a repository from a template
func GenerateRepository(ctx models.DBContext, doer, owner *models.User, templateRepo *models.Repository, opts models.GenerateRepoOptions) (_ *models.Repository, err error) {
generateRepo := &models.Repository{
OwnerID: owner.ID,
Owner: owner,
OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
IsPrivate: opts.Private,
IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
IsFsckEnabled: templateRepo.IsFsckEnabled,
TemplateID: templateRepo.ID,
}
if err = models.CreateRepository(ctx, doer, owner, generateRepo); err != nil {
return nil, err
}
repoPath := models.RepoPath(owner.Name, generateRepo.Name)
if err = checkInitRepository(repoPath); err != nil {
return generateRepo, err
}
return generateRepo, nil
}

214
modules/repository/init.go Normal file
View File

@ -0,0 +1,214 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repository
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"github.com/mcuadros/go-version"
"github.com/unknwon/com"
)
func prepareRepoCommit(ctx models.DBContext, repo *models.Repository, tmpDir, repoPath string, opts models.CreateRepoOptions) error {
commitTimeStr := time.Now().Format(time.RFC3339)
authorSig := repo.Owner.NewGitSig()
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
"GIT_AUTHOR_NAME="+authorSig.Name,
"GIT_AUTHOR_EMAIL="+authorSig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr,
"GIT_COMMITTER_NAME="+authorSig.Name,
"GIT_COMMITTER_EMAIL="+authorSig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
// Clone to temporary path and do the init commit.
if stdout, err := git.NewCommand("clone", repoPath, tmpDir).
SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)).
RunInDirWithEnv("", env); err != nil {
log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git clone: %v", err)
}
// README
data, err := models.GetRepoInitFile("readme", opts.Readme)
if err != nil {
return fmt.Errorf("GetRepoInitFile[%s]: %v", opts.Readme, err)
}
cloneLink := repo.CloneLink()
match := map[string]string{
"Name": repo.Name,
"Description": repo.Description,
"CloneURL.SSH": cloneLink.SSH,
"CloneURL.HTTPS": cloneLink.HTTPS,
}
if err = ioutil.WriteFile(filepath.Join(tmpDir, "README.md"),
[]byte(com.Expand(string(data), match)), 0644); err != nil {
return fmt.Errorf("write README.md: %v", err)
}
// .gitignore
if len(opts.Gitignores) > 0 {
var buf bytes.Buffer
names := strings.Split(opts.Gitignores, ",")
for _, name := range names {
data, err = models.GetRepoInitFile("gitignore", name)
if err != nil {
return fmt.Errorf("GetRepoInitFile[%s]: %v", name, err)
}
buf.WriteString("# ---> " + name + "\n")
buf.Write(data)
buf.WriteString("\n")
}
if buf.Len() > 0 {
if err = ioutil.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0644); err != nil {
return fmt.Errorf("write .gitignore: %v", err)
}
}
}
// LICENSE
if len(opts.License) > 0 {
data, err = models.GetRepoInitFile("license", opts.License)
if err != nil {
return fmt.Errorf("GetRepoInitFile[%s]: %v", opts.License, err)
}
if err = ioutil.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0644); err != nil {
return fmt.Errorf("write LICENSE: %v", err)
}
}
return nil
}
// initRepoCommit temporarily changes with work directory.
func initRepoCommit(tmpPath string, repo *models.Repository, u *models.User) (err error) {
commitTimeStr := time.Now().Format(time.RFC3339)
sig := u.NewGitSig()
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
"GIT_AUTHOR_NAME="+sig.Name,
"GIT_AUTHOR_EMAIL="+sig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr,
"GIT_COMMITTER_NAME="+sig.Name,
"GIT_COMMITTER_EMAIL="+sig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
if stdout, err := git.NewCommand("add", "--all").
SetDescription(fmt.Sprintf("initRepoCommit (git add): %s", tmpPath)).
RunInDir(tmpPath); err != nil {
log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err)
return fmt.Errorf("git add --all: %v", err)
}
binVersion, err := git.BinVersion()
if err != nil {
return fmt.Errorf("Unable to get git version: %v", err)
}
args := []string{
"commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", "Initial commit",
}
if version.Compare(binVersion, "1.7.9", ">=") {
sign, keyID := models.SignInitialCommit(tmpPath, u)
if sign {
args = append(args, "-S"+keyID)
} else if version.Compare(binVersion, "2.0.0", ">=") {
args = append(args, "--no-gpg-sign")
}
}
if stdout, err := git.NewCommand(args...).
SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)).
RunInDirWithEnv(tmpPath, env); err != nil {
log.Error("Failed to commit: %v: Stdout: %s\nError: %v", args, stdout, err)
return fmt.Errorf("git commit: %v", err)
}
if stdout, err := git.NewCommand("push", "origin", "master").
SetDescription(fmt.Sprintf("initRepoCommit (git push): %s", tmpPath)).
RunInDirWithEnv(tmpPath, models.InternalPushingEnvironment(u, repo)); err != nil {
log.Error("Failed to push back to master: Stdout: %s\nError: %v", stdout, err)
return fmt.Errorf("git push: %v", err)
}
return nil
}
func checkInitRepository(repoPath string) (err error) {
// Somehow the directory could exist.
if com.IsExist(repoPath) {
return fmt.Errorf("checkInitRepository: path already exists: %s", repoPath)
}
// Init git bare new repository.
if err = git.InitRepository(repoPath, true); err != nil {
return fmt.Errorf("git.InitRepository: %v", err)
} else if err = models.CreateDelegateHooks(repoPath); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
}
return nil
}
// InitRepository initializes README and .gitignore if needed.
func initRepository(ctx models.DBContext, repoPath string, u *models.User, repo *models.Repository, opts models.CreateRepoOptions) (err error) {
if err = checkInitRepository(repoPath); err != nil {
return err
}
// Initialize repository according to user's choice.
if opts.AutoInit {
tmpDir, err := ioutil.TempDir(os.TempDir(), "gitea-"+repo.Name)
if err != nil {
return fmt.Errorf("Failed to create temp dir for repository %s: %v", repo.RepoPath(), err)
}
defer os.RemoveAll(tmpDir)
if err = prepareRepoCommit(ctx, repo, tmpDir, repoPath, opts); err != nil {
return fmt.Errorf("prepareRepoCommit: %v", err)
}
// Apply changes and commit.
if err = initRepoCommit(tmpDir, repo, u); err != nil {
return fmt.Errorf("initRepoCommit: %v", err)
}
}
// Re-fetch the repository from database before updating it (else it would
// override changes that were done earlier with sql)
if repo, err = models.GetRepositoryByIDCtx(ctx, repo.ID); err != nil {
return fmt.Errorf("getRepositoryByID: %v", err)
}
if !opts.AutoInit {
repo.IsEmpty = true
}
repo.DefaultBranch = "master"
if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
return fmt.Errorf("updateRepository: %v", err)
}
return nil
}

View File

@ -116,7 +116,7 @@ func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opt
}
}
if err = repo.UpdateSize(); err != nil {
if err = repo.UpdateSize(models.DefaultDBContext()); err != nil {
log.Error("Failed to update size for repository: %v", err)
}

View File

@ -21,6 +21,8 @@ var (
MaxGitDiffLines int
MaxGitDiffLineCharacters int
MaxGitDiffFiles int
VerbosePush bool
VerbosePushDelay time.Duration
GCArgs []string `ini:"GC_ARGS" delim:" "`
EnableAutoGitWireProtocol bool
Timeout struct {
@ -36,6 +38,8 @@ var (
MaxGitDiffLines: 1000,
MaxGitDiffLineCharacters: 5000,
MaxGitDiffFiles: 100,
VerbosePush: true,
VerbosePushDelay: 5 * time.Second,
GCArgs: []string{},
EnableAutoGitWireProtocol: true,
Timeout: struct {

View File

@ -5,6 +5,7 @@
package task
import (
"encoding/json"
"fmt"
"code.gitea.io/gitea/models"
@ -12,7 +13,9 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migrations/base"
"code.gitea.io/gitea/modules/queue"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
)
// taskQueue is a global queue of tasks
@ -52,10 +55,56 @@ func handle(data ...queue.Data) {
// MigrateRepository add migration repository to task
func MigrateRepository(doer, u *models.User, opts base.MigrateOptions) error {
task, err := models.CreateMigrateTask(doer, u, opts)
task, err := CreateMigrateTask(doer, u, opts)
if err != nil {
return err
}
return taskQueue.Push(task)
}
// CreateMigrateTask creates a migrate task
func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models.Task, error) {
bs, err := json.Marshal(&opts)
if err != nil {
return nil, err
}
var task = models.Task{
DoerID: doer.ID,
OwnerID: u.ID,
Type: structs.TaskTypeMigrateRepo,
Status: structs.TaskStatusQueue,
PayloadContent: string(bs),
}
if err := models.CreateTask(&task); err != nil {
return nil, err
}
repo, err := repo_module.CreateRepository(doer, u, models.CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
OriginalURL: opts.OriginalURL,
GitServiceType: opts.GitServiceType,
IsPrivate: opts.Private,
IsMirror: opts.Mirror,
Status: models.RepositoryBeingMigrated,
})
if err != nil {
task.EndTime = timeutil.TimeStampNow()
task.Status = structs.TaskStatusFailed
err2 := task.UpdateCols("end_time", "status")
if err2 != nil {
log.Error("UpdateCols Failed: %v", err2.Error())
}
return nil, err
}
task.RepoID = repo.ID
if err = task.UpdateCols("repo_id"); err != nil {
return nil, err
}
return &task, nil
}

View File

@ -1036,7 +1036,6 @@ pulls.cannot_auto_merge_helper=Pro vyřešení konfliktů proveďte ruční slou
pulls.no_merge_desc=Tento požadavek na natažení nemůže být sloučen, protože všechny možnosti repozitáře na sloučení jsou zakázány.
pulls.no_merge_helper=Povolte možnosti sloučení v nastavení repozitáře nebo proveďte sloučení požadavku na natažení ručně.
pulls.no_merge_wip=Požadavek na natažení nemůže být sloučen protože je označen jako nedokončený.
pulls.no_merge_status_check=Tento požadavek na natažení nemůže být sloučen, protože ne všechny požadované kontroly stavu byly úspěšné.
pulls.merge_pull_request=Sloučit požadavek na natažení
pulls.rebase_merge_pull_request=Rebase a sloučit
pulls.rebase_merge_commit_pull_request=Rebase a sloučit (--no-ff)

View File

@ -1061,7 +1061,6 @@ pulls.cannot_auto_merge_helper=Bitte manuell zusammenführen, um die Konflikte z
pulls.no_merge_desc=Dieser Pull-Request kann nicht gemerged werden, da keine Mergeoptionen aktiviert sind.
pulls.no_merge_helper=Aktiviere Mergeoptionen in den Repositoryeinstellungen oder merge den Pull-Request manuell.
pulls.no_merge_wip=Dieser Pull Request kann nicht zusammengeführt werden, da er als Work In Progress gekennzeichnet ist.
pulls.no_merge_status_check=Dieser Pull-Request kann nicht zusammengeführt werden, da nicht alle erforderlichen Statusprüfungen erfolgreich waren.
pulls.merge_pull_request=Pull-Request zusammenführen
pulls.rebase_merge_pull_request=Rebase und Mergen
pulls.rebase_merge_commit_pull_request=Rebasen und Mergen (--no-ff)

View File

@ -1057,7 +1057,6 @@ pulls.cannot_auto_merge_helper=Combinar manualmente para resolver los conflictos
pulls.no_merge_desc=Este pull request no se puede combinar porque todas las opciones de combinación del repositorio están deshabilitadas.
pulls.no_merge_helper=Habilite las opciones de combinación en la configuración del repositorio o fusione el pull request manualmente.
pulls.no_merge_wip=Este pull request no se puede combinar porque está marcada como un trabajo en progreso.
pulls.no_merge_status_check=No se puede fusionar este pull request porque no todas las comprobaciones de estado requeridas resultaron exitosas.
pulls.merge_pull_request=Fusionar Pull Request
pulls.rebase_merge_pull_request=Hacer Rebase y Fusionar
pulls.rebase_merge_commit_pull_request=Hacer Rebase y Fusionar (--no-ff)

View File

@ -1062,7 +1062,6 @@ pulls.cannot_auto_merge_helper=به صورتی دستی ادغام کنید تا
pulls.no_merge_desc=این تقاضای واکشی قابل ادغام نیست لذا تمامی گزینه های ادغام مخزن غیر فعال هستند.
pulls.no_merge_helper=گزینه های ادغام را در تنظیمات مخزن فعال کنید یا از تقاضای واکشی به صورت دستی ادغام نمایید.
pulls.no_merge_wip=این تقاضای واکشی قابل ادغام نیست لذا اکنون به این مخزن درحال پردازش علامت گذاری شده است.
pulls.no_merge_status_check=این تقاضای واکشی نمی‌تواند ادغام شود لذا تمامی حالت های ضروری با موفقیت بررسی نشدند.
pulls.merge_pull_request=ادغام تقاضای واکشی
pulls.rebase_merge_pull_request=بازگردانی و ادغام
pulls.rebase_merge_commit_pull_request=بازگردانی و ادغام (--no-ff)

View File

@ -1054,7 +1054,6 @@ pulls.cannot_auto_merge_helper=Fusionner manuellement pour résoudre les conflit
pulls.no_merge_desc=Cette demande de fusion ne peut être appliquée directement car toutes les options de fusion du dépôt sont désactivées.
pulls.no_merge_helper=Activez des options de fusion dans les paramètres du dépôt ou fusionnez la demande manuellement.
pulls.no_merge_wip=Cette demande d'ajout ne peut pas être fusionnée car elle est marquée comme en cours de chantier.
pulls.no_merge_status_check=Cette demande de pull ne peut pas être fusionnée car tous les contrôles de statut requis ne sont pas réussis.
pulls.merge_pull_request=Fusionner la demande d'ajout
pulls.rebase_merge_pull_request=Rebase et fusionner
pulls.rebase_merge_commit_pull_request=Rebase et Fusion (--no-ff)

View File

@ -10,6 +10,7 @@ link_account=Tautan Akun
register=Daftar
website=Situs Web
version=Versi
powered_by=Didukung oleh %s
page=Halaman
template=Contoh
language=Bahasa
@ -29,8 +30,16 @@ twofa_scratch=Kode Awal Dua Faktor
passcode=Kode Akses
u2f_insert_key=Masukkan kunci keamanan anda
u2f_sign_in=Tekan tombol pada kunci keamanan anda. Jika kunci keamanan anda tidak memiliki tombol, masukkan kembali.
u2f_press_button=Silahkan tekan tombol pada kunci keamanan anda…
u2f_use_twofa=Menggunakan kode dua faktor dari telepon anda
u2f_error=Tidak dapat membaca kunci keamanan Anda.
u2f_unsupported_browser=Browser Anda tidak mendukung kunci keamanan U2F.
u2f_error_1=Terdapat kesalahan yang tidak diketahui. Mohon coba lagi.
u2f_error_2=Pastikan menggunakan URL yang benar dan terenkripsi (https://).
u2f_error_3=Server tidak bisa memproses permintaan anda.
u2f_error_4=Kunci keamanan tidak diperbolehkan untuk permintaan ini. Pastikan bahwa kunci ini belum terdaftar sebelumnya.
u2f_error_5=Waktu habis sebelum kunci Anda dapat dibaca. Mohon muat ulang halaman ini dan coba lagi.
u2f_reload=Muat Ulang
repository=Repositori
@ -58,25 +67,48 @@ forks=Garpu
activities=Aktivitas
pull_requests=Tarik Permintaan
issues=Masalah
milestones=Tonggak
cancel=Batal
add=Tambah
add_all=Tambah Semua
remove=Buang
remove_all=Buang Semua
write=Tulis
preview=Pratinjau
loading=Memuat…
[startpage]
app_desc=Sebuah layanan hosting Git sendiri yang tanpa kesulitan
install=Mudah dipasang
install_desc=Cukup <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">jalankan program biner</a> yang sesuai dengan sistem operasi Anda. Atau jalankan Gitea dengan <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> atau <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, atau install dari <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">paket</a>.
platform=Lintas platform
platform_desc=Gitea bisa digunakan di mana <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> bisa dijalankan: Windows, macOS, Linux, ARM, dll. Silahkan pilih yang Anda suka!
lightweight=Ringan
lightweight_desc=Gitea hanya membutuhkan persyaratan minimal dan bisa berjalan pada Raspberry Pi yang murah. Bisa menghemat listrik!
license=Sumber Terbuka
license_desc="Go get" (Dapatkan kode sumber dari) <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! Mari bergabung dengan <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">berkontribusi</a> untuk membuat proyek ini lebih baik. Jangan malu untuk menjadi kontributor!
[install]
install=Pemasangan
title=Konfigurasi Awal
docker_helper=Jika Anda menjalankan Gitea di dalam Docker, baca <a target="_blank" rel="noopener" href="%s">dokumentasi </a> sebelum mengubah pengaturan.
requite_db_desc=Gitea memerlukan MySQL, PostgreSQL, MSSQL atau SQLite3.
db_title=Pengaturan Basis Data
db_type=Tipe Basis Data
host=Host
user=Nama Pengguna
password=Kata Sandi
db_name=Nama Basis Data
db_helper=Untuk pengguna MySQL: Mohon gunakan mesin penyimpanan InnoDB, dan jika Anda menggunakan enkoding "utf8mb4", versi InnoDB Anda harus diatas 5.6.
ssl_mode=SSL
charset=Jenis karakter
path=Jalur
sqlite_helper=Jalur berkas untuk basis data SQLite3 atau TiDB.<br>Masukkan path absolut jika anda menjalankan Gitea sebagai layanan.
no_admin_and_disable_registration=Anda tidak dapat menonaktifkan pendaftaran tanpa membuat akun admin.
err_empty_admin_password=Sandi administrator tidak boleh kosong.
err_empty_admin_email=Email administrator tidak boleh kosong.
general_title=Pengaturan Umum
app_name=Judul Situs
@ -111,13 +143,19 @@ server_service_title=Server dan Pengaturan Layanan Pihak Ketiga
offline_mode=Aktifkan Mode Lokal
offline_mode_popup=Non-aktifkan jaringan pengiriman konten dari pihak ketiga dan layani semua sumber daya secara lokal.
disable_gravatar=Non-aktifkan Gravatar
federated_avatar_lookup=Aktifkan Avatar Terfederasi
federated_avatar_lookup_popup=Mengaktifkan pencarian avatar federasi menggunakan Libravatar.
disable_registration_popup=Nonaktifkan pendaftaran oleh pengguna. Hanya admin yang dapat membuat akun pengguna baru.
allow_only_external_registration_popup=Perbolehkan Pendaftaran Hanya Melalui Layanan External
openid_signin=Aktifkan Login OpenID
openid_signin_popup=Aktifkan masuk pengguna lewat OpenID.
openid_signup=Aktifkan Pendaftaran OpenID
openid_signup_popup=Aktifkan pendaftaran berdasarkan OpenID.
enable_captcha=Aktifkan CAPTCHA
enable_captcha_popup=Membutukan CAPTCHA untuk pendaftaran.
require_sign_in_view=Anda Harus Login untuk Melihat Halaman
require_sign_in_view_popup=Batasi akses halaman hanya pada pengguna yang masuk. Pengunjung hanya dapat melihat halaman masuk dan pendaftaran.
admin_setting_desc=Akun administrator tidak wajib dibuat. Pengguna yang pertama kali mendaftar akan secara otomatis menjadi administrator.
admin_title=Pengaturan Akun Admin
admin_name=Nama Pengguna Admin
admin_password=Kata sandi
@ -128,6 +166,7 @@ test_git_failed=Tidak dapat menguji perintah 'git': %v
sqlite3_not_available=Gitea versi ini tidak mendukung SQLite3, Silahkan untuh versi biner resmi dari %s (bukan versi 'gobuild').
invalid_db_setting=Pengaturan basis data tidak valid: %v
save_config_failed=Gagal menyimpan konfigurasi: %v
install_success=Selamat datang! Terimakasih telah memilih Gitea. Selamat bersenang-senang dan hati-hati!
[home]
uname_holder=Nama Pengguna atau Alamat Surel
@ -161,30 +200,43 @@ social_register_helper_msg=Sudah memiliki akun? Hubungkan sekarang!
remember_me=Ingat Saya
forgot_password_title=Lupa Kata Sandi
forgot_password=Lupa kata sandi?
sign_up_now=Butuh akun? Daftar sekarang.
sign_up_successful=Akun berhasil dibuat.
confirmation_mail_sent_prompt=Surel konfirmasi baru telah dikirim ke <b>%s</b>. Silakan periksa kotak masuk anda dalam %s ke depan untuk menyelesaikan proses pendaftaran.
active_your_account=Aktifkan Akun Anda
account_activated=Akun telah diaktifkan
prohibit_login_desc=Akun Anda tidak diperbolehkan untuk masuk, silakan hubungi admin situs.
has_unconfirmed_mail=Hai %s, anda memiliki sebuah alamat surel yang belum dikonfirmasi (<b>%s</b>). Jika anda belum menerima surel konfirmasi atau perlu untuk mengirim ulang yang baru, silakan klik pada tombol di bawah.
resend_mail=Klik di sini untuk mengirim ulang surel aktivasi anda
email_not_associate=Alamat surel tidak terhubung dengan akun apapun.
send_reset_mail=Kirim Surel Pemulihan Akun
reset_password=Pemulihan Akun
password_too_short=Panjang kata sandi tidak boleh kurang dari %d karakter.
verify=Verifikasi
scratch_code=Kode coretan
use_scratch_code=Gunakan kode coretan
twofa_scratch_used=Anda telah menggunakan kode coretan anda. Anda telah dialihkan ke halaman pengaturan dua-faktor jadi anda boleh menghapus pendaftaran perangkat anda atau menghasilkan kode coretan yang baru.
twofa_passcode_incorrect=Kata sandi Anda salah. Jika Anda salah tempatkan perangkat Anda, gunakan kode gosok Anda untuk masuk.
twofa_scratch_token_incorrect=Kode coretan anda tidak tepat.
login_openid=OpenID
openid_connect_submit=Sambungkan
openid_connect_title=Sambungkan ke akun yang sudah ada
openid_register_title=Buat akun baru
openid_signin_desc=Masukkan URI OpenID Anda. Misalnya: https://anne.me, bob.openid.org.cn, atau gnusocial.net/carry.
email_domain_blacklisted=Anda tidak dapat mendaftar dengan alamat email.
authorize_application=Izinkan aplikasi
[mail]
activate_account=Silakan aktifkan akun anda
activate_email=Verifikasi alamat surel anda
reset_password=Pulihkan akun Anda
register_success=Pendaftaran berhasil
register_notify=Selamat Datang di Gitea
[modal]
yes=Ya
no=Tidak
modify=Perbarui
[form]
UserName=Nama Pengguna
@ -214,43 +266,77 @@ email_error=` bukan alamat surel yang valid. `
url_error=` bukan URL yang valid.`
include_error=` harus mengandung substring '%s'.`
unknown_error=Kesalahan yang tidak diketahui:
lang_select_error=Pilih bahasa dari daftar.
email_been_used=Alamat email sudah digunakan.
openid_been_used=Alamat OpenID '%s' sudah digunakan.
username_password_incorrect=Nama pengguna atau sandi salah.
password_complexity=Kata sandi tidak memenuhi persyaratan kerumitan:
password_lowercase_one=Sekurang-kurangnya satu karakter kecil
password_uppercase_one=Sekurang-kurangnya satu karakter besar
password_digit_one=Sekurang-kurangnya satu angka
password_special_one=Sekurang-kurangnya satu karater khusus (tanda baca, kurung, kutip, dll.)
enterred_invalid_repo_name=Nama repositori yang Anda masukkan salah.
enterred_invalid_owner_name=Nama pemilik baru salah.
enterred_invalid_password=Kata sandi yang Anda masukkan salah.
user_not_exist=Pengguna tidak ada.
last_org_owner=Anda tidak dapat menghapus pengguna terakhir dari tim pemilik. Harus ada setidaknya satu pemilik dalam tim yang diberikan.
cannot_add_org_to_team=Sebuah organisasi tidak dapat ditambahkan sebagai anggota tim.
invalid_ssh_key=Tidak dapat memverifikasi kunci SSH Anda: %s
invalid_gpg_key=Tidak dapat memverifikasi kunci GPG Anda: %s
unable_verify_ssh_key=Tidak dapat memverifikasi kunci SSH; periksa kembali bila ada kesalahan.
auth_failed=Otentikasi gagal: %v
still_own_repo=Akun anda memiliki satu atau lebih repositori, pindahkan atau transfer terlebih dahulu.
still_has_org=Akun Anda adalah anggota dari satu atau lebih organisasi, tinggalkan terlebih dahulu.
org_still_own_repo=Organisasi ini masih memiliki satu atau lebih repositori; hapus atau transfer terlebih dahulu.
target_branch_not_exist=Target cabang tidak ada.
[user]
change_avatar=Ganti avatar anda…
join_on=Telah bergabung di
repositories=Repositori
activity=Aktivitas Publik
followers=Pengikut
starred=Repositori Terbintang
following=Mengikuti
follow=Ikuti
unfollow=Berhenti Mengikuti
heatmap.loading=Memuat Peta Panas…
user_bio=Biografi
form.name_reserved=Nama pengguna '%s' dicadangkan.
form.name_pattern_not_allowed=Pola '%s' tidak diperbolehkan dalam nama pengguna.
[settings]
profile=Profil
account=Akun
password=Kata Sandi
security=Keamanan
avatar=Avatar
ssh_gpg_keys=Kunci SSH / GPG
social=Akun Sosial
applications=Aplikasi
orgs=Kelola organisasi
repos=Repositori
delete=Hapus Akun
twofa=Otentikasi Dua-Faktor
account_link=Akun Tertaut
organization=Organisasi
uid=Uid
u2f=Kunci keamanan
public_profile=Profil Publik
profile_desc=Alamat email Anda akan digunakan untuk notifikasi dan operasi lainnya.
password_username_disabled=Pengguna non-lokal tidak diizinkan untuk mengubah nama pengguna mereka. Silakan hubungi administrator sistem anda untuk lebih lanjut.
full_name=Nama Lengkap
website=Situs Web
location=Lokasi
update_profile=Perbarui Profil
update_profile_success=Profil anda telah diperbarui.
change_username_prompt=Catatan: Perubahan nama pengguna juga mengubah URL akun Anda.
continue=Lanjutkan
cancel=Batalkan
@ -318,6 +404,7 @@ confirm_delete_account=Konfirmasi Penghapusan
owner=Pemilik
repo_name=Nama Repositori
visibility=Jarak pandang
clone_helper=Butuh bantuan kloning? Kunjungi <a target="_blank" rel="noopener noreferrer" href="%s">Bantuan</a>.
fork_repo=Cabang Gudang penyimpanan
fork_from=Cabang Dari
repo_desc=Deskripsi
@ -508,6 +595,7 @@ pulls.new=Permintaan Tarik Baru
pulls.filter_branch=Penyaringan cabang
pulls.no_results=Hasil tidak ditemukan.
pulls.create=Buat Permintaan Tarik
pulls.title_desc=ingin menggabungkan komit %[1]d dari <code>%[2]s</code> menuju <code id="branch_target">%[3]s</code>
pulls.merged_title_desc=commit %[1]d telah digabungkan dari <code>%[2]s</code> menjadi <code>%[3]s</code> %[4]s
pulls.tab_conversation=Percakapan
pulls.tab_commits=Melakukan

View File

@ -1061,7 +1061,8 @@ pulls.cannot_auto_merge_helper=コンフリクトを解消するため手動で
pulls.no_merge_desc=リポジトリのマージオプションがすべて無効になっているため、このプルリクエストをマージすることはできせん。
pulls.no_merge_helper=リポジトリ設定でマージを有効にするか、手動でマージしてください。
pulls.no_merge_wip=このプルリクエストはWork in Progressとマークされているため、マージすることはできません。
pulls.no_merge_status_check=すべての必要なステータスチェックが成功していないため、このプルリクエストはマージできません。
pulls.no_merge_not_ready=このプルリクエストはマージする準備ができていません。 レビュー状況とステータスチェックを確認してください。
pulls.no_merge_access=このプルリクエストをマージする権限がありません。
pulls.merge_pull_request=プルリクエストをマージ
pulls.rebase_merge_pull_request=リベースしてマージ
pulls.rebase_merge_commit_pull_request=リベースしてマージ(--no-ff)
@ -1412,6 +1413,8 @@ settings.protect_approvals_whitelist_enabled=ホワイトリストに登録し
settings.protect_approvals_whitelist_enabled_desc=ホワイトリストに登録したユーザーやチームによるレビューのみを、必要な承認とみなします。 承認のホワイトリストが無い場合は、書き込み権限がある人によるレビューを必要な承認とみなします。
settings.protect_approvals_whitelist_users=ホワイトリストに含めるレビューア:
settings.protect_approvals_whitelist_teams=ホワイトリストに含めるレビューチーム:
settings.dismiss_stale_approvals=古くなった承認を取り消す
settings.dismiss_stale_approvals_desc=プルリクエストの内容を変える新たなコミットがブランチにプッシュされた場合、以前の承認を取り消します。
settings.add_protected_branch=保護を有効にする
settings.delete_protected_branch=保護を無効にする
settings.update_protect_branch_success=ブランチ '%s' の保護を更新しました。
@ -2025,8 +2028,54 @@ monitor.execute_time=実行時間
monitor.process.cancel=処理をキャンセル
monitor.process.cancel_desc=処理をキャンセルするとデータが失われる可能性があります
monitor.process.cancel_notices=キャンセル: <strong>%s</strong>?
monitor.queues=キュー
monitor.queue=キュー: %s
monitor.queue.name=キュー名
monitor.queue.type=種類
monitor.queue.exemplar=要素の型
monitor.queue.numberworkers=ワーカー数
monitor.queue.maxnumberworkers=ワーカー数上限
monitor.queue.review=設定確認
monitor.queue.review_add=確認/ワーカー追加
monitor.queue.configuration=初期設定
monitor.queue.nopool.title=ワーカープールはありません
monitor.queue.nopool.desc=このキューは他のキューをラップし、これ自体にはワーカープールがありません。
monitor.queue.wrapped.desc=wrappedキューは、すぐに開始されないキューをラップし、入ってきたリクエストをチャンネルにバッファリングします。 これ自体にはワーカープールがありません。
monitor.queue.persistable-channel.desc=persistable-channelキューは二つのキューをラップします。 一つはchannelキューで、自分のワーカープールを持ちます。もう一つはlevelキューで、前回のシャットダウンからリクエストを引き継ぐためのものです。 これ自体にはワーカープールがありません。
monitor.queue.pool.timeout=タイムアウト
monitor.queue.pool.addworkers.title=ワーカーの追加
monitor.queue.pool.addworkers.submit=ワーカーを追加
monitor.queue.pool.addworkers.desc=このプールに、タイムアウト付きまたはタイムアウト無しでワーカーを追加します。 タイムアウトを指定した場合は、タイムアウト後にそれらのワーカーがこのプールから取り除かれます。
monitor.queue.pool.addworkers.numberworkers.placeholder=ワーカー数
monitor.queue.pool.addworkers.timeout.placeholder=0でタイムアウト無し
monitor.queue.pool.addworkers.mustnumbergreaterzero=追加するワーカー数は1以上にしてください
monitor.queue.pool.addworkers.musttimeoutduration=タイムアウトは 、Go言語の時間差表記(例 5m)、または0にしてください
monitor.queue.settings.title=プール設定
monitor.queue.settings.desc=ワーカーへのキューのブロックが発生すると、それに応じてプール数がブースト分ずつ動的に増えます。 これらの変更は現在のワーカーグループには影響しません。
monitor.queue.settings.timeout=ブースト分のタイムアウト
monitor.queue.settings.timeout.placeholder=現在の設定 %[1]v
monitor.queue.settings.timeout.error=タイムアウトは 、Go言語の時間差表記(例 5m)、または0にしてください
monitor.queue.settings.numberworkers=ブースト分のワーカー数
monitor.queue.settings.numberworkers.placeholder=現在の設定 %[1]d
monitor.queue.settings.numberworkers.error=追加するワーカー数はゼロ以上にしてください
monitor.queue.settings.maxnumberworkers=ワーカー数上限
monitor.queue.settings.maxnumberworkers.placeholder=現在の設定 %[1]d
monitor.queue.settings.maxnumberworkers.error=ワーカー数上限は数値にしてください
monitor.queue.settings.submit=設定を更新
monitor.queue.settings.changed=設定を更新しました
monitor.queue.settings.blocktimeout=現在のブロックタイムアウト
monitor.queue.settings.blocktimeout.value=%[1]v
monitor.queue.pool.none=このキューにはプールがありません
monitor.queue.pool.added=ワーカーグループを追加しました
monitor.queue.pool.max_changed=ワーカー数の上限を変更しました
monitor.queue.pool.workers.title=アクティブなワーカーグループ
monitor.queue.pool.workers.none=ワーカーグループはありません。
monitor.queue.pool.cancel=ワーカーグループのシャットダウン
monitor.queue.pool.cancelling=ワーカーグループをシャットダウンしています
monitor.queue.pool.cancel_notices=このワーカー数 %s のグループをシャットダウンしますか?
monitor.queue.pool.cancel_desc=キューをワーカーグループ無しのままにすると、リクエストがブロックし続ける原因となります。
notices.system_notice_list=システム通知
notices.view_detail_header=通知の詳細を表示

View File

@ -1061,7 +1061,6 @@ pulls.cannot_auto_merge_helper=Sapludiniet manuāli, lai atrisinātu konfliktus.
pulls.no_merge_desc=Šo izmaiņu pieprasījumu nav iespējams sapludināt, jo nav atļauts neviens sapludināšanas veids.
pulls.no_merge_helper=Lai sapludinātu šo izmaiņu pieprasījumu, iespējojiet vismaz vienu sapludināšanas veidu repozitorija iestatījumos vai sapludiniet to manuāli.
pulls.no_merge_wip=Šo izmaiņu pieprasījumu nav iespējams sapludināt, jo tas ir atzīmēts, ka darbs pie tā vēl nav pabeigts.
pulls.no_merge_status_check=Šo izmaiņu pieprasījumu nevar saplusināt, jo nav veiksmīgi izildītas visas obligātas statusa pārbaudes.
pulls.merge_pull_request=Izmaiņu pieprasījuma sapludināšana
pulls.rebase_merge_pull_request=Pārbāzēt un sapludināt
pulls.rebase_merge_commit_pull_request=Pārbāzēt un sapludināt (--no-ff)

View File

@ -1055,7 +1055,6 @@ pulls.cannot_auto_merge_helper=Scal ręcznie, aby rozwiązać konflikty.
pulls.no_merge_desc=Ten Pull Request nie może zostać scalony, ponieważ wszystkie opcje scalania dla tego repozytorium są wyłączone.
pulls.no_merge_helper=Włącz opcje scalania w ustawieniach repozytorium, lub scal ten Pull Request ręcznie.
pulls.no_merge_wip=Ten pull request nie może być automatycznie scalony, ponieważ jest oznaczony jako praca w toku.
pulls.no_merge_status_check=Ten Pull Request nie może być scalony, bo nie wszystkie kontrole stanów były pomyślne.
pulls.merge_pull_request=Scal Pull Request
pulls.rebase_merge_pull_request=Zmień bazę i scal
pulls.rebase_merge_commit_pull_request=Zmień bazę i scal (--no-ff)

View File

@ -1061,7 +1061,6 @@ pulls.cannot_auto_merge_helper=Faça o merge manualmente para resolver os confli
pulls.no_merge_desc=O merge deste pull request não pode ser aplicado porque todas as opções de mesclagem do repositório estão desabilitadas.
pulls.no_merge_helper=Habilite as opções de merge nas configurações do repositório ou faça o merge do pull request manualmente.
pulls.no_merge_wip=O merge deste pull request não pode ser aplicado porque está marcado como um trabalho em andamento.
pulls.no_merge_status_check=Este pull request não pode ter seu merge aplicado porque nem todas as verificações de status necessárias foram bem sucedidas.
pulls.merge_pull_request=Aplicar merge do pull request
pulls.rebase_merge_pull_request=Aplicar Rebase e Merge
pulls.rebase_merge_commit_pull_request=Aplicar Rebase e Merge (--no-ff)

View File

@ -1054,7 +1054,6 @@ pulls.cannot_auto_merge_helper=Çakışmaları çözmek için el ile birleştiri
pulls.no_merge_desc=Tüm depo birleştirme seçenekleri devre dışı bırakıldığından, bu değişiklik isteği birleştirilemez.
pulls.no_merge_helper=Depo ayarlarındaki birleştirme seçeneklerini etkinleştirin veya değişiklik isteğini el ile birleştirin.
pulls.no_merge_wip=Bu değişiklik isteği birleştirilemez çünkü devam eden bir çalışma olarak işaretlendi.
pulls.no_merge_status_check=Gerekli olan tüm durum denetimleri başarılı olmadığından bu değişiklik isteği birleştirilemez.
pulls.merge_pull_request=Değişiklik İsteğini Birleştir
pulls.rebase_merge_pull_request=Rebase ve Merge
pulls.rebase_merge_commit_pull_request=Rebase ve Merge (--no-ff)

View File

@ -1058,7 +1058,6 @@ pulls.cannot_auto_merge_helper=Злийте вручну для вирішенн
pulls.no_merge_desc=Цей запити на злиття неможливо злити, оскільки всі параметри об'єднання репозиторія вимкнено.
pulls.no_merge_helper=Увімкніть параметри злиття в налаштуваннях репозиторія або злийте запити на злиття вручну.
pulls.no_merge_wip=Цей пулл-реквест не можливо об'єднати, тому-що він вже виконується.
pulls.no_merge_status_check=Не вдалося об'єднати цей запит на злиття, оскільки не всі обов'язкові перевірки були успішними.
pulls.merge_pull_request=Об'єднати запит на злиття
pulls.rebase_merge_pull_request=Зробити Rebase і злити
pulls.rebase_merge_commit_pull_request=Rebase та злитя (--no-ff)

View File

@ -1061,7 +1061,6 @@ pulls.cannot_auto_merge_helper=手动合并解决此冲突
pulls.no_merge_desc=由于未启用合并选项,此合并请求无法被合并。
pulls.no_merge_helper=在仓库设置中启用合并选项或者手工合并请求。
pulls.no_merge_wip=这个合并请求无法合并,因为被标记为尚未完成的工作。
pulls.no_merge_status_check=此合并请求不能合并,因为不是所有的状态检查都是成功的。
pulls.merge_pull_request=合并请求
pulls.rebase_merge_pull_request=变基并合并
pulls.rebase_merge_commit_pull_request=变基合并 (--no-ff)

View File

@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers"
)
@ -25,6 +26,6 @@ func Organizations(ctx *context.Context) {
routers.RenderUserSearch(ctx, &models.SearchUserOptions{
Type: models.UserTypeOrganization,
PageSize: setting.UI.Admin.OrgPagingNum,
Private: true,
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
}, tplOrgs)
}

View File

@ -43,7 +43,7 @@ func DeleteRepo(ctx *context.Context) {
ctx.ServerError("DeleteRepository", err)
return
}
log.Trace("Repository deleted: %s/%s", repo.MustOwner().Name, repo.Name)
log.Trace("Repository deleted: %s", repo.FullName())
ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
ctx.JSON(200, map[string]interface{}{

View File

@ -104,7 +104,7 @@ func GetAllOrgs(ctx *context.APIContext) {
OrderBy: models.SearchOrderByAlphabetically,
Page: ctx.QueryInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
Private: true,
Visible: []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate},
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err)

View File

@ -821,6 +821,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/user/orgs", reqToken(), org.ListMyOrgs)
m.Get("/users/:username/orgs", org.ListUserOrgs)
m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create)
m.Get("/orgs", org.GetAll)
m.Group("/orgs/:orgname", func() {
m.Combo("").Get(org.Get).
Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit).

View File

@ -66,6 +66,53 @@ func ListUserOrgs(ctx *context.APIContext) {
listUserOrgs(ctx, u, ctx.User.IsAdmin)
}
// GetAll return list of all public organizations
func GetAll(ctx *context.APIContext) {
// swagger:operation Get /orgs organization orgGetAll
// ---
// summary: Get list of organizations
// produces:
// - application/json
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses:
// "200":
// "$ref": "#/responses/OrganizationList"
vMode := []api.VisibleType{api.VisibleTypePublic}
if ctx.IsSigned {
vMode = append(vMode, api.VisibleTypeLimited)
if ctx.User.IsAdmin {
vMode = append(vMode, api.VisibleTypePrivate)
}
}
publicOrgs, _, err := models.SearchUsers(&models.SearchUserOptions{
Type: models.UserTypeOrganization,
OrderBy: models.SearchOrderByAlphabetically,
Page: ctx.QueryInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
Visible: vMode,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err)
return
}
orgs := make([]*api.Organization, len(publicOrgs))
for i := range publicOrgs {
orgs[i] = convert.ToOrganization(publicOrgs[i])
}
ctx.JSON(http.StatusOK, &orgs)
}
// Create api for create organization
func Create(ctx *context.APIContext, form api.CreateOrgOption) {
// swagger:operation POST /orgs organization orgCreate

View File

@ -67,19 +67,23 @@ func SearchIssues(ctx *context.APIContext) {
// find repos user can access (for issue search)
repoIDs := make([]int64, 0)
opts := &models.SearchRepoOptions{
PageSize: 15,
Private: false,
AllPublic: true,
TopicOnly: false,
Collaborate: util.OptionalBoolNone,
OrderBy: models.SearchOrderByRecentUpdated,
Actor: ctx.User,
}
if ctx.IsSigned {
opts.Private = true
opts.AllLimited = true
}
issueCount := 0
for page := 1; ; page++ {
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
Page: page,
PageSize: 15,
Private: true,
Keyword: "",
OwnerID: ctx.User.ID,
TopicOnly: false,
Collaborate: util.OptionalBoolNone,
Actor: ctx.User,
OrderBy: models.SearchOrderByRecentUpdated,
})
opts.Page = page
repos, count, err := models.SearchRepositoryByName(opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
return

View File

@ -22,8 +22,8 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migrations"
"code.gitea.io/gitea/modules/notification"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
@ -450,10 +450,10 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
return
}
var gitServiceType = structs.PlainGitService
var gitServiceType = api.PlainGitService
u, err := url.Parse(remoteAddr)
if err == nil && strings.EqualFold(u.Host, "github.com") {
gitServiceType = structs.GithubService
gitServiceType = api.GithubService
}
var opts = migrations.MigrateOptions{
@ -482,7 +482,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
opts.Releases = false
}
repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
repo, err := repo_module.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
OriginalURL: form.CloneAddr,

View File

@ -15,6 +15,7 @@ import (
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/user"
)
@ -252,7 +253,7 @@ func ExploreUsers(ctx *context.Context) {
Type: models.UserTypeIndividual,
PageSize: setting.UI.ExplorePagingNum,
IsActive: util.OptionalBoolTrue,
Private: true,
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
}, tplExploreUsers)
}
@ -263,10 +264,15 @@ func ExploreOrganizations(ctx *context.Context) {
ctx.Data["PageIsExploreOrganizations"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
visibleTypes := []structs.VisibleType{structs.VisibleTypePublic}
if ctx.User != nil {
visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate)
}
RenderUserSearch(ctx, &models.SearchUserOptions{
Type: models.UserTypeOrganization,
PageSize: setting.UI.ExplorePagingNum,
Private: ctx.User != nil,
Visible: visibleTypes,
}, tplExploreOrganizations)
}

View File

@ -361,10 +361,8 @@ func parseBaseRepoInfo(ctx *context.Context, repo *models.Repository) error {
if err := repo.GetBaseRepo(); err != nil {
return err
}
if err := repo.BaseRepo.GetOwnerName(); err != nil {
return err
}
baseGitRepo, err := git.OpenRepository(models.RepoPath(repo.BaseRepo.OwnerName, repo.BaseRepo.Name))
baseGitRepo, err := git.OpenRepository(repo.BaseRepo.RepoPath())
if err != nil {
return err
}

View File

@ -35,7 +35,7 @@ func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) {
return
}
if err := models.InitalizeLabels(ctx.Repo.Repository.ID, form.TemplateName); err != nil {
if err := models.InitalizeLabels(models.DefaultDBContext(), ctx.Repo.Repository.ID, form.TemplateName); err != nil {
if models.IsErrIssueLabelTemplateLoad(err) {
originalErr := err.(models.ErrIssueLabelTemplateLoad).OriginalError
ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, originalErr))

View File

@ -10,7 +10,6 @@ import (
"fmt"
"html/template"
"mime"
"path"
"regexp"
"strings"
texttmpl "text/template"
@ -142,7 +141,7 @@ func SendRegisterNotifyMail(locale Locale, u *models.User) {
// SendCollaboratorMail sends mail notification to new collaborator.
func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
repoName := path.Join(repo.Owner.Name, repo.Name)
repoName := repo.FullName()
subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repoName)
data := map[string]interface{}{

View File

@ -217,7 +217,7 @@ func runSync(m *models.Mirror) ([]*mirrorSyncResult, bool) {
}
gitRepo.Close()
if err := m.Repo.UpdateSize(); err != nil {
if err := m.Repo.UpdateSize(models.DefaultDBContext()); err != nil {
log.Error("Failed to update size for mirror repository: %v", err)
}

View File

@ -38,7 +38,7 @@ func TestRelease_MirrorDelete(t *testing.T) {
Releases: false,
}
mirrorRepo, err := models.CreateRepository(user, user, models.CreateRepoOptions{
mirrorRepo, err := repository.CreateRepository(user, user, models.CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
IsPrivate: opts.Private,

View File

@ -55,8 +55,8 @@ func DownloadDiffOrPatch(pr *models.PullRequest, w io.Writer, patch bool) error
}
pr.MergeBase = strings.TrimSpace(pr.MergeBase)
if err := gitRepo.GetDiffOrPatch(pr.MergeBase, "tracking", w, patch); err != nil {
log.Error("Unable to get patch file from %s to %s in %s/%s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name, err)
return fmt.Errorf("Unable to get patch file from %s to %s in %s/%s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name, err)
log.Error("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
return fmt.Errorf("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
}
return nil
}
@ -108,8 +108,8 @@ func TestPatch(pr *models.PullRequest) error {
if err := gitRepo.GetDiff(pr.MergeBase, "tracking", tmpPatchFile); err != nil {
tmpPatchFile.Close()
log.Error("Unable to get patch file from %s to %s in %s/%s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name, err)
return fmt.Errorf("Unable to get patch file from %s to %s in %s/%s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name, err)
log.Error("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
return fmt.Errorf("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
}
stat, err := tmpPatchFile.Stat()
if err != nil {

View File

@ -256,7 +256,7 @@ func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID st
// Add a temporary remote.
tmpRemote := "checkIfPRContentChanged-" + com.ToStr(time.Now().UnixNano())
if err = headGitRepo.AddRemote(tmpRemote, models.RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name), true); err != nil {
if err = headGitRepo.AddRemote(tmpRemote, pr.BaseRepo.RepoPath(), true); err != nil {
return false, fmt.Errorf("AddRemote: %s/%s-%s: %v", pr.HeadRepo.OwnerName, pr.HeadRepo.Name, tmpRemote, err)
}
defer func() {

View File

@ -8,20 +8,21 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
repo_module "code.gitea.io/gitea/modules/repository"
)
// GenerateRepository generates a repository from a template
func GenerateRepository(doer, owner *models.User, templateRepo *models.Repository, opts models.GenerateRepoOptions) (_ *models.Repository, err error) {
var generateRepo *models.Repository
if err = models.WithTx(func(ctx models.DBContext) error {
generateRepo, err = models.GenerateRepository(ctx, doer, owner, templateRepo, opts)
generateRepo, err = repo_module.GenerateRepository(ctx, doer, owner, templateRepo, opts)
if err != nil {
return err
}
// Git Content
if opts.GitContent && !templateRepo.IsEmpty {
if err = models.GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
if err = repo_module.GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
return err
}
}

View File

@ -10,11 +10,12 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
repo_module "code.gitea.io/gitea/modules/repository"
)
// CreateRepository creates a repository for the user/organization.
func CreateRepository(doer, owner *models.User, opts models.CreateRepoOptions) (*models.Repository, error) {
repo, err := models.CreateRepository(doer, owner, opts)
repo, err := repo_module.CreateRepository(doer, owner, opts)
if err != nil {
if repo != nil {
if errDelete := models.DeleteRepository(doer, owner.ID, repo.ID); errDelete != nil {
@ -31,7 +32,7 @@ func CreateRepository(doer, owner *models.User, opts models.CreateRepoOptions) (
// ForkRepository forks a repository
func ForkRepository(doer, u *models.User, oldRepo *models.Repository, name, desc string) (*models.Repository, error) {
repo, err := models.ForkRepository(doer, u, oldRepo, name, desc)
repo, err := repo_module.ForkRepository(doer, u, oldRepo, name, desc)
if err != nil {
if repo != nil {
if errDelete := models.DeleteRepository(doer, u.ID, repo.ID); errDelete != nil {

View File

@ -606,6 +606,35 @@
}
},
"/orgs": {
"get": {
"produces": [
"application/json"
],
"tags": [
"organization"
],
"summary": "Get list of organizations",
"operationId": "orgGetAll",
"parameters": [
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results, maximum page size is 50",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/OrganizationList"
}
}
},
"post": {
"consumes": [
"application/json"

View File

@ -22,6 +22,9 @@ if (typeof (Dropzone) !== 'undefined') {
Dropzone.autoDiscover = false;
}
// Silence fomantic's error logging when tabs are used without a target content element
$.fn.tab.settings.silent = true;
function initCommentPreviewTab($form) {
const $tabMenu = $form.find('.tabular.menu');
$tabMenu.find('.item').tab();