@ -0,0 +1,140 @@ |
|||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cmd |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"bytes" |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"path/filepath" |
||||||
|
|
||||||
|
"github.com/Unknwon/com" |
||||||
|
"github.com/urfave/cli" |
||||||
|
|
||||||
|
"github.com/gogits/gogs/models" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
CmdHook = cli.Command{ |
||||||
|
Name: "hook", |
||||||
|
Usage: "Delegate commands to corresponding Git hooks", |
||||||
|
Description: "All sub-commands should only be called by Git", |
||||||
|
Flags: []cli.Flag{ |
||||||
|
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"), |
||||||
|
}, |
||||||
|
Subcommands: []cli.Command{ |
||||||
|
subcmdHookPreReceive, |
||||||
|
subcmdHookUpadte, |
||||||
|
subcmdHookPostReceive, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
subcmdHookPreReceive = cli.Command{ |
||||||
|
Name: "pre-receive", |
||||||
|
Usage: "Delegate pre-receive Git hook", |
||||||
|
Description: "This command should only be called by Git", |
||||||
|
Action: runHookPreReceive, |
||||||
|
} |
||||||
|
subcmdHookUpadte = cli.Command{ |
||||||
|
Name: "update", |
||||||
|
Usage: "Delegate update Git hook", |
||||||
|
Description: "This command should only be called by Git", |
||||||
|
Action: runHookUpdate, |
||||||
|
} |
||||||
|
subcmdHookPostReceive = cli.Command{ |
||||||
|
Name: "post-receive", |
||||||
|
Usage: "Delegate post-receive Git hook", |
||||||
|
Description: "This command should only be called by Git", |
||||||
|
Action: runHookPostReceive, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func runHookPreReceive(c *cli.Context) error { |
||||||
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
setup(c, "hooks/pre-receive.log") |
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil) |
||||||
|
scanner := bufio.NewScanner(os.Stdin) |
||||||
|
for scanner.Scan() { |
||||||
|
buf.Write(scanner.Bytes()) |
||||||
|
buf.WriteByte('\n') |
||||||
|
} |
||||||
|
|
||||||
|
customHooksPath := os.Getenv(_ENV_REPO_CUSTOM_HOOKS_PATH) |
||||||
|
if !com.IsFile(customHooksPath) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
hookCmd := exec.Command(filepath.Join(customHooksPath, "pre-receive")) |
||||||
|
hookCmd.Stdout = os.Stdout |
||||||
|
hookCmd.Stdin = buf |
||||||
|
hookCmd.Stderr = os.Stderr |
||||||
|
if err := hookCmd.Run(); err != nil { |
||||||
|
fail("Internal error", "Fail to execute custom pre-receive hook: %v", err) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func runHookUpdate(c *cli.Context) error { |
||||||
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
setup(c, "hooks/update.log") |
||||||
|
|
||||||
|
args := c.Args() |
||||||
|
if len(args) != 3 { |
||||||
|
fail("Arguments received are not equal to three", "Arguments received are not equal to three") |
||||||
|
} else if len(args[0]) == 0 { |
||||||
|
fail("First argument 'refName' is empty", "First argument 'refName' is empty") |
||||||
|
} |
||||||
|
|
||||||
|
uuid := os.Getenv(_ENV_UPDATE_TASK_UUID) |
||||||
|
if err := models.AddUpdateTask(&models.UpdateTask{ |
||||||
|
UUID: uuid, |
||||||
|
RefName: args[0], |
||||||
|
OldCommitID: args[1], |
||||||
|
NewCommitID: args[2], |
||||||
|
}); err != nil { |
||||||
|
fail("Internal error", "Fail to add update task '%s': %v", uuid, err) |
||||||
|
} |
||||||
|
|
||||||
|
customHooksPath := os.Getenv(_ENV_REPO_CUSTOM_HOOKS_PATH) |
||||||
|
if !com.IsFile(customHooksPath) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
hookCmd := exec.Command(filepath.Join(customHooksPath, "update"), args...) |
||||||
|
hookCmd.Stdout = os.Stdout |
||||||
|
hookCmd.Stdin = os.Stdin |
||||||
|
hookCmd.Stderr = os.Stderr |
||||||
|
if err := hookCmd.Run(); err != nil { |
||||||
|
fail("Internal error", "Fail to execute custom pre-receive hook: %v", err) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func runHookPostReceive(c *cli.Context) error { |
||||||
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
setup(c, "hooks/post-receive.log") |
||||||
|
|
||||||
|
customHooksPath := os.Getenv(_ENV_REPO_CUSTOM_HOOKS_PATH) |
||||||
|
if !com.IsFile(customHooksPath) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
hookCmd := exec.Command(filepath.Join(customHooksPath, "post-receive")) |
||||||
|
hookCmd.Stdout = os.Stdout |
||||||
|
hookCmd.Stdin = os.Stdin |
||||||
|
hookCmd.Stderr = os.Stderr |
||||||
|
if err := hookCmd.Run(); err != nil { |
||||||
|
fail("Internal error", "Fail to execute custom post-receive hook: %v", err) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -1,58 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd |
|
||||||
|
|
||||||
import ( |
|
||||||
"os" |
|
||||||
|
|
||||||
"github.com/urfave/cli" |
|
||||||
|
|
||||||
"github.com/gogits/gogs/models" |
|
||||||
"github.com/gogits/gogs/modules/log" |
|
||||||
"github.com/gogits/gogs/modules/setting" |
|
||||||
) |
|
||||||
|
|
||||||
var CmdUpdate = cli.Command{ |
|
||||||
Name: "update", |
|
||||||
Usage: "This command should only be called by Git hook", |
|
||||||
Description: `Update get pushed info and insert into database`, |
|
||||||
Action: runUpdate, |
|
||||||
Flags: []cli.Flag{ |
|
||||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"), |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
func runUpdate(c *cli.Context) error { |
|
||||||
if c.IsSet("config") { |
|
||||||
setting.CustomConf = c.String("config") |
|
||||||
} |
|
||||||
|
|
||||||
setup("update.log") |
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { |
|
||||||
log.GitLogger.Trace("SSH_ORIGINAL_COMMAND is empty") |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
args := c.Args() |
|
||||||
if len(args) != 3 { |
|
||||||
log.GitLogger.Fatal(2, "Arguments received are not equal to three") |
|
||||||
} else if len(args[0]) == 0 { |
|
||||||
log.GitLogger.Fatal(2, "First argument 'refName' is empty, shouldn't use") |
|
||||||
} |
|
||||||
|
|
||||||
task := models.UpdateTask{ |
|
||||||
UUID: os.Getenv("uuid"), |
|
||||||
RefName: args[0], |
|
||||||
OldCommitID: args[1], |
|
||||||
NewCommitID: args[2], |
|
||||||
} |
|
||||||
|
|
||||||
if err := models.AddUpdateTask(&task); err != nil { |
|
||||||
log.GitLogger.Fatal(2, "AddUpdateTask: %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
@ -1,154 +0,0 @@ |
|||||||
hash: 1d5fcf2a90f7621ecbc0b1abed548e11d13bda3fea49b4326c829a523268e5cf |
|
||||||
updated: 2016-06-12T17:35:14.27036884+08:00 |
|
||||||
imports: |
|
||||||
- name: github.com/bradfitz/gomemcache |
|
||||||
version: 2fafb84a66c4911e11a8f50955b01e74fe3ab9c5 |
|
||||||
subpackages: |
|
||||||
- memcache |
|
||||||
- name: github.com/urfave/cli |
|
||||||
version: 347a9884a87374d000eec7e6445a34487c1f4a2b |
|
||||||
- name: github.com/go-macaron/binding |
|
||||||
version: 48920167fa152d02f228cfbece7e0f1e452d200a |
|
||||||
- name: github.com/go-macaron/cache |
|
||||||
version: 56173531277692bc2925924d51fda1cd0a6b8178 |
|
||||||
subpackages: |
|
||||||
- memcache |
|
||||||
- redis |
|
||||||
- name: github.com/go-macaron/captcha |
|
||||||
version: 8aa5919789ab301e865595eb4b1114d6b9847deb |
|
||||||
- name: github.com/go-macaron/csrf |
|
||||||
version: 6a9a7df172cc1fcd81e4585f44b09200b6087cc0 |
|
||||||
- name: github.com/go-macaron/gzip |
|
||||||
version: cad1c6580a07c56f5f6bc52d66002a05985c5854 |
|
||||||
- name: github.com/go-macaron/i18n |
|
||||||
version: ef57533c3b0fc2d8581deda14937e52f11a203ab |
|
||||||
- name: github.com/go-macaron/inject |
|
||||||
version: d8a0b8677191f4380287cfebd08e462217bac7ad |
|
||||||
- name: github.com/go-macaron/session |
|
||||||
version: 66031fcb37a0fff002a1f028eb0b3a815c78306b |
|
||||||
subpackages: |
|
||||||
- redis |
|
||||||
- name: github.com/go-macaron/toolbox |
|
||||||
version: 82b511550b0aefc36b3a28062ad3a52e812bee38 |
|
||||||
- name: github.com/go-sql-driver/mysql |
|
||||||
version: 2e00b5cd70399450106cec6431c2e2ce3cae5034 |
|
||||||
- name: github.com/go-xorm/builder |
|
||||||
version: 867edcc549127f15667ece8876c3a60093c9634b |
|
||||||
- name: github.com/go-xorm/core |
|
||||||
version: 2fbe2c76c6781d9e1c0398fc25386426e611f975 |
|
||||||
- name: github.com/go-xorm/xorm |
|
||||||
version: 445a934d32ed0934aedf0ef99b4b7c69b22f8e58 |
|
||||||
- name: github.com/gogits/chardet |
|
||||||
version: 2404f777256163ea3eadb273dada5dcb037993c0 |
|
||||||
- name: github.com/gogits/cron |
|
||||||
version: 2fc07a4c4f1e3c4d2301c5ed578d5e2c31c70421 |
|
||||||
- name: github.com/gogits/git-module |
|
||||||
version: 172cbc21accbf0085a58fd0832f46a9f694130e8 |
|
||||||
- name: github.com/gogits/go-gogs-client |
|
||||||
version: 98046bb98061fc6baa5bb86359af0b7c300d384a |
|
||||||
- name: github.com/gogits/go-libravatar |
|
||||||
version: cd1abbd55d09b793672732a7a1dfdaa12a40dfd0 |
|
||||||
- name: github.com/issue9/identicon |
|
||||||
version: d36b54562f4cf70c83653e13dc95c220c79ef521 |
|
||||||
- name: github.com/jaytaylor/html2text |
|
||||||
version: d16d4129aab4e6be4497f4db898965f09b93b565 |
|
||||||
- name: github.com/kardianos/minwinsvc |
|
||||||
version: cad6b2b879b0970e4245a20ebf1a81a756e2bb70 |
|
||||||
- name: github.com/klauspost/compress |
|
||||||
version: 461e8fd8397ae84a23f56e385801e4feda2048ce |
|
||||||
subpackages: |
|
||||||
- gzip |
|
||||||
- flate |
|
||||||
- name: github.com/klauspost/cpuid |
|
||||||
version: 09cded8978dc9e80714c4d85b0322337b0a1e5e0 |
|
||||||
- name: github.com/klauspost/crc32 |
|
||||||
version: cb6bfca970f6908083f26f39a79009d608efd5cd |
|
||||||
- name: github.com/lib/pq |
|
||||||
version: 67c3f2a8884c9b1aac5503c8d42ae4f73a93511c |
|
||||||
subpackages: |
|
||||||
- oid |
|
||||||
- name: github.com/mattn/go-sqlite3 |
|
||||||
version: ce9149a3c941c30de51a01dbc5bc414ddaa52927 |
|
||||||
- name: github.com/mcuadros/go-version |
|
||||||
version: 257f7b9a7d87427c8d7f89469a5958d57f8abd7c |
|
||||||
- name: github.com/microcosm-cc/bluemonday |
|
||||||
version: e79763773ab6222ca1d5a7cbd9d62d83c1f77081 |
|
||||||
- name: github.com/msteinert/pam |
|
||||||
version: 02ccfbfaf0cc627aa3aec8ef7ed5cfeec5b43f63 |
|
||||||
- name: github.com/nfnt/resize |
|
||||||
version: 891127d8d1b52734debe1b3c3d7e747502b6c366 |
|
||||||
- name: github.com/russross/blackfriday |
|
||||||
version: 5f33e7b7878355cd2b7e6b8eefc48a5472c69f70 |
|
||||||
- name: github.com/satori/go.uuid |
|
||||||
version: b061729afc07e77a8aa4fad0a2fd840958f1942a |
|
||||||
- name: github.com/sergi/go-diff |
|
||||||
version: 24e2351369ec4949b2ed0dc5c477afdd4c4034e8 |
|
||||||
subpackages: |
|
||||||
- diffmatchpatch |
|
||||||
- name: github.com/shurcooL/sanitized_anchor_name |
|
||||||
version: 1dba4b3954bc059efc3991ec364f9f9a35f597d2 |
|
||||||
- name: github.com/Unknwon/cae |
|
||||||
version: c6aac99ea2cae2ebaf23f26f76b04fe3fcfc9f8c |
|
||||||
subpackages: |
|
||||||
- zip |
|
||||||
- name: github.com/Unknwon/com |
|
||||||
version: 28b053d5a2923b87ce8c5a08f3af779894a72758 |
|
||||||
- name: github.com/Unknwon/i18n |
|
||||||
version: 39d6f2727e0698b1021ceb6a77c1801aa92e7d5d |
|
||||||
- name: github.com/Unknwon/paginater |
|
||||||
version: 701c23f468663c34d1b1768c3ae1bcc57e11c5b3 |
|
||||||
- name: golang.org/x/crypto |
|
||||||
version: dc137beb6cce2043eb6b5f223ab8bf51c32459f4 |
|
||||||
subpackages: |
|
||||||
- ssh |
|
||||||
- curve25519 |
|
||||||
- ed25519 |
|
||||||
- ed25519/internal/edwards25519 |
|
||||||
- name: golang.org/x/net |
|
||||||
version: f2499483f923065a842d38eb4c7f1927e6fc6e6d |
|
||||||
subpackages: |
|
||||||
- html |
|
||||||
- html/charset |
|
||||||
- html/atom |
|
||||||
- name: golang.org/x/sys |
|
||||||
version: d75a52659825e75fff6158388dddc6a5b04f9ba5 |
|
||||||
subpackages: |
|
||||||
- windows/svc |
|
||||||
- windows |
|
||||||
- name: golang.org/x/text |
|
||||||
version: ece019dcfd29abcf65d0d1dfe145e8faad097640 |
|
||||||
subpackages: |
|
||||||
- transform |
|
||||||
- language |
|
||||||
- encoding |
|
||||||
- encoding/charmap |
|
||||||
- encoding/htmlindex |
|
||||||
- internal/tag |
|
||||||
- encoding/internal/identifier |
|
||||||
- encoding/internal |
|
||||||
- encoding/japanese |
|
||||||
- encoding/korean |
|
||||||
- encoding/simplifiedchinese |
|
||||||
- encoding/traditionalchinese |
|
||||||
- encoding/unicode |
|
||||||
- internal/utf8internal |
|
||||||
- runes |
|
||||||
- name: gopkg.in/alexcesaro/quotedprintable.v3 |
|
||||||
version: 2caba252f4dc53eaf6b553000885530023f54623 |
|
||||||
- name: gopkg.in/asn1-ber.v1 |
|
||||||
version: 4e86f4367175e39f69d9358a5f17b4dda270378d |
|
||||||
- name: gopkg.in/bufio.v1 |
|
||||||
version: 567b2bfa514e796916c4747494d6ff5132a1dfce |
|
||||||
- name: gopkg.in/editorconfig/editorconfig-core-go.v1 |
|
||||||
version: a872f05c2e34b37b567401384d202aff11ba06d4 |
|
||||||
- name: gopkg.in/gomail.v2 |
|
||||||
version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1 |
|
||||||
- name: gopkg.in/ini.v1 |
|
||||||
version: e3c2d47c61e5333f9aa2974695dd94396eb69c75 |
|
||||||
- name: gopkg.in/ldap.v2 |
|
||||||
version: 8168ee085ee43257585e50c6441aadf54ecb2c9f |
|
||||||
- name: gopkg.in/macaron.v1 |
|
||||||
version: 78521e4647ad5dcbabd5734d94582ca2373cbad8 |
|
||||||
- name: gopkg.in/redis.v2 |
|
||||||
version: e6179049628164864e6e84e973cfb56335748dea |
|
||||||
devImports: [] |
|
@ -1,59 +0,0 @@ |
|||||||
package: github.com/gogits/gogs |
|
||||||
import: |
|
||||||
- package: github.com/Unknwon/cae |
|
||||||
subpackages: |
|
||||||
- zip |
|
||||||
- package: github.com/Unknwon/com |
|
||||||
- package: github.com/Unknwon/i18n |
|
||||||
- package: github.com/Unknwon/paginater |
|
||||||
- package: github.com/urfave/cli |
|
||||||
- package: github.com/go-macaron/binding |
|
||||||
- package: github.com/go-macaron/cache |
|
||||||
subpackages: |
|
||||||
- memcache |
|
||||||
- redis |
|
||||||
- package: github.com/go-macaron/captcha |
|
||||||
- package: github.com/go-macaron/csrf |
|
||||||
- package: github.com/go-macaron/gzip |
|
||||||
- package: github.com/go-macaron/i18n |
|
||||||
- package: github.com/go-macaron/session |
|
||||||
subpackages: |
|
||||||
- redis |
|
||||||
- package: github.com/go-macaron/toolbox |
|
||||||
- package: github.com/go-sql-driver/mysql |
|
||||||
- package: github.com/go-xorm/core |
|
||||||
- package: github.com/go-xorm/xorm |
|
||||||
- package: github.com/gogits/chardet |
|
||||||
- package: github.com/gogits/cron |
|
||||||
- package: github.com/gogits/git-module |
|
||||||
- package: github.com/gogits/go-gogs-client |
|
||||||
- package: github.com/issue9/identicon |
|
||||||
- package: github.com/kardianos/minwinsvc |
|
||||||
- package: github.com/lib/pq |
|
||||||
- package: github.com/mattn/go-sqlite3 |
|
||||||
- package: github.com/mcuadros/go-version |
|
||||||
- package: github.com/microcosm-cc/bluemonday |
|
||||||
- package: github.com/msteinert/pam |
|
||||||
- package: github.com/nfnt/resize |
|
||||||
- package: github.com/russross/blackfriday |
|
||||||
- package: github.com/satori/go.uuid |
|
||||||
- package: github.com/sergi/go-diff |
|
||||||
subpackages: |
|
||||||
- diffmatchpatch |
|
||||||
- package: github.com/strk/go-libravatar |
|
||||||
- package: golang.org/x/crypto |
|
||||||
subpackages: |
|
||||||
- ssh |
|
||||||
- package: golang.org/x/net |
|
||||||
subpackages: |
|
||||||
- html |
|
||||||
- html/charset |
|
||||||
- package: golang.org/x/text |
|
||||||
subpackages: |
|
||||||
- transform |
|
||||||
- language |
|
||||||
- package: gopkg.in/editorconfig/editorconfig-core-go.v1 |
|
||||||
- package: gopkg.in/gomail.v2 |
|
||||||
- package: gopkg.in/ini.v1 |
|
||||||
- package: gopkg.in/ldap.v2 |
|
||||||
- package: gopkg.in/macaron.v1 |
|
@ -0,0 +1,175 @@ |
|||||||
|
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"mime/multipart" |
||||||
|
"os" |
||||||
|
"path" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-xorm/xorm" |
||||||
|
gouuid "github.com/satori/go.uuid" |
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/setting" |
||||||
|
) |
||||||
|
|
||||||
|
// Attachment represent a attachment of issue/comment/release.
|
||||||
|
type Attachment struct { |
||||||
|
ID int64 `xorm:"pk autoincr"` |
||||||
|
UUID string `xorm:"uuid UNIQUE"` |
||||||
|
IssueID int64 `xorm:"INDEX"` |
||||||
|
CommentID int64 |
||||||
|
ReleaseID int64 `xorm:"INDEX"` |
||||||
|
Name string |
||||||
|
|
||||||
|
Created time.Time `xorm:"-"` |
||||||
|
CreatedUnix int64 |
||||||
|
} |
||||||
|
|
||||||
|
func (a *Attachment) BeforeInsert() { |
||||||
|
a.CreatedUnix = time.Now().Unix() |
||||||
|
} |
||||||
|
|
||||||
|
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) { |
||||||
|
switch colName { |
||||||
|
case "created_unix": |
||||||
|
a.Created = time.Unix(a.CreatedUnix, 0).Local() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
|
||||||
|
func AttachmentLocalPath(uuid string) string { |
||||||
|
return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid) |
||||||
|
} |
||||||
|
|
||||||
|
// LocalPath returns where attachment is stored in local file system.
|
||||||
|
func (attach *Attachment) LocalPath() string { |
||||||
|
return AttachmentLocalPath(attach.UUID) |
||||||
|
} |
||||||
|
|
||||||
|
// NewAttachment creates a new attachment object.
|
||||||
|
func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) { |
||||||
|
attach := &Attachment{ |
||||||
|
UUID: gouuid.NewV4().String(), |
||||||
|
Name: name, |
||||||
|
} |
||||||
|
|
||||||
|
localPath := attach.LocalPath() |
||||||
|
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil { |
||||||
|
return nil, fmt.Errorf("MkdirAll: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
fw, err := os.Create(localPath) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("Create: %v", err) |
||||||
|
} |
||||||
|
defer fw.Close() |
||||||
|
|
||||||
|
if _, err = fw.Write(buf); err != nil { |
||||||
|
return nil, fmt.Errorf("Write: %v", err) |
||||||
|
} else if _, err = io.Copy(fw, file); err != nil { |
||||||
|
return nil, fmt.Errorf("Copy: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
if _, err := x.Insert(attach); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return attach, nil |
||||||
|
} |
||||||
|
|
||||||
|
func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) { |
||||||
|
attach := &Attachment{UUID: uuid} |
||||||
|
has, err := x.Get(attach) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} else if !has { |
||||||
|
return nil, ErrAttachmentNotExist{0, uuid} |
||||||
|
} |
||||||
|
return attach, nil |
||||||
|
} |
||||||
|
|
||||||
|
func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) { |
||||||
|
if len(uuids) == 0 { |
||||||
|
return []*Attachment{}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Silently drop invalid uuids.
|
||||||
|
attachments := make([]*Attachment, 0, len(uuids)) |
||||||
|
return attachments, e.In("uuid", uuids).Find(&attachments) |
||||||
|
} |
||||||
|
|
||||||
|
// GetAttachmentByUUID returns attachment by given UUID.
|
||||||
|
func GetAttachmentByUUID(uuid string) (*Attachment, error) { |
||||||
|
return getAttachmentByUUID(x, uuid) |
||||||
|
} |
||||||
|
|
||||||
|
func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) { |
||||||
|
attachments := make([]*Attachment, 0, 10) |
||||||
|
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments) |
||||||
|
} |
||||||
|
|
||||||
|
// GetAttachmentsByIssueID returns all attachments of an issue.
|
||||||
|
func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) { |
||||||
|
return getAttachmentsByIssueID(x, issueID) |
||||||
|
} |
||||||
|
|
||||||
|
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) { |
||||||
|
attachments := make([]*Attachment, 0, 10) |
||||||
|
return attachments, e.Where("comment_id=?", commentID).Find(&attachments) |
||||||
|
} |
||||||
|
|
||||||
|
// GetAttachmentsByCommentID returns all attachments if comment by given ID.
|
||||||
|
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) { |
||||||
|
return getAttachmentsByCommentID(x, commentID) |
||||||
|
} |
||||||
|
|
||||||
|
// DeleteAttachment deletes the given attachment and optionally the associated file.
|
||||||
|
func DeleteAttachment(a *Attachment, remove bool) error { |
||||||
|
_, err := DeleteAttachments([]*Attachment{a}, remove) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// DeleteAttachments deletes the given attachments and optionally the associated files.
|
||||||
|
func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) { |
||||||
|
for i, a := range attachments { |
||||||
|
if remove { |
||||||
|
if err := os.Remove(a.LocalPath()); err != nil { |
||||||
|
return i, err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if _, err := x.Delete(a); err != nil { |
||||||
|
return i, err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return len(attachments), nil |
||||||
|
} |
||||||
|
|
||||||
|
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
|
||||||
|
func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) { |
||||||
|
attachments, err := GetAttachmentsByIssueID(issueId) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
|
||||||
|
return DeleteAttachments(attachments, remove) |
||||||
|
} |
||||||
|
|
||||||
|
// DeleteAttachmentsByComment deletes all attachments associated with the given comment.
|
||||||
|
func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) { |
||||||
|
attachments, err := GetAttachmentsByCommentID(commentId) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
|
||||||
|
return DeleteAttachments(attachments, remove) |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package migrations |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/Unknwon/com" |
||||||
|
"github.com/go-xorm/xorm" |
||||||
|
log "gopkg.in/clog.v1" |
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/setting" |
||||||
|
) |
||||||
|
|
||||||
|
func generateAndMigrateGitHooks(x *xorm.Engine) (err error) { |
||||||
|
type Repository struct { |
||||||
|
ID int64 |
||||||
|
OwnerID int64 |
||||||
|
Name string |
||||||
|
} |
||||||
|
type User struct { |
||||||
|
ID int64 |
||||||
|
Name string |
||||||
|
} |
||||||
|
var ( |
||||||
|
hookNames = []string{"pre-receive", "update", "post-receive"} |
||||||
|
hookTpls = []string{ |
||||||
|
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf), |
||||||
|
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n", setting.ScriptType, setting.AppPath, setting.CustomConf), |
||||||
|
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf), |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
// Cleanup old update.log and http.log files.
|
||||||
|
filepath.Walk(setting.LogRootPath, func(path string, info os.FileInfo, err error) error { |
||||||
|
if !info.IsDir() && |
||||||
|
(strings.HasPrefix(filepath.Base(path), "update.log") || |
||||||
|
strings.HasPrefix(filepath.Base(path), "http.log")) { |
||||||
|
os.Remove(path) |
||||||
|
} |
||||||
|
return nil |
||||||
|
}) |
||||||
|
|
||||||
|
return x.Where("id > 0").Iterate(new(Repository), |
||||||
|
func(idx int, bean interface{}) error { |
||||||
|
repo := bean.(*Repository) |
||||||
|
if repo.Name == "." || repo.Name == ".." { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
user := new(User) |
||||||
|
has, err := x.Where("id = ?", repo.OwnerID).Get(user) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("query owner of repository [repo_id: %d, owner_id: %d]: %v", repo.ID, repo.OwnerID, err) |
||||||
|
} else if !has { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git" |
||||||
|
log.Trace("[%04d]: %s", idx, repoPath) |
||||||
|
|
||||||
|
hookDir := filepath.Join(repoPath, "hooks") |
||||||
|
customHookDir := filepath.Join(repoPath, "custom_hooks") |
||||||
|
|
||||||
|
for i, hookName := range hookNames { |
||||||
|
oldHookPath := filepath.Join(hookDir, hookName) |
||||||
|
newHookPath := filepath.Join(customHookDir, hookName) |
||||||
|
|
||||||
|
// Gogs didn't allow user to set custom update hook thus no migration for it.
|
||||||
|
// In case user runs this migration multiple times, and custom hook exists,
|
||||||
|
// we assume it's been migrated already.
|
||||||
|
if hookName != "update" && com.IsFile(oldHookPath) && !com.IsExist(customHookDir) { |
||||||
|
os.MkdirAll(customHookDir, os.ModePerm) |
||||||
|
if err = os.Rename(oldHookPath, newHookPath); err != nil { |
||||||
|
return fmt.Errorf("move hook file to custom directory '%s' -> '%s': %v", oldHookPath, newHookPath, err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if err = ioutil.WriteFile(oldHookPath, []byte(hookTpls[i]), 0777); err != nil { |
||||||
|
return fmt.Errorf("write hook file '%s': %v", oldHookPath, err) |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,349 @@ |
|||||||
|
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models |
||||||
|
|
||||||
|
import ( |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-xorm/xorm" |
||||||
|
|
||||||
|
api "github.com/gogits/go-gogs-client" |
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/setting" |
||||||
|
) |
||||||
|
|
||||||
|
// Milestone represents a milestone of repository.
|
||||||
|
type Milestone struct { |
||||||
|
ID int64 `xorm:"pk autoincr"` |
||||||
|
RepoID int64 `xorm:"INDEX"` |
||||||
|
Name string |
||||||
|
Content string `xorm:"TEXT"` |
||||||
|
RenderedContent string `xorm:"-"` |
||||||
|
IsClosed bool |
||||||
|
NumIssues int |
||||||
|
NumClosedIssues int |
||||||
|
NumOpenIssues int `xorm:"-"` |
||||||
|
Completeness int // Percentage(1-100).
|
||||||
|
IsOverDue bool `xorm:"-"` |
||||||
|
|
||||||
|
DeadlineString string `xorm:"-"` |
||||||
|
Deadline time.Time `xorm:"-"` |
||||||
|
DeadlineUnix int64 |
||||||
|
ClosedDate time.Time `xorm:"-"` |
||||||
|
ClosedDateUnix int64 |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Milestone) BeforeInsert() { |
||||||
|
m.DeadlineUnix = m.Deadline.Unix() |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Milestone) BeforeUpdate() { |
||||||
|
if m.NumIssues > 0 { |
||||||
|
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues |
||||||
|
} else { |
||||||
|
m.Completeness = 0 |
||||||
|
} |
||||||
|
|
||||||
|
m.DeadlineUnix = m.Deadline.Unix() |
||||||
|
m.ClosedDateUnix = m.ClosedDate.Unix() |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) { |
||||||
|
switch colName { |
||||||
|
case "num_closed_issues": |
||||||
|
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues |
||||||
|
|
||||||
|
case "deadline_unix": |
||||||
|
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local() |
||||||
|
if m.Deadline.Year() == 9999 { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
m.DeadlineString = m.Deadline.Format("2006-01-02") |
||||||
|
if time.Now().Local().After(m.Deadline) { |
||||||
|
m.IsOverDue = true |
||||||
|
} |
||||||
|
|
||||||
|
case "closed_date_unix": |
||||||
|
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// State returns string representation of milestone status.
|
||||||
|
func (m *Milestone) State() api.StateType { |
||||||
|
if m.IsClosed { |
||||||
|
return api.STATE_CLOSED |
||||||
|
} |
||||||
|
return api.STATE_OPEN |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Milestone) ChangeStatus(isClosed bool) error { |
||||||
|
return ChangeMilestoneStatus(m, isClosed) |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Milestone) APIFormat() *api.Milestone { |
||||||
|
apiMilestone := &api.Milestone{ |
||||||
|
ID: m.ID, |
||||||
|
State: m.State(), |
||||||
|
Title: m.Name, |
||||||
|
Description: m.Content, |
||||||
|
OpenIssues: m.NumOpenIssues, |
||||||
|
ClosedIssues: m.NumClosedIssues, |
||||||
|
} |
||||||
|
if m.IsClosed { |
||||||
|
apiMilestone.Closed = &m.ClosedDate |
||||||
|
} |
||||||
|
if m.Deadline.Year() < 9999 { |
||||||
|
apiMilestone.Deadline = &m.Deadline |
||||||
|
} |
||||||
|
return apiMilestone |
||||||
|
} |
||||||
|
|
||||||
|
// NewMilestone creates new milestone of repository.
|
||||||
|
func NewMilestone(m *Milestone) (err error) { |
||||||
|
sess := x.NewSession() |
||||||
|
defer sessionRelease(sess) |
||||||
|
if err = sess.Begin(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if _, err = sess.Insert(m); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if _, err = sess.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return sess.Commit() |
||||||
|
} |
||||||
|
|
||||||
|
func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) { |
||||||
|
m := &Milestone{ |
||||||
|
ID: id, |
||||||
|
RepoID: repoID, |
||||||
|
} |
||||||
|
has, err := e.Get(m) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} else if !has { |
||||||
|
return nil, ErrMilestoneNotExist{id, repoID} |
||||||
|
} |
||||||
|
return m, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetWebhookByRepoID returns the milestone in a repository.
|
||||||
|
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) { |
||||||
|
return getMilestoneByRepoID(x, repoID, id) |
||||||
|
} |
||||||
|
|
||||||
|
// GetMilestonesByRepoID returns all milestones of a repository.
|
||||||
|
func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) { |
||||||
|
miles := make([]*Milestone, 0, 10) |
||||||
|
return miles, x.Where("repo_id = ?", repoID).Find(&miles) |
||||||
|
} |
||||||
|
|
||||||
|
// GetMilestones returns a list of milestones of given repository and status.
|
||||||
|
func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) { |
||||||
|
miles := make([]*Milestone, 0, setting.UI.IssuePagingNum) |
||||||
|
sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed) |
||||||
|
if page > 0 { |
||||||
|
sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum) |
||||||
|
} |
||||||
|
return miles, sess.Find(&miles) |
||||||
|
} |
||||||
|
|
||||||
|
func updateMilestone(e Engine, m *Milestone) error { |
||||||
|
_, err := e.Id(m.ID).AllCols().Update(m) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// UpdateMilestone updates information of given milestone.
|
||||||
|
func UpdateMilestone(m *Milestone) error { |
||||||
|
return updateMilestone(x, m) |
||||||
|
} |
||||||
|
|
||||||
|
func countRepoMilestones(e Engine, repoID int64) int64 { |
||||||
|
count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone)) |
||||||
|
return count |
||||||
|
} |
||||||
|
|
||||||
|
// CountRepoMilestones returns number of milestones in given repository.
|
||||||
|
func CountRepoMilestones(repoID int64) int64 { |
||||||
|
return countRepoMilestones(x, repoID) |
||||||
|
} |
||||||
|
|
||||||
|
func countRepoClosedMilestones(e Engine, repoID int64) int64 { |
||||||
|
closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone)) |
||||||
|
return closed |
||||||
|
} |
||||||
|
|
||||||
|
// CountRepoClosedMilestones returns number of closed milestones in given repository.
|
||||||
|
func CountRepoClosedMilestones(repoID int64) int64 { |
||||||
|
return countRepoClosedMilestones(x, repoID) |
||||||
|
} |
||||||
|
|
||||||
|
// MilestoneStats returns number of open and closed milestones of given repository.
|
||||||
|
func MilestoneStats(repoID int64) (open int64, closed int64) { |
||||||
|
open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone)) |
||||||
|
return open, CountRepoClosedMilestones(repoID) |
||||||
|
} |
||||||
|
|
||||||
|
// ChangeMilestoneStatus changes the milestone open/closed status.
|
||||||
|
// If milestone passes with changed values, those values will be
|
||||||
|
// updated to database as well.
|
||||||
|
func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) { |
||||||
|
repo, err := GetRepositoryByID(m.RepoID) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
sess := x.NewSession() |
||||||
|
defer sessionRelease(sess) |
||||||
|
if err = sess.Begin(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
m.IsClosed = isClosed |
||||||
|
if err = updateMilestone(sess, m); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID)) |
||||||
|
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID)) |
||||||
|
if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return sess.Commit() |
||||||
|
} |
||||||
|
|
||||||
|
func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error { |
||||||
|
if issue.MilestoneID == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if issue.IsClosed { |
||||||
|
m.NumOpenIssues-- |
||||||
|
m.NumClosedIssues++ |
||||||
|
} else { |
||||||
|
m.NumOpenIssues++ |
||||||
|
m.NumClosedIssues-- |
||||||
|
} |
||||||
|
|
||||||
|
return updateMilestone(e, m) |
||||||
|
} |
||||||
|
|
||||||
|
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress
|
||||||
|
// for the milestone associated with the given issue.
|
||||||
|
func ChangeMilestoneIssueStats(issue *Issue) (err error) { |
||||||
|
sess := x.NewSession() |
||||||
|
defer sessionRelease(sess) |
||||||
|
if err = sess.Begin(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if err = changeMilestoneIssueStats(sess, issue); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
return sess.Commit() |
||||||
|
} |
||||||
|
|
||||||
|
func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error { |
||||||
|
if oldMilestoneID > 0 { |
||||||
|
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
m.NumIssues-- |
||||||
|
if issue.IsClosed { |
||||||
|
m.NumClosedIssues-- |
||||||
|
} |
||||||
|
|
||||||
|
if err = updateMilestone(e, m); err != nil { |
||||||
|
return err |
||||||
|
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if issue.MilestoneID > 0 { |
||||||
|
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
m.NumIssues++ |
||||||
|
if issue.IsClosed { |
||||||
|
m.NumClosedIssues++ |
||||||
|
} |
||||||
|
|
||||||
|
if err = updateMilestone(e, m); err != nil { |
||||||
|
return err |
||||||
|
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return updateIssue(e, issue) |
||||||
|
} |
||||||
|
|
||||||
|
// ChangeMilestoneAssign changes assignment of milestone for issue.
|
||||||
|
func ChangeMilestoneAssign(issue *Issue, oldMilestoneID int64) (err error) { |
||||||
|
sess := x.NewSession() |
||||||
|
defer sess.Close() |
||||||
|
if err = sess.Begin(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return sess.Commit() |
||||||
|
} |
||||||
|
|
||||||
|
// DeleteMilestoneOfRepoByID deletes a milestone from a repository.
|
||||||
|
func DeleteMilestoneOfRepoByID(repoID, id int64) error { |
||||||
|
m, err := GetMilestoneByRepoID(repoID, id) |
||||||
|
if err != nil { |
||||||
|
if IsErrMilestoneNotExist(err) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
repo, err := GetRepositoryByID(m.RepoID) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
sess := x.NewSession() |
||||||
|
defer sessionRelease(sess) |
||||||
|
if err = sess.Begin(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if _, err = sess.Id(m.ID).Delete(new(Milestone)); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID)) |
||||||
|
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID)) |
||||||
|
if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil { |
||||||
|
return err |
||||||
|
} else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return sess.Commit() |
||||||
|
} |
@ -1,18 +0,0 @@ |
|||||||
// +build tidb go1.4.2
|
|
||||||
|
|
||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package models |
|
||||||
|
|
||||||
import ( |
|
||||||
_ "github.com/go-xorm/tidb" |
|
||||||
"github.com/ngaut/log" |
|
||||||
_ "github.com/pingcap/tidb" |
|
||||||
) |
|
||||||
|
|
||||||
func init() { |
|
||||||
EnableTiDB = true |
|
||||||
log.SetLevelByString("error") |
|
||||||
} |
|
@ -1,104 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package log |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"io" |
|
||||||
"log" |
|
||||||
"net" |
|
||||||
) |
|
||||||
|
|
||||||
// ConnWriter implements LoggerInterface.
|
|
||||||
// it writes messages in keep-live tcp connection.
|
|
||||||
type ConnWriter struct { |
|
||||||
lg *log.Logger |
|
||||||
innerWriter io.WriteCloser |
|
||||||
ReconnectOnMsg bool `json:"reconnectOnMsg"` |
|
||||||
Reconnect bool `json:"reconnect"` |
|
||||||
Net string `json:"net"` |
|
||||||
Addr string `json:"addr"` |
|
||||||
Level int `json:"level"` |
|
||||||
} |
|
||||||
|
|
||||||
// create new ConnWrite returning as LoggerInterface.
|
|
||||||
func NewConn() LoggerInterface { |
|
||||||
conn := new(ConnWriter) |
|
||||||
conn.Level = TRACE |
|
||||||
return conn |
|
||||||
} |
|
||||||
|
|
||||||
// init connection writer with json config.
|
|
||||||
// json config only need key "level".
|
|
||||||
func (cw *ConnWriter) Init(jsonconfig string) error { |
|
||||||
return json.Unmarshal([]byte(jsonconfig), cw) |
|
||||||
} |
|
||||||
|
|
||||||
// write message in connection.
|
|
||||||
// if connection is down, try to re-connect.
|
|
||||||
func (cw *ConnWriter) WriteMsg(msg string, skip, level int) error { |
|
||||||
if cw.Level > level { |
|
||||||
return nil |
|
||||||
} |
|
||||||
if cw.neddedConnectOnMsg() { |
|
||||||
if err := cw.connect(); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if cw.ReconnectOnMsg { |
|
||||||
defer cw.innerWriter.Close() |
|
||||||
} |
|
||||||
cw.lg.Println(msg) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (_ *ConnWriter) Flush() { |
|
||||||
} |
|
||||||
|
|
||||||
// destroy connection writer and close tcp listener.
|
|
||||||
func (cw *ConnWriter) Destroy() { |
|
||||||
if cw.innerWriter == nil { |
|
||||||
return |
|
||||||
} |
|
||||||
cw.innerWriter.Close() |
|
||||||
} |
|
||||||
|
|
||||||
func (cw *ConnWriter) connect() error { |
|
||||||
if cw.innerWriter != nil { |
|
||||||
cw.innerWriter.Close() |
|
||||||
cw.innerWriter = nil |
|
||||||
} |
|
||||||
|
|
||||||
conn, err := net.Dial(cw.Net, cw.Addr) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
if tcpConn, ok := conn.(*net.TCPConn); ok { |
|
||||||
tcpConn.SetKeepAlive(true) |
|
||||||
} |
|
||||||
|
|
||||||
cw.innerWriter = conn |
|
||||||
cw.lg = log.New(conn, "", log.Ldate|log.Ltime) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (cw *ConnWriter) neddedConnectOnMsg() bool { |
|
||||||
if cw.Reconnect { |
|
||||||
cw.Reconnect = false |
|
||||||
return true |
|
||||||
} |
|
||||||
|
|
||||||
if cw.innerWriter == nil { |
|
||||||
return true |
|
||||||
} |
|
||||||
|
|
||||||
return cw.ReconnectOnMsg |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
Register("conn", NewConn) |
|
||||||
} |
|
@ -1,73 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package log |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"log" |
|
||||||
"os" |
|
||||||
"runtime" |
|
||||||
) |
|
||||||
|
|
||||||
type Brush func(string) string |
|
||||||
|
|
||||||
func NewBrush(color string) Brush { |
|
||||||
pre := "\033[" |
|
||||||
reset := "\033[0m" |
|
||||||
return func(text string) string { |
|
||||||
return pre + color + "m" + text + reset |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
var colors = []Brush{ |
|
||||||
NewBrush("1;36"), // Trace cyan
|
|
||||||
NewBrush("1;34"), // Debug blue
|
|
||||||
NewBrush("1;32"), // Info green
|
|
||||||
NewBrush("1;33"), // Warn yellow
|
|
||||||
NewBrush("1;31"), // Error red
|
|
||||||
NewBrush("1;35"), // Critical purple
|
|
||||||
NewBrush("1;31"), // Fatal red
|
|
||||||
} |
|
||||||
|
|
||||||
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
|
|
||||||
type ConsoleWriter struct { |
|
||||||
lg *log.Logger |
|
||||||
Level int `json:"level"` |
|
||||||
} |
|
||||||
|
|
||||||
// create ConsoleWriter returning as LoggerInterface.
|
|
||||||
func NewConsole() LoggerInterface { |
|
||||||
return &ConsoleWriter{ |
|
||||||
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime), |
|
||||||
Level: TRACE, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (cw *ConsoleWriter) Init(config string) error { |
|
||||||
return json.Unmarshal([]byte(config), cw) |
|
||||||
} |
|
||||||
|
|
||||||
func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error { |
|
||||||
if cw.Level > level { |
|
||||||
return nil |
|
||||||
} |
|
||||||
if runtime.GOOS == "windows" { |
|
||||||
cw.lg.Println(msg) |
|
||||||
} else { |
|
||||||
cw.lg.Println(colors[level](msg)) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (_ *ConsoleWriter) Flush() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
func (_ *ConsoleWriter) Destroy() { |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
Register("console", NewConsole) |
|
||||||
} |
|
@ -1,243 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package log |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"io/ioutil" |
|
||||||
"log" |
|
||||||
"os" |
|
||||||
"path/filepath" |
|
||||||
"strings" |
|
||||||
"sync" |
|
||||||
"time" |
|
||||||
) |
|
||||||
|
|
||||||
// FileLogWriter implements LoggerInterface.
|
|
||||||
// It writes messages by lines limit, file size limit, or time frequency.
|
|
||||||
type FileLogWriter struct { |
|
||||||
*log.Logger |
|
||||||
mw *MuxWriter |
|
||||||
// The opened file
|
|
||||||
Filename string `json:"filename"` |
|
||||||
|
|
||||||
Maxlines int `json:"maxlines"` |
|
||||||
maxlines_curlines int |
|
||||||
|
|
||||||
// Rotate at size
|
|
||||||
Maxsize int `json:"maxsize"` |
|
||||||
maxsize_cursize int |
|
||||||
|
|
||||||
// Rotate daily
|
|
||||||
Daily bool `json:"daily"` |
|
||||||
Maxdays int64 `json:"maxdays"` |
|
||||||
daily_opendate int |
|
||||||
|
|
||||||
Rotate bool `json:"rotate"` |
|
||||||
|
|
||||||
startLock sync.Mutex // Only one log can write to the file
|
|
||||||
|
|
||||||
Level int `json:"level"` |
|
||||||
} |
|
||||||
|
|
||||||
// an *os.File writer with locker.
|
|
||||||
type MuxWriter struct { |
|
||||||
sync.Mutex |
|
||||||
fd *os.File |
|
||||||
} |
|
||||||
|
|
||||||
// write to os.File.
|
|
||||||
func (l *MuxWriter) Write(b []byte) (int, error) { |
|
||||||
l.Lock() |
|
||||||
defer l.Unlock() |
|
||||||
return l.fd.Write(b) |
|
||||||
} |
|
||||||
|
|
||||||
// set os.File in writer.
|
|
||||||
func (l *MuxWriter) SetFd(fd *os.File) { |
|
||||||
if l.fd != nil { |
|
||||||
l.fd.Close() |
|
||||||
} |
|
||||||
l.fd = fd |
|
||||||
} |
|
||||||
|
|
||||||
// create a FileLogWriter returning as LoggerInterface.
|
|
||||||
func NewFileWriter() LoggerInterface { |
|
||||||
w := &FileLogWriter{ |
|
||||||
Filename: "", |
|
||||||
Maxlines: 1000000, |
|
||||||
Maxsize: 1 << 28, //256 MB
|
|
||||||
Daily: true, |
|
||||||
Maxdays: 7, |
|
||||||
Rotate: true, |
|
||||||
Level: TRACE, |
|
||||||
} |
|
||||||
// use MuxWriter instead direct use os.File for lock write when rotate
|
|
||||||
w.mw = new(MuxWriter) |
|
||||||
// set MuxWriter as Logger's io.Writer
|
|
||||||
w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) |
|
||||||
return w |
|
||||||
} |
|
||||||
|
|
||||||
// Init file logger with json config.
|
|
||||||
// config like:
|
|
||||||
// {
|
|
||||||
// "filename":"log/gogs.log",
|
|
||||||
// "maxlines":10000,
|
|
||||||
// "maxsize":1<<30,
|
|
||||||
// "daily":true,
|
|
||||||
// "maxdays":15,
|
|
||||||
// "rotate":true
|
|
||||||
// }
|
|
||||||
func (w *FileLogWriter) Init(config string) error { |
|
||||||
if err := json.Unmarshal([]byte(config), w); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
if len(w.Filename) == 0 { |
|
||||||
return errors.New("config must have filename") |
|
||||||
} |
|
||||||
return w.StartLogger() |
|
||||||
} |
|
||||||
|
|
||||||
// start file logger. create log file and set to locker-inside file writer.
|
|
||||||
func (w *FileLogWriter) StartLogger() error { |
|
||||||
fd, err := w.createLogFile() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
w.mw.SetFd(fd) |
|
||||||
if err = w.initFd(); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (w *FileLogWriter) docheck(size int) { |
|
||||||
w.startLock.Lock() |
|
||||||
defer w.startLock.Unlock() |
|
||||||
if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) || |
|
||||||
(w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) || |
|
||||||
(w.Daily && time.Now().Day() != w.daily_opendate)) { |
|
||||||
if err := w.DoRotate(); err != nil { |
|
||||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) |
|
||||||
return |
|
||||||
} |
|
||||||
} |
|
||||||
w.maxlines_curlines++ |
|
||||||
w.maxsize_cursize += size |
|
||||||
} |
|
||||||
|
|
||||||
// write logger message into file.
|
|
||||||
func (w *FileLogWriter) WriteMsg(msg string, skip, level int) error { |
|
||||||
if level < w.Level { |
|
||||||
return nil |
|
||||||
} |
|
||||||
n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
|
|
||||||
w.docheck(n) |
|
||||||
w.Logger.Println(msg) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (w *FileLogWriter) createLogFile() (*os.File, error) { |
|
||||||
// Open the log file
|
|
||||||
return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) |
|
||||||
} |
|
||||||
|
|
||||||
func (w *FileLogWriter) initFd() error { |
|
||||||
fd := w.mw.fd |
|
||||||
finfo, err := fd.Stat() |
|
||||||
if err != nil { |
|
||||||
return fmt.Errorf("get stat: %s\n", err) |
|
||||||
} |
|
||||||
w.maxsize_cursize = int(finfo.Size()) |
|
||||||
w.daily_opendate = time.Now().Day() |
|
||||||
if finfo.Size() > 0 { |
|
||||||
content, err := ioutil.ReadFile(w.Filename) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
w.maxlines_curlines = len(strings.Split(string(content), "\n")) |
|
||||||
} else { |
|
||||||
w.maxlines_curlines = 0 |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// DoRotate means it need to write file in new file.
|
|
||||||
// new file name like xx.log.2013-01-01.2
|
|
||||||
func (w *FileLogWriter) DoRotate() error { |
|
||||||
_, err := os.Lstat(w.Filename) |
|
||||||
if err == nil { // file exists
|
|
||||||
// Find the next available number
|
|
||||||
num := 1 |
|
||||||
fname := "" |
|
||||||
for ; err == nil && num <= 999; num++ { |
|
||||||
fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) |
|
||||||
_, err = os.Lstat(fname) |
|
||||||
} |
|
||||||
// return error if the last file checked still existed
|
|
||||||
if err == nil { |
|
||||||
return fmt.Errorf("rotate: cannot find free log number to rename %s\n", w.Filename) |
|
||||||
} |
|
||||||
|
|
||||||
// block Logger's io.Writer
|
|
||||||
w.mw.Lock() |
|
||||||
defer w.mw.Unlock() |
|
||||||
|
|
||||||
fd := w.mw.fd |
|
||||||
fd.Close() |
|
||||||
|
|
||||||
// close fd before rename
|
|
||||||
// Rename the file to its newfound home
|
|
||||||
if err = os.Rename(w.Filename, fname); err != nil { |
|
||||||
return fmt.Errorf("Rotate: %s\n", err) |
|
||||||
} |
|
||||||
|
|
||||||
// re-start logger
|
|
||||||
if err = w.StartLogger(); err != nil { |
|
||||||
return fmt.Errorf("Rotate StartLogger: %s\n", err) |
|
||||||
} |
|
||||||
|
|
||||||
go w.deleteOldLog() |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (w *FileLogWriter) deleteOldLog() { |
|
||||||
dir := filepath.Dir(w.Filename) |
|
||||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) { |
|
||||||
defer func() { |
|
||||||
if r := recover(); r != nil { |
|
||||||
returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r) |
|
||||||
} |
|
||||||
}() |
|
||||||
|
|
||||||
if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) { |
|
||||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) { |
|
||||||
os.Remove(path) |
|
||||||
} |
|
||||||
} |
|
||||||
return returnErr |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
// destroy file logger, close file writer.
|
|
||||||
func (w *FileLogWriter) Destroy() { |
|
||||||
w.mw.fd.Close() |
|
||||||
} |
|
||||||
|
|
||||||
// flush file logger.
|
|
||||||
// there are no buffering messages in file logger in memory.
|
|
||||||
// flush file means sync file from disk.
|
|
||||||
func (w *FileLogWriter) Flush() { |
|
||||||
w.mw.fd.Sync() |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
Register("file", NewFileWriter) |
|
||||||
} |
|
@ -1,312 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package log |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"os" |
|
||||||
"path" |
|
||||||
"path/filepath" |
|
||||||
"runtime" |
|
||||||
"strings" |
|
||||||
"sync" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
loggers []*Logger |
|
||||||
GitLogger *Logger |
|
||||||
) |
|
||||||
|
|
||||||
func NewLogger(bufLen int64, mode, config string) { |
|
||||||
logger := newLogger(bufLen) |
|
||||||
|
|
||||||
isExist := false |
|
||||||
for i, l := range loggers { |
|
||||||
if l.adapter == mode { |
|
||||||
isExist = true |
|
||||||
loggers[i] = logger |
|
||||||
} |
|
||||||
} |
|
||||||
if !isExist { |
|
||||||
loggers = append(loggers, logger) |
|
||||||
} |
|
||||||
if err := logger.SetLogger(mode, config); err != nil { |
|
||||||
Fatal(2, "Fail to set logger (%s): %v", mode, err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// FIXME: use same log level as other loggers.
|
|
||||||
func NewGitLogger(logPath string) { |
|
||||||
os.MkdirAll(path.Dir(logPath), os.ModePerm) |
|
||||||
GitLogger = newLogger(0) |
|
||||||
GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath)) |
|
||||||
} |
|
||||||
|
|
||||||
func Trace(format string, v ...interface{}) { |
|
||||||
for _, logger := range loggers { |
|
||||||
logger.Trace(format, v...) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func Debug(format string, v ...interface{}) { |
|
||||||
for _, logger := range loggers { |
|
||||||
logger.Debug(format, v...) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func Info(format string, v ...interface{}) { |
|
||||||
for _, logger := range loggers { |
|
||||||
logger.Info(format, v...) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func Warn(format string, v ...interface{}) { |
|
||||||
for _, logger := range loggers { |
|
||||||
logger.Warn(format, v...) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func Error(skip int, format string, v ...interface{}) { |
|
||||||
for _, logger := range loggers { |
|
||||||
logger.Error(skip, format, v...) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func Critical(skip int, format string, v ...interface{}) { |
|
||||||
for _, logger := range loggers { |
|
||||||
logger.Critical(skip, format, v...) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func Fatal(skip int, format string, v ...interface{}) { |
|
||||||
Error(skip, format, v...) |
|
||||||
for _, l := range loggers { |
|
||||||
l.Close() |
|
||||||
} |
|
||||||
os.Exit(1) |
|
||||||
} |
|
||||||
|
|
||||||
func Close() { |
|
||||||
for _, l := range loggers { |
|
||||||
l.Close() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// .___ __ _____
|
|
||||||
// | | _____/ |_ ____________/ ____\____ ____ ____
|
|
||||||
// | |/ \ __\/ __ \_ __ \ __\\__ \ _/ ___\/ __ \
|
|
||||||
// | | | \ | \ ___/| | \/| | / __ \\ \__\ ___/
|
|
||||||
// |___|___| /__| \___ >__| |__| (____ /\___ >___ >
|
|
||||||
// \/ \/ \/ \/ \/
|
|
||||||
|
|
||||||
type LogLevel int |
|
||||||
|
|
||||||
const ( |
|
||||||
TRACE = iota |
|
||||||
DEBUG |
|
||||||
INFO |
|
||||||
WARN |
|
||||||
ERROR |
|
||||||
CRITICAL |
|
||||||
FATAL |
|
||||||
) |
|
||||||
|
|
||||||
// LoggerInterface represents behaviors of a logger provider.
|
|
||||||
type LoggerInterface interface { |
|
||||||
Init(config string) error |
|
||||||
WriteMsg(msg string, skip, level int) error |
|
||||||
Destroy() |
|
||||||
Flush() |
|
||||||
} |
|
||||||
|
|
||||||
type loggerType func() LoggerInterface |
|
||||||
|
|
||||||
var adapters = make(map[string]loggerType) |
|
||||||
|
|
||||||
// Register registers given logger provider to adapters.
|
|
||||||
func Register(name string, log loggerType) { |
|
||||||
if log == nil { |
|
||||||
panic("log: register provider is nil") |
|
||||||
} |
|
||||||
if _, dup := adapters[name]; dup { |
|
||||||
panic("log: register called twice for provider \"" + name + "\"") |
|
||||||
} |
|
||||||
adapters[name] = log |
|
||||||
} |
|
||||||
|
|
||||||
type logMsg struct { |
|
||||||
skip, level int |
|
||||||
msg string |
|
||||||
} |
|
||||||
|
|
||||||
// Logger is default logger in beego application.
|
|
||||||
// it can contain several providers and log message into all providers.
|
|
||||||
type Logger struct { |
|
||||||
adapter string |
|
||||||
lock sync.Mutex |
|
||||||
level int |
|
||||||
msg chan *logMsg |
|
||||||
outputs map[string]LoggerInterface |
|
||||||
quit chan bool |
|
||||||
} |
|
||||||
|
|
||||||
// newLogger initializes and returns a new logger.
|
|
||||||
func newLogger(buffer int64) *Logger { |
|
||||||
l := &Logger{ |
|
||||||
msg: make(chan *logMsg, buffer), |
|
||||||
outputs: make(map[string]LoggerInterface), |
|
||||||
quit: make(chan bool), |
|
||||||
} |
|
||||||
go l.StartLogger() |
|
||||||
return l |
|
||||||
} |
|
||||||
|
|
||||||
// SetLogger sets new logger instance with given logger adapter and config.
|
|
||||||
func (l *Logger) SetLogger(adapter string, config string) error { |
|
||||||
l.lock.Lock() |
|
||||||
defer l.lock.Unlock() |
|
||||||
if log, ok := adapters[adapter]; ok { |
|
||||||
lg := log() |
|
||||||
if err := lg.Init(config); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
l.outputs[adapter] = lg |
|
||||||
l.adapter = adapter |
|
||||||
} else { |
|
||||||
panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)") |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// DelLogger removes a logger adapter instance.
|
|
||||||
func (l *Logger) DelLogger(adapter string) error { |
|
||||||
l.lock.Lock() |
|
||||||
defer l.lock.Unlock() |
|
||||||
if lg, ok := l.outputs[adapter]; ok { |
|
||||||
lg.Destroy() |
|
||||||
delete(l.outputs, adapter) |
|
||||||
} else { |
|
||||||
panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)") |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (l *Logger) writerMsg(skip, level int, msg string) error { |
|
||||||
if l.level > level { |
|
||||||
return nil |
|
||||||
} |
|
||||||
lm := &logMsg{ |
|
||||||
skip: skip, |
|
||||||
level: level, |
|
||||||
} |
|
||||||
|
|
||||||
// Only error information needs locate position for debugging.
|
|
||||||
if lm.level >= ERROR { |
|
||||||
pc, file, line, ok := runtime.Caller(skip) |
|
||||||
if ok { |
|
||||||
// Get caller function name.
|
|
||||||
fn := runtime.FuncForPC(pc) |
|
||||||
var fnName string |
|
||||||
if fn == nil { |
|
||||||
fnName = "?()" |
|
||||||
} else { |
|
||||||
fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()" |
|
||||||
} |
|
||||||
|
|
||||||
fileName := file |
|
||||||
if len(fileName) > 20 { |
|
||||||
fileName = "..." + fileName[len(fileName)-20:] |
|
||||||
} |
|
||||||
lm.msg = fmt.Sprintf("[%s:%d %s] %s", fileName, line, fnName, msg) |
|
||||||
} else { |
|
||||||
lm.msg = msg |
|
||||||
} |
|
||||||
} else { |
|
||||||
lm.msg = msg |
|
||||||
} |
|
||||||
l.msg <- lm |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// StartLogger starts logger chan reading.
|
|
||||||
func (l *Logger) StartLogger() { |
|
||||||
for { |
|
||||||
select { |
|
||||||
case bm := <-l.msg: |
|
||||||
for _, l := range l.outputs { |
|
||||||
if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil { |
|
||||||
fmt.Println("ERROR, unable to WriteMsg:", err) |
|
||||||
} |
|
||||||
} |
|
||||||
case <-l.quit: |
|
||||||
return |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Flush flushs all chan data.
|
|
||||||
func (l *Logger) Flush() { |
|
||||||
for _, l := range l.outputs { |
|
||||||
l.Flush() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Close closes logger, flush all chan data and destroy all adapter instances.
|
|
||||||
func (l *Logger) Close() { |
|
||||||
l.quit <- true |
|
||||||
for { |
|
||||||
if len(l.msg) > 0 { |
|
||||||
bm := <-l.msg |
|
||||||
for _, l := range l.outputs { |
|
||||||
if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil { |
|
||||||
fmt.Println("ERROR, unable to WriteMsg:", err) |
|
||||||
} |
|
||||||
} |
|
||||||
} else { |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
for _, l := range l.outputs { |
|
||||||
l.Flush() |
|
||||||
l.Destroy() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (l *Logger) Trace(format string, v ...interface{}) { |
|
||||||
msg := fmt.Sprintf("[T] "+format, v...) |
|
||||||
l.writerMsg(0, TRACE, msg) |
|
||||||
} |
|
||||||
|
|
||||||
func (l *Logger) Debug(format string, v ...interface{}) { |
|
||||||
msg := fmt.Sprintf("[D] "+format, v...) |
|
||||||
l.writerMsg(0, DEBUG, msg) |
|
||||||
} |
|
||||||
|
|
||||||
func (l *Logger) Info(format string, v ...interface{}) { |
|
||||||
msg := fmt.Sprintf("[I] "+format, v...) |
|
||||||
l.writerMsg(0, INFO, msg) |
|
||||||
} |
|
||||||
|
|
||||||
func (l *Logger) Warn(format string, v ...interface{}) { |
|
||||||
msg := fmt.Sprintf("[W] "+format, v...) |
|
||||||
l.writerMsg(0, WARN, msg) |
|
||||||
} |
|
||||||
|
|
||||||
func (l *Logger) Error(skip int, format string, v ...interface{}) { |
|
||||||
msg := fmt.Sprintf("[E] "+format, v...) |
|
||||||
l.writerMsg(skip, ERROR, msg) |
|
||||||
} |
|
||||||
|
|
||||||
func (l *Logger) Critical(skip int, format string, v ...interface{}) { |
|
||||||
msg := fmt.Sprintf("[C] "+format, v...) |
|
||||||
l.writerMsg(skip, CRITICAL, msg) |
|
||||||
} |
|
||||||
|
|
||||||
func (l *Logger) Fatal(skip int, format string, v ...interface{}) { |
|
||||||
msg := fmt.Sprintf("[F] "+format, v...) |
|
||||||
l.writerMsg(skip, FATAL, msg) |
|
||||||
l.Close() |
|
||||||
os.Exit(1) |
|
||||||
} |
|
@ -1,87 +0,0 @@ |
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package log |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"net/smtp" |
|
||||||
"strings" |
|
||||||
"time" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
subjectPhrase = "Diagnostic message from server" |
|
||||||
) |
|
||||||
|
|
||||||
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
|
|
||||||
type SmtpWriter struct { |
|
||||||
Username string `json:"Username"` |
|
||||||
Password string `json:"password"` |
|
||||||
Host string `json:"Host"` |
|
||||||
Subject string `json:"subject"` |
|
||||||
RecipientAddresses []string `json:"sendTos"` |
|
||||||
Level int `json:"level"` |
|
||||||
} |
|
||||||
|
|
||||||
// create smtp writer.
|
|
||||||
func NewSmtpWriter() LoggerInterface { |
|
||||||
return &SmtpWriter{Level: TRACE} |
|
||||||
} |
|
||||||
|
|
||||||
// init smtp writer with json config.
|
|
||||||
// config like:
|
|
||||||
// {
|
|
||||||
// "Username":"example@gmail.com",
|
|
||||||
// "password:"password",
|
|
||||||
// "host":"smtp.gmail.com:465",
|
|
||||||
// "subject":"email title",
|
|
||||||
// "sendTos":["email1","email2"],
|
|
||||||
// "level":LevelError
|
|
||||||
// }
|
|
||||||
func (sw *SmtpWriter) Init(jsonconfig string) error { |
|
||||||
return json.Unmarshal([]byte(jsonconfig), sw) |
|
||||||
} |
|
||||||
|
|
||||||
// write message in smtp writer.
|
|
||||||
// it will send an email with subject and only this message.
|
|
||||||
func (s *SmtpWriter) WriteMsg(msg string, skip, level int) error { |
|
||||||
if level < s.Level { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
hp := strings.Split(s.Host, ":") |
|
||||||
|
|
||||||
// Set up authentication information.
|
|
||||||
auth := smtp.PlainAuth( |
|
||||||
"", |
|
||||||
s.Username, |
|
||||||
s.Password, |
|
||||||
hp[0], |
|
||||||
) |
|
||||||
// Connect to the server, authenticate, set the sender and recipient,
|
|
||||||
// and send the email all in one step.
|
|
||||||
content_type := "Content-Type: text/plain" + "; charset=UTF-8" |
|
||||||
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.Username + "<" + s.Username + |
|
||||||
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg) |
|
||||||
|
|
||||||
return smtp.SendMail( |
|
||||||
s.Host, |
|
||||||
auth, |
|
||||||
s.Username, |
|
||||||
s.RecipientAddresses, |
|
||||||
mailmsg, |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
func (_ *SmtpWriter) Flush() { |
|
||||||
} |
|
||||||
|
|
||||||
func (_ *SmtpWriter) Destroy() { |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
Register("smtp", NewSmtpWriter) |
|
||||||
} |
|
@ -0,0 +1,76 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>JavaScript license information</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<table id="jslicense-labels1"> |
||||||
|
<tr> |
||||||
|
<td><a href="/js/libs/jquery.are-you-sure.js">jquery.are-you-sure.js</a></td> |
||||||
|
<td> |
||||||
|
<a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a> |
||||||
|
<br> |
||||||
|
<a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU-GPL-2.0-or-later</a> |
||||||
|
</td> |
||||||
|
<td><a href="/js/libs/jquery.are-you-sure.js">jquery.are-you-sure.js</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/js/semantic-2.2.7.min.js">semantic-2.2.1.min.js</a></td> |
||||||
|
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td> |
||||||
|
<td><a href="https://github.com/Semantic-Org/Semantic-UI/archive/2.2.1.tar.gz">semantic-UI-2.2.1.tar.gz</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/js/gogs.js">gogs.js</a></td> |
||||||
|
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td> |
||||||
|
<td><a href="/js/gogs.js">gogs.js</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/js/libs/clipboard-1.5.9.min.js">clipboard-1.5.9.min.js</a></td> |
||||||
|
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td> |
||||||
|
<td><a href="https://github.com/zenorocha/clipboard.js/archive/v1.5.9.tar.gz">clipboard-1.5.9.tar.gz</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/js/libs/emojify-1.1.0.min.js">emojify-1.1.0.min.js</a></td> |
||||||
|
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td> |
||||||
|
<td><a href="https://github.com/Ranks/emojify.js/archive/1.1.0.tar.gz">emojify-1.1.0.tar.gz</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/plugins/dropzone-4.2.0/dropzone.js">dropzone.js</a></td> |
||||||
|
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td> |
||||||
|
<td><a href="/plugins/dropzone-4.2.0/dropzone.js">dropzone.js</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/plugins/highlight-9.6.0/highlight.pack.js">highlight.pack.js</a></td> |
||||||
|
<td><a href="https://github.com/isagalaev/highlight.js/blob/master/LICENSE">BSD 3 Clause</a></td> |
||||||
|
<td><a href="https://github.com/isagalaev/highlight.js/archive/9.6.0.tar.gz">highlight.js-9.6.0.tar.gz</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/plugins/jquery.datetimepicker-2.4.5/jquery.datetimepicker.js">jquery.datetimepicker.js</a></td> |
||||||
|
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td> |
||||||
|
<td><a href="/plugins/jquery.datetimepicker-2.4.5/jquery.datetimepicker.js">jquery.datetimepicker.js</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/plugins/jquery.minicolors-2.2.3/jquery.minicolors.min.js">jquery.minicolors.min.js</a></td> |
||||||
|
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td> |
||||||
|
<td><a href="https://github.com/claviska/jquery-minicolors/archive/2.2.3.tar.gz">jquery.minicolors-2.2.3.tar.gz</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/plugins/simplemde-1.10.1/simplemde.min.js">simplemde.min.js</a></td> |
||||||
|
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td> |
||||||
|
<td><a href="https://github.com/NextStepWebs/simplemde-markdown-editor/archive/1.10.1.tar.gz">simplemde-markdown-editor-1.10.1.tar.gz</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/plugins/marked-0.3.6/marked.min.js">marked.min.js</a></td> |
||||||
|
<td><a href="https://github.com/chjj/marked/blob/master/LICENSE">Expat</a></td> |
||||||
|
<td><a href="https://github.com/chjj/marked/archive/v0.3.6.tar.gz">marked-0.3.6.tar.gz</a></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td><a href="/plugins/notebookjs-0.2.6/notebook.min.js">notebook.min.js</a></td> |
||||||
|
<td><a href="https://github.com/jsvine/notebookjs/blob/master/LICENSE.txt">Expat</a></td> |
||||||
|
<td><a href="https://github.com/jsvine/notebookjs/archive/v0.2.6.tar.gz">notebookjs-0.2.6.tar.gz</a></td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 502 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 164 KiB |