mirror of
https://github.com/go-gitea/gitea.git
synced 2025-04-08 12:48:09 +00:00
Compare commits
3 Commits
c27d87a9ac
...
ba921fd903
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ba921fd903 | ||
![]() |
f94ee4fd3c | ||
![]() |
45c45934aa |
@ -463,7 +463,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa
|
||||
matchTimes++
|
||||
}
|
||||
case "paths":
|
||||
filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref)
|
||||
filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.MergeBase)
|
||||
if err != nil {
|
||||
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err)
|
||||
} else {
|
||||
@ -476,7 +476,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa
|
||||
}
|
||||
}
|
||||
case "paths-ignore":
|
||||
filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref)
|
||||
filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.MergeBase)
|
||||
if err != nil {
|
||||
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err)
|
||||
} else {
|
||||
|
@ -4,6 +4,7 @@
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"strconv"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
@ -29,9 +30,7 @@ func (n *Details) Kind() ast.NodeKind {
|
||||
|
||||
// NewDetails returns a new Paragraph node.
|
||||
func NewDetails() *Details {
|
||||
return &Details{
|
||||
BaseBlock: ast.BaseBlock{},
|
||||
}
|
||||
return &Details{}
|
||||
}
|
||||
|
||||
// Summary is a block that contains the summary of details block
|
||||
@ -54,9 +53,7 @@ func (n *Summary) Kind() ast.NodeKind {
|
||||
|
||||
// NewSummary returns a new Summary node.
|
||||
func NewSummary() *Summary {
|
||||
return &Summary{
|
||||
BaseBlock: ast.BaseBlock{},
|
||||
}
|
||||
return &Summary{}
|
||||
}
|
||||
|
||||
// TaskCheckBoxListItem is a block that represents a list item of a markdown block with a checkbox
|
||||
@ -95,29 +92,6 @@ type Icon struct {
|
||||
Name []byte
|
||||
}
|
||||
|
||||
// Dump implements Node.Dump .
|
||||
func (n *Icon) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
m["Name"] = string(n.Name)
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
// KindIcon is the NodeKind for Icon
|
||||
var KindIcon = ast.NewNodeKind("Icon")
|
||||
|
||||
// Kind implements Node.Kind.
|
||||
func (n *Icon) Kind() ast.NodeKind {
|
||||
return KindIcon
|
||||
}
|
||||
|
||||
// NewIcon returns a new Paragraph node.
|
||||
func NewIcon(name string) *Icon {
|
||||
return &Icon{
|
||||
BaseInline: ast.BaseInline{},
|
||||
Name: []byte(name),
|
||||
}
|
||||
}
|
||||
|
||||
// ColorPreview is an inline for a color preview
|
||||
type ColorPreview struct {
|
||||
ast.BaseInline
|
||||
@ -175,3 +149,24 @@ func NewAttention(attentionType string) *Attention {
|
||||
AttentionType: attentionType,
|
||||
}
|
||||
}
|
||||
|
||||
var KindRawHTML = ast.NewNodeKind("RawHTML")
|
||||
|
||||
type RawHTML struct {
|
||||
ast.BaseBlock
|
||||
rawHTML template.HTML
|
||||
}
|
||||
|
||||
func (n *RawHTML) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
m["RawHTML"] = string(n.rawHTML)
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
func (n *RawHTML) Kind() ast.NodeKind {
|
||||
return KindRawHTML
|
||||
}
|
||||
|
||||
func NewRawHTML(rawHTML template.HTML) *RawHTML {
|
||||
return &RawHTML{rawHTML: rawHTML}
|
||||
}
|
||||
|
@ -4,23 +4,22 @@
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/svg"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
east "github.com/yuin/goldmark/extension/ast"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func nodeToTable(meta *yaml.Node) ast.Node {
|
||||
for {
|
||||
if meta == nil {
|
||||
return nil
|
||||
}
|
||||
switch meta.Kind {
|
||||
case yaml.DocumentNode:
|
||||
meta = meta.Content[0]
|
||||
continue
|
||||
default:
|
||||
}
|
||||
break
|
||||
for meta != nil && meta.Kind == yaml.DocumentNode {
|
||||
meta = meta.Content[0]
|
||||
}
|
||||
if meta == nil {
|
||||
return nil
|
||||
}
|
||||
switch meta.Kind {
|
||||
case yaml.MappingNode:
|
||||
@ -72,12 +71,28 @@ func sequenceNodeToTable(meta *yaml.Node) ast.Node {
|
||||
return table
|
||||
}
|
||||
|
||||
func nodeToDetails(meta *yaml.Node, icon string) ast.Node {
|
||||
func nodeToDetails(g *ASTTransformer, meta *yaml.Node) ast.Node {
|
||||
for meta != nil && meta.Kind == yaml.DocumentNode {
|
||||
meta = meta.Content[0]
|
||||
}
|
||||
if meta == nil {
|
||||
return nil
|
||||
}
|
||||
if meta.Kind != yaml.MappingNode {
|
||||
return nil
|
||||
}
|
||||
var keys []string
|
||||
for i := 0; i < len(meta.Content); i += 2 {
|
||||
if meta.Content[i].Kind == yaml.ScalarNode {
|
||||
keys = append(keys, meta.Content[i].Value)
|
||||
}
|
||||
}
|
||||
details := NewDetails()
|
||||
details.SetAttributeString(g.renderInternal.SafeAttr("class"), g.renderInternal.SafeValue("frontmatter-content"))
|
||||
summary := NewSummary()
|
||||
summary.AppendChild(summary, NewIcon(icon))
|
||||
summaryInnerHTML := htmlutil.HTMLFormat("%s %s", svg.RenderHTML("octicon-table", 12), strings.Join(keys, ", "))
|
||||
summary.AppendChild(summary, NewRawHTML(summaryInnerHTML))
|
||||
details.AppendChild(details, summary)
|
||||
details.AppendChild(details, nodeToTable(meta))
|
||||
|
||||
return details
|
||||
}
|
||||
|
@ -5,9 +5,6 @@ package markdown
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
@ -51,7 +48,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||
|
||||
tocList := make([]Header, 0, 20)
|
||||
if rc.yamlNode != nil {
|
||||
metaNode := rc.toMetaNode()
|
||||
metaNode := rc.toMetaNode(g)
|
||||
if metaNode != nil {
|
||||
node.InsertBefore(node, firstChild, metaNode)
|
||||
}
|
||||
@ -112,11 +109,6 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||
}
|
||||
}
|
||||
|
||||
// it is copied from old code, which is quite doubtful whether it is correct
|
||||
var reValidIconName = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^[-\w]+$`) // old: regexp.MustCompile("^[a-z ]+$")
|
||||
})
|
||||
|
||||
// NewHTMLRenderer creates a HTMLRenderer to render in the gitea form.
|
||||
func NewHTMLRenderer(renderInternal *internal.RenderInternal, opts ...html.Option) renderer.NodeRenderer {
|
||||
r := &HTMLRenderer{
|
||||
@ -141,11 +133,11 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(ast.KindDocument, r.renderDocument)
|
||||
reg.Register(KindDetails, r.renderDetails)
|
||||
reg.Register(KindSummary, r.renderSummary)
|
||||
reg.Register(KindIcon, r.renderIcon)
|
||||
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
|
||||
reg.Register(KindAttention, r.renderAttention)
|
||||
reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
|
||||
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
|
||||
reg.Register(KindRawHTML, r.renderRawHTML)
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
@ -207,30 +199,14 @@ func (r *HTMLRenderer) renderSummary(w util.BufWriter, source []byte, node ast.N
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) renderIcon(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
func (r *HTMLRenderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if !entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
n := node.(*Icon)
|
||||
|
||||
name := strings.TrimSpace(strings.ToLower(string(n.Name)))
|
||||
|
||||
if len(name) == 0 {
|
||||
// skip this
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
if !reValidIconName().MatchString(name) {
|
||||
// skip this
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
// FIXME: the "icon xxx" is from Fomantic UI, it's really questionable whether it still works correctly
|
||||
err := r.renderInternal.FormatWithSafeAttrs(w, `<i class="icon %s"></i>`, name)
|
||||
n := node.(*RawHTML)
|
||||
_, err := w.WriteString(string(r.renderInternal.ProtectSafeAttrs(n.rawHTML)))
|
||||
if err != nil {
|
||||
return ast.WalkStop, err
|
||||
}
|
||||
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
@ -184,11 +184,7 @@ func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
|
||||
// Preserve original length.
|
||||
bufWithMetadataLength := len(buf)
|
||||
|
||||
rc := &RenderConfig{
|
||||
Meta: markup.RenderMetaAsDetails,
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}
|
||||
rc := &RenderConfig{Meta: markup.RenderMetaAsDetails}
|
||||
buf, _ = ExtractMetadataBytes(buf, rc)
|
||||
|
||||
metaLength := bufWithMetadataLength - len(buf)
|
||||
|
@ -383,18 +383,74 @@ func TestColorPreview(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskList(t *testing.T) {
|
||||
func TestMarkdownFrontmatter(t *testing.T) {
|
||||
testcases := []struct {
|
||||
testcase string
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"MapInFrontmatter",
|
||||
`---
|
||||
key1: val1
|
||||
key2: val2
|
||||
---
|
||||
test
|
||||
`,
|
||||
`<details class="frontmatter-content"><summary><span>octicon-table(12/)</span> key1, key2</summary><table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>key1</th>
|
||||
<th>key2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>val1</td>
|
||||
<td>val2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</details><p>test</p>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
"ListInFrontmatter",
|
||||
`---
|
||||
- item1
|
||||
- item2
|
||||
---
|
||||
test
|
||||
`,
|
||||
`- item1
|
||||
- item2
|
||||
|
||||
<p>test</p>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
"StringInFrontmatter",
|
||||
`---
|
||||
anything
|
||||
---
|
||||
test
|
||||
`,
|
||||
`anything
|
||||
|
||||
<p>test</p>
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
// data-source-position should take into account YAML frontmatter.
|
||||
"ListAfterFrontmatter",
|
||||
`---
|
||||
foo: bar
|
||||
---
|
||||
- [ ] task 1`,
|
||||
`<details><summary><i class="icon table"></i></summary><table>
|
||||
`<details class="frontmatter-content"><summary><span>octicon-table(12/)</span> foo</summary><table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>foo</th>
|
||||
@ -414,9 +470,9 @@ foo: bar
|
||||
}
|
||||
|
||||
for _, test := range testcases {
|
||||
res, err := markdown.RenderString(markup.NewTestRenderContext(), test.testcase)
|
||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
||||
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
|
||||
res, err := markdown.RenderString(markup.NewTestRenderContext(), test.input)
|
||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test.name)
|
||||
assert.Equal(t, test.expected, string(res), "Unexpected result in testcase %q", test.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ import (
|
||||
// RenderConfig represents rendering configuration for this file
|
||||
type RenderConfig struct {
|
||||
Meta markup.RenderMetaMode
|
||||
Icon string
|
||||
TOC string // "false": hide, "side"/empty: in sidebar, "main"/"true": in main view
|
||||
Lang string
|
||||
yamlNode *yaml.Node
|
||||
@ -74,7 +73,7 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||
|
||||
type yamlRenderConfig struct {
|
||||
Meta *string `yaml:"meta"`
|
||||
Icon *string `yaml:"details_icon"`
|
||||
Icon *string `yaml:"details_icon"` // deprecated, because there is no font icon, so no custom icon
|
||||
TOC *string `yaml:"include_toc"`
|
||||
Lang *string `yaml:"lang"`
|
||||
}
|
||||
@ -96,10 +95,6 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||
rc.Meta = renderMetaModeFromString(*cfg.Gitea.Meta)
|
||||
}
|
||||
|
||||
if cfg.Gitea.Icon != nil {
|
||||
rc.Icon = strings.TrimSpace(strings.ToLower(*cfg.Gitea.Icon))
|
||||
}
|
||||
|
||||
if cfg.Gitea.Lang != nil && *cfg.Gitea.Lang != "" {
|
||||
rc.Lang = *cfg.Gitea.Lang
|
||||
}
|
||||
@ -111,7 +106,7 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *RenderConfig) toMetaNode() ast.Node {
|
||||
func (rc *RenderConfig) toMetaNode(g *ASTTransformer) ast.Node {
|
||||
if rc.yamlNode == nil {
|
||||
return nil
|
||||
}
|
||||
@ -119,7 +114,7 @@ func (rc *RenderConfig) toMetaNode() ast.Node {
|
||||
case markup.RenderMetaAsTable:
|
||||
return nodeToTable(rc.yamlNode)
|
||||
case markup.RenderMetaAsDetails:
|
||||
return nodeToDetails(rc.yamlNode, rc.Icon)
|
||||
return nodeToDetails(g, rc.yamlNode)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
@ -21,42 +21,36 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
{
|
||||
"empty", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "",
|
||||
},
|
||||
{
|
||||
"lang", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "test",
|
||||
}, "lang: test",
|
||||
},
|
||||
{
|
||||
"metatable", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "gitea: table",
|
||||
},
|
||||
{
|
||||
"metanone", &RenderConfig{
|
||||
Meta: "none",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "gitea: none",
|
||||
},
|
||||
{
|
||||
"metadetails", &RenderConfig{
|
||||
Meta: "details",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "gitea: details",
|
||||
},
|
||||
{
|
||||
"metawrong", &RenderConfig{
|
||||
Meta: "details",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "gitea: wrong",
|
||||
},
|
||||
@ -64,7 +58,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
"toc", &RenderConfig{
|
||||
TOC: "true",
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "include_toc: true",
|
||||
},
|
||||
@ -72,14 +65,12 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
"tocfalse", &RenderConfig{
|
||||
TOC: "false",
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "include_toc: false",
|
||||
},
|
||||
{
|
||||
"toclang", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
TOC: "true",
|
||||
Lang: "testlang",
|
||||
}, `
|
||||
@ -90,7 +81,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
{
|
||||
"complexlang", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "testlang",
|
||||
}, `
|
||||
gitea:
|
||||
@ -100,7 +90,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
{
|
||||
"complexlang2", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "testlang",
|
||||
}, `
|
||||
lang: notright
|
||||
@ -111,7 +100,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
{
|
||||
"complexlang", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "testlang",
|
||||
}, `
|
||||
gitea:
|
||||
@ -123,7 +111,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
Lang: "two",
|
||||
Meta: "table",
|
||||
TOC: "true",
|
||||
Icon: "smiley",
|
||||
}, `
|
||||
lang: one
|
||||
include_toc: true
|
||||
@ -139,14 +126,12 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}
|
||||
err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", " ")), got)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.expected.Meta, got.Meta)
|
||||
assert.Equal(t, tt.expected.Icon, got.Icon)
|
||||
assert.Equal(t, tt.expected.Lang, got.Lang)
|
||||
assert.Equal(t, tt.expected.TOC, got.TOC)
|
||||
})
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package structs
|
||||
|
||||
import "time"
|
||||
|
||||
// FileOptions options for all file APIs
|
||||
type FileOptions struct {
|
||||
// message (optional) for the commit of this file. if not supplied, a default message will be used
|
||||
@ -121,6 +123,10 @@ type ContentsResponse struct {
|
||||
Path string `json:"path"`
|
||||
SHA string `json:"sha"`
|
||||
LastCommitSHA string `json:"last_commit_sha"`
|
||||
// swagger:strfmt date-time
|
||||
LastCommitterDate time.Time `json:"last_committer_date"`
|
||||
// swagger:strfmt date-time
|
||||
LastAuthorDate time.Time `json:"last_author_date"`
|
||||
// `type` will be `file`, `dir`, `symlink`, or `submodule`
|
||||
Type string `json:"type"`
|
||||
Size int64 `json:"size"`
|
||||
|
@ -188,6 +188,14 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
|
||||
},
|
||||
}
|
||||
|
||||
// GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them
|
||||
// https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits
|
||||
if lastCommit.Committer != nil {
|
||||
contentsResponse.LastCommitterDate = lastCommit.Committer.When
|
||||
}
|
||||
if lastCommit.Author != nil {
|
||||
contentsResponse.LastAuthorDate = lastCommit.Author.When
|
||||
}
|
||||
// Now populate the rest of the ContentsResponse based on entry type
|
||||
if entry.IsRegular() || entry.IsExecutable() {
|
||||
contentsResponse.Type = string(ContentTypeRegular)
|
||||
|
@ -5,6 +5,7 @@ package files
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
@ -30,18 +31,20 @@ func getExpectedReadmeContentsResponse() *api.ContentsResponse {
|
||||
gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha
|
||||
downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath
|
||||
return &api.ContentsResponse{
|
||||
Name: treePath,
|
||||
Path: treePath,
|
||||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
||||
LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Name: treePath,
|
||||
Path: treePath,
|
||||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
|
||||
LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
||||
LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
|
@ -5,6 +5,7 @@ package files
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
@ -42,18 +43,20 @@ func getExpectedFileResponse() *api.FileResponse {
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
|
||||
return &api.FileResponse{
|
||||
Content: &api.ContentsResponse{
|
||||
Name: treePath,
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Name: treePath,
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
||||
LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
|
10
templates/swagger/v1_json.tmpl
generated
10
templates/swagger/v1_json.tmpl
generated
@ -20411,10 +20411,20 @@
|
||||
"type": "string",
|
||||
"x-go-name": "HTMLURL"
|
||||
},
|
||||
"last_author_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"x-go-name": "LastAuthorDate"
|
||||
},
|
||||
"last_commit_sha": {
|
||||
"type": "string",
|
||||
"x-go-name": "LastCommitSHA"
|
||||
},
|
||||
"last_committer_date": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"x-go-name": "LastCommitterDate"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"x-go-name": "Name"
|
||||
|
@ -4,6 +4,7 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -28,6 +29,7 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
release_service "code.gitea.io/gitea/services/release"
|
||||
@ -1368,3 +1370,81 @@ jobs:
|
||||
assert.Equal(t, "true", dispatchPayload.Inputs["myinput3"])
|
||||
})
|
||||
}
|
||||
|
||||
func TestClosePullRequestWithPath(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
// user2 is the owner of the base repo
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
user2Token := getTokenForLoggedInUser(t, loginUser(t, user2.Name), auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||
// user4 is the owner of the fork repo
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
user4Token := getTokenForLoggedInUser(t, loginUser(t, user4.Name), auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||
|
||||
// create the base repo
|
||||
apiBaseRepo := createActionsTestRepo(t, user2Token, "close-pull-request-with-path", false)
|
||||
baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiBaseRepo.ID})
|
||||
user2APICtx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
// init the workflow
|
||||
wfTreePath := ".gitea/workflows/pull.yml"
|
||||
wfFileContent := `name: Pull Request
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
paths:
|
||||
- 'app/**'
|
||||
jobs:
|
||||
echo:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo 'Hello World'
|
||||
`
|
||||
opts1 := getWorkflowCreateFileOptions(user2, baseRepo.DefaultBranch, "create "+wfTreePath, wfFileContent)
|
||||
createWorkflowFile(t, user2Token, baseRepo.OwnerName, baseRepo.Name, wfTreePath, opts1)
|
||||
|
||||
// user4 forks the repo
|
||||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", baseRepo.OwnerName, baseRepo.Name),
|
||||
&api.CreateForkOption{
|
||||
Name: util.ToPointer("close-pull-request-with-path-fork"),
|
||||
}).AddTokenAuth(user4Token)
|
||||
resp := MakeRequest(t, req, http.StatusAccepted)
|
||||
var apiForkRepo api.Repository
|
||||
DecodeJSON(t, resp, &apiForkRepo)
|
||||
forkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiForkRepo.ID})
|
||||
user4APICtx := NewAPITestContext(t, user4.Name, forkRepo.Name, auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
// user4 creates a pull request to add file "app/main.go"
|
||||
doAPICreateFile(user4APICtx, "app/main.go", &api.CreateFileOptions{
|
||||
FileOptions: api.FileOptions{
|
||||
NewBranchName: "user4/add-main",
|
||||
Message: "create main.go",
|
||||
Author: api.Identity{
|
||||
Name: user4.Name,
|
||||
Email: user4.Email,
|
||||
},
|
||||
Committer: api.Identity{
|
||||
Name: user4.Name,
|
||||
Email: user4.Email,
|
||||
},
|
||||
Dates: api.CommitDateOptions{
|
||||
Author: time.Now(),
|
||||
Committer: time.Now(),
|
||||
},
|
||||
},
|
||||
ContentBase64: base64.StdEncoding.EncodeToString([]byte("// main.go")),
|
||||
})(t)
|
||||
apiPull, err := doAPICreatePullRequest(user4APICtx, baseRepo.OwnerName, baseRepo.Name, baseRepo.DefaultBranch, user4.Name+":user4/add-main")(t)
|
||||
assert.NoError(t, err)
|
||||
|
||||
doAPIMergePullRequest(user2APICtx, baseRepo.OwnerName, baseRepo.Name, apiPull.Index)(t)
|
||||
|
||||
pullRequest := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID})
|
||||
|
||||
// load and compare ActionRun
|
||||
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: baseRepo.ID}))
|
||||
actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: baseRepo.ID})
|
||||
assert.Equal(t, actions_module.GithubEventPullRequest, actionRun.TriggerEvent)
|
||||
assert.Equal(t, pullRequest.MergedCommitID, actionRun.CommitSHA)
|
||||
})
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -49,28 +49,42 @@ func getCreateFileOptions() api.CreateFileOptions {
|
||||
}
|
||||
}
|
||||
|
||||
func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCommitSHA string) *api.FileResponse {
|
||||
func normalizeFileContentResponseCommitTime(c *api.ContentsResponse) {
|
||||
// decoded JSON response may contain different timezone from the one parsed by git commit
|
||||
// so we need to normalize the time to UTC to make "assert.Equal" pass
|
||||
c.LastCommitterDate = c.LastCommitterDate.UTC()
|
||||
c.LastAuthorDate = c.LastAuthorDate.UTC()
|
||||
}
|
||||
|
||||
type apiFileResponseInfo struct {
|
||||
repoFullName, commitID, treePath, lastCommitSHA string
|
||||
lastCommitterWhen, lastAuthorWhen time.Time
|
||||
}
|
||||
|
||||
func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileResponse {
|
||||
sha := "a635aa942442ddfdba07468cf9661c08fbdf0ebf"
|
||||
encoding := "base64"
|
||||
content := "VGhpcyBpcyBuZXcgdGV4dA=="
|
||||
selfURL := setting.AppURL + "api/v1/repos/" + repoFullName + "/contents/" + treePath + "?ref=master"
|
||||
htmlURL := setting.AppURL + repoFullName + "/src/branch/master/" + treePath
|
||||
gitURL := setting.AppURL + "api/v1/repos/" + repoFullName + "/git/blobs/" + sha
|
||||
downloadURL := setting.AppURL + repoFullName + "/raw/branch/master/" + treePath
|
||||
return &api.FileResponse{
|
||||
selfURL := setting.AppURL + "api/v1/repos/" + info.repoFullName + "/contents/" + info.treePath + "?ref=master"
|
||||
htmlURL := setting.AppURL + info.repoFullName + "/src/branch/master/" + info.treePath
|
||||
gitURL := setting.AppURL + "api/v1/repos/" + info.repoFullName + "/git/blobs/" + sha
|
||||
downloadURL := setting.AppURL + info.repoFullName + "/raw/branch/master/" + info.treePath
|
||||
ret := &api.FileResponse{
|
||||
Content: &api.ContentsResponse{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: latestCommitSHA,
|
||||
Size: 16,
|
||||
Type: "file",
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Name: path.Base(info.treePath),
|
||||
Path: info.treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: info.lastCommitSHA,
|
||||
LastCommitterDate: info.lastCommitterWhen,
|
||||
LastAuthorDate: info.lastAuthorWhen,
|
||||
Size: 16,
|
||||
Type: "file",
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
@ -79,10 +93,10 @@ func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCo
|
||||
},
|
||||
Commit: &api.FileCommitResponse{
|
||||
CommitMeta: api.CommitMeta{
|
||||
URL: setting.AppURL + "api/v1/repos/" + repoFullName + "/git/commits/" + commitID,
|
||||
SHA: commitID,
|
||||
URL: setting.AppURL + "api/v1/repos/" + info.repoFullName + "/git/commits/" + info.commitID,
|
||||
SHA: info.commitID,
|
||||
},
|
||||
HTMLURL: setting.AppURL + repoFullName + "/commit/" + commitID,
|
||||
HTMLURL: setting.AppURL + info.repoFullName + "/commit/" + info.commitID,
|
||||
Author: &api.CommitUser{
|
||||
Identity: api.Identity{
|
||||
Name: "Anne Doe",
|
||||
@ -106,6 +120,8 @@ func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCo
|
||||
Payload: "",
|
||||
},
|
||||
}
|
||||
normalizeFileContentResponseCommitTime(ret.Content)
|
||||
return ret
|
||||
}
|
||||
|
||||
func BenchmarkAPICreateFileSmall(b *testing.B) {
|
||||
@ -167,11 +183,20 @@ func TestAPICreateFile(t *testing.T) {
|
||||
AddTokenAuth(token2)
|
||||
resp := MakeRequest(t, req, http.StatusCreated)
|
||||
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1)
|
||||
defer gitRepo.Close()
|
||||
commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName)
|
||||
latestCommit, _ := gitRepo.GetCommitByPath(treePath)
|
||||
expectedFileResponse := getExpectedFileResponseForCreate("user2/repo1", commitID, treePath, latestCommit.ID.String())
|
||||
lastCommit, _ := gitRepo.GetCommitByPath(treePath)
|
||||
expectedFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{
|
||||
repoFullName: "user2/repo1",
|
||||
commitID: commitID,
|
||||
treePath: treePath,
|
||||
lastCommitSHA: lastCommit.ID.String(),
|
||||
lastCommitterWhen: lastCommit.Committer.When,
|
||||
lastAuthorWhen: lastCommit.Author.When,
|
||||
})
|
||||
var fileResponse api.FileResponse
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
normalizeFileContentResponseCommitTime(fileResponse.Content)
|
||||
assert.Equal(t, expectedFileResponse.Content, fileResponse.Content)
|
||||
assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
||||
assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
||||
@ -181,7 +206,6 @@ func TestAPICreateFile(t *testing.T) {
|
||||
assert.Equal(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email)
|
||||
assert.Equal(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name)
|
||||
assert.Equal(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date)
|
||||
gitRepo.Close()
|
||||
}
|
||||
|
||||
// Test creating a file in a new branch
|
||||
@ -285,10 +309,19 @@ func TestAPICreateFile(t *testing.T) {
|
||||
resp = MakeRequest(t, req, http.StatusCreated)
|
||||
emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "empty-repo"}) // public repo
|
||||
gitRepo, _ := gitrepo.OpenRepository(t.Context(), emptyRepo)
|
||||
defer gitRepo.Close()
|
||||
commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName)
|
||||
latestCommit, _ := gitRepo.GetCommitByPath(treePath)
|
||||
expectedFileResponse := getExpectedFileResponseForCreate("user2/empty-repo", commitID, treePath, latestCommit.ID.String())
|
||||
expectedFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{
|
||||
repoFullName: "user2/empty-repo",
|
||||
commitID: commitID,
|
||||
treePath: treePath,
|
||||
lastCommitSHA: latestCommit.ID.String(),
|
||||
lastCommitterWhen: latestCommit.Committer.When,
|
||||
lastAuthorWhen: latestCommit.Author.When,
|
||||
})
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
normalizeFileContentResponseCommitTime(fileResponse.Content)
|
||||
assert.Equal(t, expectedFileResponse.Content, fileResponse.Content)
|
||||
assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
||||
assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
||||
@ -298,6 +331,5 @@ func TestAPICreateFile(t *testing.T) {
|
||||
assert.Equal(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email)
|
||||
assert.Equal(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name)
|
||||
assert.Equal(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date)
|
||||
gitRepo.Close()
|
||||
})
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
@ -47,28 +47,30 @@ func getUpdateFileOptions() *api.UpdateFileOptions {
|
||||
}
|
||||
}
|
||||
|
||||
func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string) *api.FileResponse {
|
||||
func getExpectedFileResponseForUpdate(info apiFileResponseInfo) *api.FileResponse {
|
||||
sha := "08bd14b2e2852529157324de9c226b3364e76136"
|
||||
encoding := "base64"
|
||||
content := "VGhpcyBpcyB1cGRhdGVkIHRleHQ="
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
|
||||
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + info.treePath + "?ref=master"
|
||||
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + info.treePath
|
||||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
|
||||
return &api.FileResponse{
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + info.treePath
|
||||
ret := &api.FileResponse{
|
||||
Content: &api.ContentsResponse{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: lastCommitSHA,
|
||||
Type: "file",
|
||||
Size: 20,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Name: path.Base(info.treePath),
|
||||
Path: info.treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: info.lastCommitSHA,
|
||||
LastCommitterDate: info.lastCommitterWhen,
|
||||
LastAuthorDate: info.lastAuthorWhen,
|
||||
Type: "file",
|
||||
Size: 20,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
@ -77,10 +79,10 @@ func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string)
|
||||
},
|
||||
Commit: &api.FileCommitResponse{
|
||||
CommitMeta: api.CommitMeta{
|
||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
|
||||
SHA: commitID,
|
||||
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + info.commitID,
|
||||
SHA: info.commitID,
|
||||
},
|
||||
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
|
||||
HTMLURL: setting.AppURL + "user2/repo1/commit/" + info.commitID,
|
||||
Author: &api.CommitUser{
|
||||
Identity: api.Identity{
|
||||
Name: "John Doe",
|
||||
@ -102,6 +104,8 @@ func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string)
|
||||
Payload: "",
|
||||
},
|
||||
}
|
||||
normalizeFileContentResponseCommitTime(ret.Content)
|
||||
return ret
|
||||
}
|
||||
|
||||
func TestAPIUpdateFile(t *testing.T) {
|
||||
@ -135,17 +139,24 @@ func TestAPIUpdateFile(t *testing.T) {
|
||||
AddTokenAuth(token2)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1)
|
||||
defer gitRepo.Close()
|
||||
commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName)
|
||||
lasCommit, _ := gitRepo.GetCommitByPath(treePath)
|
||||
expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath, lasCommit.ID.String())
|
||||
expectedFileResponse := getExpectedFileResponseForUpdate(apiFileResponseInfo{
|
||||
commitID: commitID,
|
||||
treePath: treePath,
|
||||
lastCommitSHA: lasCommit.ID.String(),
|
||||
lastCommitterWhen: lasCommit.Committer.When,
|
||||
lastAuthorWhen: lasCommit.Author.When,
|
||||
})
|
||||
var fileResponse api.FileResponse
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
normalizeFileContentResponseCommitTime(fileResponse.Content)
|
||||
assert.Equal(t, expectedFileResponse.Content, fileResponse.Content)
|
||||
assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
|
||||
assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
|
||||
assert.Equal(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
|
||||
assert.Equal(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
|
||||
gitRepo.Close()
|
||||
}
|
||||
|
||||
// Test updating a file in a new branch
|
||||
|
@ -77,51 +77,56 @@ func TestAPIChangeFiles(t *testing.T) {
|
||||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
// Test changing files in repo1 which user2 owns, try both with branch and empty branch
|
||||
for _, branch := range [...]string{
|
||||
"master", // Branch
|
||||
"", // Empty branch
|
||||
} {
|
||||
fileID++
|
||||
createTreePath := fmt.Sprintf("new/file%d.txt", fileID)
|
||||
updateTreePath := fmt.Sprintf("update/file%d.txt", fileID)
|
||||
deleteTreePath := fmt.Sprintf("delete/file%d.txt", fileID)
|
||||
createFile(user2, repo1, updateTreePath)
|
||||
createFile(user2, repo1, deleteTreePath)
|
||||
changeFilesOptions := getChangeFilesOptions()
|
||||
changeFilesOptions.BranchName = branch
|
||||
changeFilesOptions.Files[0].Path = createTreePath
|
||||
changeFilesOptions.Files[1].Path = updateTreePath
|
||||
changeFilesOptions.Files[2].Path = deleteTreePath
|
||||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents", user2.Name, repo1.Name), &changeFilesOptions).
|
||||
AddTokenAuth(token2)
|
||||
resp := MakeRequest(t, req, http.StatusCreated)
|
||||
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1)
|
||||
commitID, _ := gitRepo.GetBranchCommitID(changeFilesOptions.NewBranchName)
|
||||
createLasCommit, _ := gitRepo.GetCommitByPath(createTreePath)
|
||||
updateLastCommit, _ := gitRepo.GetCommitByPath(updateTreePath)
|
||||
expectedCreateFileResponse := getExpectedFileResponseForCreate(fmt.Sprintf("%v/%v", user2.Name, repo1.Name), commitID, createTreePath, createLasCommit.ID.String())
|
||||
expectedUpdateFileResponse := getExpectedFileResponseForUpdate(commitID, updateTreePath, updateLastCommit.ID.String())
|
||||
var filesResponse api.FilesResponse
|
||||
DecodeJSON(t, resp, &filesResponse)
|
||||
|
||||
// check create file
|
||||
assert.Equal(t, expectedCreateFileResponse.Content, filesResponse.Files[0])
|
||||
|
||||
// check update file
|
||||
assert.Equal(t, expectedUpdateFileResponse.Content, filesResponse.Files[1])
|
||||
|
||||
// test commit info
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.SHA, filesResponse.Commit.SHA)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.Committer.Email, filesResponse.Commit.Committer.Email)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.Committer.Name, filesResponse.Commit.Committer.Name)
|
||||
|
||||
// test delete file
|
||||
assert.Nil(t, filesResponse.Files[2])
|
||||
|
||||
gitRepo.Close()
|
||||
for _, branch := range []string{"master", ""} {
|
||||
t.Run("Branch-"+branch, func(t *testing.T) {
|
||||
fileID++
|
||||
createTreePath := fmt.Sprintf("new/file%d.txt", fileID)
|
||||
updateTreePath := fmt.Sprintf("update/file%d.txt", fileID)
|
||||
deleteTreePath := fmt.Sprintf("delete/file%d.txt", fileID)
|
||||
_, _ = createFile(user2, repo1, updateTreePath)
|
||||
_, _ = createFile(user2, repo1, deleteTreePath)
|
||||
changeFilesOptions := getChangeFilesOptions()
|
||||
changeFilesOptions.BranchName = branch
|
||||
changeFilesOptions.Files[0].Path = createTreePath
|
||||
changeFilesOptions.Files[1].Path = updateTreePath
|
||||
changeFilesOptions.Files[2].Path = deleteTreePath
|
||||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents", user2.Name, repo1.Name), &changeFilesOptions).
|
||||
AddTokenAuth(token2)
|
||||
resp := MakeRequest(t, req, http.StatusCreated)
|
||||
gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1)
|
||||
defer gitRepo.Close()
|
||||
commitID, _ := gitRepo.GetBranchCommitID(changeFilesOptions.NewBranchName)
|
||||
createLasCommit, _ := gitRepo.GetCommitByPath(createTreePath)
|
||||
updateLastCommit, _ := gitRepo.GetCommitByPath(updateTreePath)
|
||||
expectedCreateFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{
|
||||
repoFullName: fmt.Sprintf("%s/%s", user2.Name, repo1.Name),
|
||||
commitID: commitID,
|
||||
treePath: createTreePath,
|
||||
lastCommitSHA: createLasCommit.ID.String(),
|
||||
lastCommitterWhen: createLasCommit.Committer.When,
|
||||
lastAuthorWhen: createLasCommit.Author.When,
|
||||
})
|
||||
expectedUpdateFileResponse := getExpectedFileResponseForUpdate(apiFileResponseInfo{
|
||||
commitID: commitID,
|
||||
treePath: updateTreePath,
|
||||
lastCommitSHA: updateLastCommit.ID.String(),
|
||||
lastCommitterWhen: updateLastCommit.Committer.When,
|
||||
lastAuthorWhen: updateLastCommit.Author.When,
|
||||
})
|
||||
var filesResponse api.FilesResponse
|
||||
DecodeJSON(t, resp, &filesResponse)
|
||||
normalizeFileContentResponseCommitTime(filesResponse.Files[0])
|
||||
normalizeFileContentResponseCommitTime(filesResponse.Files[1])
|
||||
assert.Equal(t, expectedCreateFileResponse.Content, filesResponse.Files[0]) // check create file
|
||||
assert.Equal(t, expectedUpdateFileResponse.Content, filesResponse.Files[1]) // check update file
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.SHA, filesResponse.Commit.SHA)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.Committer.Email, filesResponse.Commit.Committer.Email)
|
||||
assert.Equal(t, expectedCreateFileResponse.Commit.Committer.Name, filesResponse.Commit.Committer.Name)
|
||||
assert.Nil(t, filesResponse.Files[2]) // test delete file
|
||||
})
|
||||
}
|
||||
|
||||
// Test changing files in a new branch
|
||||
|
@ -6,8 +6,9 @@ package integration
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@ -31,16 +32,18 @@ func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA stri
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath
|
||||
return []*api.ContentsResponse{
|
||||
{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: lastCommitSHA,
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Name: path.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: lastCommitSHA,
|
||||
LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
||||
LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@ -33,18 +34,20 @@ func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string)
|
||||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath
|
||||
return &api.ContentsResponse{
|
||||
Name: treePath,
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: lastCommitSHA,
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Name: treePath,
|
||||
Path: treePath,
|
||||
SHA: sha,
|
||||
LastCommitSHA: lastCommitSHA,
|
||||
LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
||||
LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
|
||||
Type: "file",
|
||||
Size: 30,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
|
@ -6,7 +6,7 @@ package integration
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -79,7 +79,7 @@ func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.Chang
|
||||
}
|
||||
}
|
||||
|
||||
func getExpectedFileResponseForRepofilesDelete() *api.FileResponse {
|
||||
func getExpectedFileResponseForRepoFilesDelete() *api.FileResponse {
|
||||
// Just returns fields that don't change, i.e. fields with commit SHAs and dates can't be determined
|
||||
return &api.FileResponse{
|
||||
Content: nil,
|
||||
@ -107,7 +107,7 @@ func getExpectedFileResponseForRepofilesDelete() *api.FileResponse {
|
||||
}
|
||||
}
|
||||
|
||||
func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *api.FileResponse {
|
||||
func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git.Commit) *api.FileResponse {
|
||||
treePath := "new/file.txt"
|
||||
encoding := "base64"
|
||||
content := "VGhpcyBpcyBhIE5FVyBmaWxl"
|
||||
@ -117,18 +117,20 @@ func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
|
||||
return &api.FileResponse{
|
||||
Content: &api.ContentsResponse{
|
||||
Name: filepath.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||
LastCommitSHA: lastCommitSHA,
|
||||
Type: "file",
|
||||
Size: 18,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Name: path.Base(treePath),
|
||||
Path: treePath,
|
||||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
|
||||
LastCommitSHA: lastCommit.ID.String(),
|
||||
LastCommitterDate: lastCommit.Committer.When,
|
||||
LastAuthorDate: lastCommit.Author.When,
|
||||
Type: "file",
|
||||
Size: 18,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
@ -176,7 +178,7 @@ func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *
|
||||
}
|
||||
}
|
||||
|
||||
func getExpectedFileResponseForRepofilesUpdate(commitID, filename, lastCommitSHA string) *api.FileResponse {
|
||||
func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA string, lastCommitterWhen, lastAuthorWhen time.Time) *api.FileResponse {
|
||||
encoding := "base64"
|
||||
content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ=="
|
||||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + filename + "?ref=master"
|
||||
@ -185,18 +187,20 @@ func getExpectedFileResponseForRepofilesUpdate(commitID, filename, lastCommitSHA
|
||||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename
|
||||
return &api.FileResponse{
|
||||
Content: &api.ContentsResponse{
|
||||
Name: filename,
|
||||
Path: filename,
|
||||
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||
LastCommitSHA: lastCommitSHA,
|
||||
Type: "file",
|
||||
Size: 43,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Name: filename,
|
||||
Path: filename,
|
||||
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
|
||||
LastCommitSHA: lastCommitSHA,
|
||||
LastCommitterDate: lastCommitterWhen,
|
||||
LastAuthorDate: lastAuthorWhen,
|
||||
Type: "file",
|
||||
Size: 43,
|
||||
Encoding: &encoding,
|
||||
Content: &content,
|
||||
URL: &selfURL,
|
||||
HTMLURL: &htmlURL,
|
||||
GitURL: &gitURL,
|
||||
DownloadURL: &downloadURL,
|
||||
Links: &api.FileLinksResponse{
|
||||
Self: &selfURL,
|
||||
GitURL: &gitURL,
|
||||
@ -269,7 +273,7 @@ func TestChangeRepoFilesForCreate(t *testing.T) {
|
||||
|
||||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
|
||||
lastCommit, _ := gitRepo.GetCommitByPath("new/file.txt")
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID, lastCommit.ID.String())
|
||||
expectedFileResponse := getExpectedFileResponseForRepoFilesCreate(commitID, lastCommit)
|
||||
assert.NotNil(t, expectedFileResponse)
|
||||
if expectedFileResponse != nil {
|
||||
assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
|
||||
@ -306,7 +310,7 @@ func TestChangeRepoFilesForUpdate(t *testing.T) {
|
||||
|
||||
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String())
|
||||
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
|
||||
assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
|
||||
assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
|
||||
assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
|
||||
@ -342,7 +346,7 @@ func TestChangeRepoFilesForUpdateWithFileMove(t *testing.T) {
|
||||
|
||||
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String())
|
||||
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
|
||||
// assert that the old file no longer exists in the last commit of the branch
|
||||
fromEntry, err := commit.GetTreeEntryByPath(opts.Files[0].FromTreePath)
|
||||
switch err.(type) {
|
||||
@ -393,7 +397,7 @@ func TestChangeRepoFilesWithoutBranchNames(t *testing.T) {
|
||||
|
||||
commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch)
|
||||
lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String())
|
||||
expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
|
||||
assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
|
||||
})
|
||||
}
|
||||
@ -419,7 +423,7 @@ func testDeleteRepoFiles(t *testing.T, u *url.URL) {
|
||||
t.Run("Delete README.md file", func(t *testing.T) {
|
||||
filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts)
|
||||
assert.NoError(t, err)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesDelete()
|
||||
expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
|
||||
assert.NotNil(t, filesResponse)
|
||||
assert.Nil(t, filesResponse.Files[0])
|
||||
assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
|
||||
@ -461,7 +465,7 @@ func testDeleteRepoFilesWithoutBranchNames(t *testing.T, u *url.URL) {
|
||||
t.Run("Delete README.md without Branch Name", func(t *testing.T) {
|
||||
filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts)
|
||||
assert.NoError(t, err)
|
||||
expectedFileResponse := getExpectedFileResponseForRepofilesDelete()
|
||||
expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
|
||||
assert.NotNil(t, filesResponse)
|
||||
assert.Nil(t, filesResponse.Files[0])
|
||||
assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
|
||||
|
@ -511,6 +511,18 @@
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.markup details.frontmatter-content summary {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.markup details.frontmatter-content svg {
|
||||
vertical-align: middle;
|
||||
margin: 0 0.25em;
|
||||
}
|
||||
|
||||
.file-revisions-btn {
|
||||
display: block;
|
||||
float: left;
|
||||
|
Loading…
x
Reference in New Issue
Block a user