mirror of
https://github.com/go-gitea/gitea.git
synced 2024-06-29 01:45:30 +00:00
Compare commits
25 Commits
cbfeba87a6
...
9015908269
Author | SHA1 | Date | |
---|---|---|---|
|
9015908269 | ||
|
5a7376c060 | ||
|
5d14bfa3b4 | ||
|
22ee0ab5ee | ||
|
0d40c0b963 | ||
|
363c123598 | ||
|
4b6eb46e69 | ||
|
0f09c22663 | ||
|
25f3ec5b65 | ||
|
597d1da96b | ||
|
f5dfd7d73c | ||
|
129206da45 | ||
|
f446e3b4ab | ||
|
c70d4f03c9 | ||
|
576e78fa09 | ||
|
1794b8ed78 | ||
|
d0020df4fe | ||
|
c1bae71d20 | ||
|
e55a5cc4bb | ||
|
bf9d3b4cef | ||
|
108f5fa2f4 | ||
|
09e2dac12e | ||
|
cea7be7743 | ||
|
8b33534db7 | ||
|
055e59004d |
|
@ -6,9 +6,20 @@ ignorePatterns:
|
||||||
- /web_src/fomantic
|
- /web_src/fomantic
|
||||||
- /public/assets/js
|
- /public/assets/js
|
||||||
|
|
||||||
|
parser: "@typescript-eslint/parser"
|
||||||
|
|
||||||
parserOptions:
|
parserOptions:
|
||||||
sourceType: module
|
sourceType: module
|
||||||
ecmaVersion: latest
|
ecmaVersion: latest
|
||||||
|
project: true
|
||||||
|
extraFileExtensions: [".vue"]
|
||||||
|
|
||||||
|
settings:
|
||||||
|
import/extensions: [".js", ".ts"]
|
||||||
|
import/parsers:
|
||||||
|
"@typescript-eslint/parser": [".js", ".ts"]
|
||||||
|
import/resolver:
|
||||||
|
typescript: true
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
- "@eslint-community/eslint-plugin-eslint-comments"
|
- "@eslint-community/eslint-plugin-eslint-comments"
|
||||||
|
@ -103,6 +114,21 @@ overrides:
|
||||||
- files: ["web_src/js/modules/fetch.js", "web_src/js/standalone/**/*"]
|
- files: ["web_src/js/modules/fetch.js", "web_src/js/standalone/**/*"]
|
||||||
rules:
|
rules:
|
||||||
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]
|
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]
|
||||||
|
- files: ["**/*.vue"]
|
||||||
|
plugins:
|
||||||
|
- eslint-plugin-vue
|
||||||
|
- eslint-plugin-vue-scoped-css
|
||||||
|
extends:
|
||||||
|
- plugin:vue/vue3-recommended
|
||||||
|
- plugin:vue-scoped-css/vue3-recommended
|
||||||
|
rules:
|
||||||
|
vue/attributes-order: [0]
|
||||||
|
vue/html-closing-bracket-spacing: [2, {startTag: never, endTag: never, selfClosingTag: never}]
|
||||||
|
vue/max-attributes-per-line: [0]
|
||||||
|
vue/singleline-html-element-content-newline: [0]
|
||||||
|
- files: ["tests/e2e/**"]
|
||||||
|
plugins: [eslint-plugin-playwright]
|
||||||
|
extends: plugin:playwright/recommended
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
"@eslint-community/eslint-comments/disable-enable-pair": [2]
|
"@eslint-community/eslint-comments/disable-enable-pair": [2]
|
||||||
|
@ -264,7 +290,7 @@ rules:
|
||||||
i/no-internal-modules: [0]
|
i/no-internal-modules: [0]
|
||||||
i/no-mutable-exports: [0]
|
i/no-mutable-exports: [0]
|
||||||
i/no-named-as-default-member: [0]
|
i/no-named-as-default-member: [0]
|
||||||
i/no-named-as-default: [2]
|
i/no-named-as-default: [0]
|
||||||
i/no-named-default: [0]
|
i/no-named-default: [0]
|
||||||
i/no-named-export: [0]
|
i/no-named-export: [0]
|
||||||
i/no-namespace: [0]
|
i/no-namespace: [0]
|
||||||
|
@ -274,7 +300,7 @@ rules:
|
||||||
i/no-restricted-paths: [0]
|
i/no-restricted-paths: [0]
|
||||||
i/no-self-import: [2]
|
i/no-self-import: [2]
|
||||||
i/no-unassigned-import: [0]
|
i/no-unassigned-import: [0]
|
||||||
i/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$", ^vitest/]}]
|
i/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}]
|
||||||
i/no-unused-modules: [2, {unusedExports: true}]
|
i/no-unused-modules: [2, {unusedExports: true}]
|
||||||
i/no-useless-path-segments: [2, {commonjs: true}]
|
i/no-useless-path-segments: [2, {commonjs: true}]
|
||||||
i/no-webpack-loader-syntax: [2]
|
i/no-webpack-loader-syntax: [2]
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -375,11 +375,13 @@ lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
|
||||||
|
|
||||||
.PHONY: lint-js
|
.PHONY: lint-js
|
||||||
lint-js: node_modules
|
lint-js: node_modules
|
||||||
npx eslint --color --max-warnings=0 --ext js,vue $(ESLINT_FILES)
|
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
|
||||||
|
npx tsc
|
||||||
|
|
||||||
.PHONY: lint-js-fix
|
.PHONY: lint-js-fix
|
||||||
lint-js-fix: node_modules
|
lint-js-fix: node_modules
|
||||||
npx eslint --color --max-warnings=0 --ext js,vue $(ESLINT_FILES) --fix
|
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
|
||||||
|
npx tsc
|
||||||
|
|
||||||
.PHONY: lint-css
|
.PHONY: lint-css
|
||||||
lint-css: node_modules
|
lint-css: node_modules
|
||||||
|
|
|
@ -4,12 +4,67 @@
|
||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"golang.org/x/text/collate"
|
"golang.org/x/text/collate"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func naturalSortGetRune(str string, pos int) (r rune, size int, has bool) {
|
||||||
|
if pos >= len(str) {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
r, size = utf8.DecodeRuneInString(str[pos:])
|
||||||
|
if r == utf8.RuneError {
|
||||||
|
r, size = rune(str[pos]), 1 // if invalid input, treat it as a single byte ascii
|
||||||
|
}
|
||||||
|
return r, size, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func naturalSortAdvance(str string, pos int) (end int, isNumber bool) {
|
||||||
|
end = pos
|
||||||
|
for {
|
||||||
|
r, size, has := naturalSortGetRune(str, end)
|
||||||
|
if !has {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
isCurRuneNum := '0' <= r && r <= '9'
|
||||||
|
if end == pos {
|
||||||
|
isNumber = isCurRuneNum
|
||||||
|
end += size
|
||||||
|
} else if isCurRuneNum == isNumber {
|
||||||
|
end += size
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return end, isNumber
|
||||||
|
}
|
||||||
|
|
||||||
// NaturalSortLess compares two strings so that they could be sorted in natural order
|
// NaturalSortLess compares two strings so that they could be sorted in natural order
|
||||||
func NaturalSortLess(s1, s2 string) bool {
|
func NaturalSortLess(s1, s2 string) bool {
|
||||||
|
// There is a bug in Golang's collate package: https://github.com/golang/go/issues/67997
|
||||||
|
// text/collate: CompareString(collate.Numeric) returns wrong result for "0.0" vs "1.0" #67997
|
||||||
|
// So we need to handle the number parts by ourselves
|
||||||
c := collate.New(language.English, collate.Numeric)
|
c := collate.New(language.English, collate.Numeric)
|
||||||
return c.CompareString(s1, s2) < 0
|
pos1, pos2 := 0, 0
|
||||||
|
for pos1 < len(s1) && pos2 < len(s2) {
|
||||||
|
end1, isNum1 := naturalSortAdvance(s1, pos1)
|
||||||
|
end2, isNum2 := naturalSortAdvance(s2, pos2)
|
||||||
|
part1, part2 := s1[pos1:end1], s2[pos2:end2]
|
||||||
|
if isNum1 && isNum2 {
|
||||||
|
if part1 != part2 {
|
||||||
|
if len(part1) != len(part2) {
|
||||||
|
return len(part1) < len(part2)
|
||||||
|
}
|
||||||
|
return part1 < part2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if cmp := c.CompareString(part1, part2); cmp != 0 {
|
||||||
|
return cmp < 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos1, pos2 = end1, end2
|
||||||
|
}
|
||||||
|
return len(s1) < len(s2)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,21 +10,36 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNaturalSortLess(t *testing.T) {
|
func TestNaturalSortLess(t *testing.T) {
|
||||||
test := func(s1, s2 string, less bool) {
|
testLess := func(s1, s2 string) {
|
||||||
assert.Equal(t, less, NaturalSortLess(s1, s2), "s1=%q, s2=%q", s1, s2)
|
assert.True(t, NaturalSortLess(s1, s2), "s1<s2 should be true: s1=%q, s2=%q", s1, s2)
|
||||||
|
assert.False(t, NaturalSortLess(s2, s1), "s2<s1 should be false: s1=%q, s2=%q", s1, s2)
|
||||||
|
}
|
||||||
|
testEqual := func(s1, s2 string) {
|
||||||
|
assert.False(t, NaturalSortLess(s1, s2), "s1<s2 should be false: s1=%q, s2=%q", s1, s2)
|
||||||
|
assert.False(t, NaturalSortLess(s2, s1), "s2<s1 should be false: s1=%q, s2=%q", s1, s2)
|
||||||
}
|
}
|
||||||
test("v1.20.0", "v1.2.0", false)
|
|
||||||
test("v1.20.0", "v1.29.0", true)
|
|
||||||
test("v1.20.0", "v1.20.0", false)
|
|
||||||
test("abc", "bcd", true)
|
|
||||||
test("a-1-a", "a-1-b", true)
|
|
||||||
test("2", "12", true)
|
|
||||||
test("a", "ab", true)
|
|
||||||
|
|
||||||
test("A", "b", true)
|
testEqual("", "")
|
||||||
test("a", "B", true)
|
testLess("", "a")
|
||||||
|
testLess("", "1")
|
||||||
|
|
||||||
test("cafe", "café", true)
|
testLess("v1.2", "v1.2.0")
|
||||||
test("café", "cafe", false)
|
testLess("v1.2.0", "v1.10.0")
|
||||||
test("caff", "café", false)
|
testLess("v1.20.0", "v1.29.0")
|
||||||
|
testEqual("v1.20.0", "v1.20.0")
|
||||||
|
|
||||||
|
testLess("a", "A")
|
||||||
|
testLess("a", "B")
|
||||||
|
testLess("A", "b")
|
||||||
|
testLess("A", "ab")
|
||||||
|
|
||||||
|
testLess("abc", "bcd")
|
||||||
|
testLess("a-1-a", "a-1-b")
|
||||||
|
testLess("2", "12")
|
||||||
|
|
||||||
|
testLess("cafe", "café")
|
||||||
|
testLess("café", "caff")
|
||||||
|
|
||||||
|
testLess("A-2", "A-11")
|
||||||
|
testLess("0.txt", "1.txt")
|
||||||
}
|
}
|
||||||
|
|
32
modules/cache/cache.go
vendored
32
modules/cache/cache.go
vendored
|
@ -4,6 +4,7 @@
|
||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -35,6 +36,37 @@ func Init() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
testCacheKey = "DefaultCache.TestKey"
|
||||||
|
SlowCacheThreshold = 100 * time.Microsecond
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test() (time.Duration, error) {
|
||||||
|
if defaultCache == nil {
|
||||||
|
return 0, fmt.Errorf("default cache not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
testData := fmt.Sprintf("%x", make([]byte, 500))
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
if err := defaultCache.Delete(testCacheKey); err != nil {
|
||||||
|
return 0, fmt.Errorf("expect cache to delete data based on key if exist but got: %w", err)
|
||||||
|
}
|
||||||
|
if err := defaultCache.Put(testCacheKey, testData, 10); err != nil {
|
||||||
|
return 0, fmt.Errorf("expect cache to store data but got: %w", err)
|
||||||
|
}
|
||||||
|
testVal, hit := defaultCache.Get(testCacheKey)
|
||||||
|
if !hit {
|
||||||
|
return 0, fmt.Errorf("expect cache hit but got none")
|
||||||
|
}
|
||||||
|
if testVal != testData {
|
||||||
|
return 0, fmt.Errorf("expect cache to return same value as stored but got other")
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Since(start), nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetCache returns the currently configured cache
|
// GetCache returns the currently configured cache
|
||||||
func GetCache() StringCache {
|
func GetCache() StringCache {
|
||||||
return defaultCache
|
return defaultCache
|
||||||
|
|
12
modules/cache/cache_test.go
vendored
12
modules/cache/cache_test.go
vendored
|
@ -34,6 +34,18 @@ func TestNewContext(t *testing.T) {
|
||||||
assert.Nil(t, con)
|
assert.Nil(t, con)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTest(t *testing.T) {
|
||||||
|
defaultCache = nil
|
||||||
|
_, err := Test()
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
createTestCache()
|
||||||
|
elapsed, err := Test()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// mem cache should take from 300ns up to 1ms on modern hardware ...
|
||||||
|
assert.Less(t, elapsed, SlowCacheThreshold)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetCache(t *testing.T) {
|
func TestGetCache(t *testing.T) {
|
||||||
createTestCache()
|
createTestCache()
|
||||||
|
|
||||||
|
|
|
@ -144,20 +144,6 @@ func CustomLinkURLSchemes(schemes []string) {
|
||||||
common.LinkRegex, _ = xurls.StrictMatchingScheme(strings.Join(withAuth, "|"))
|
common.LinkRegex, _ = xurls.StrictMatchingScheme(strings.Join(withAuth, "|"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSameDomain checks if given url string has the same hostname as current Gitea instance
|
|
||||||
func IsSameDomain(s string) bool {
|
|
||||||
if strings.HasPrefix(s, "/") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if uapp, err := url.Parse(setting.AppURL); err == nil {
|
|
||||||
if u, err := url.Parse(s); err == nil {
|
|
||||||
return u.Host == uapp.Host
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type postProcessError struct {
|
type postProcessError struct {
|
||||||
context string
|
context string
|
||||||
err error
|
err error
|
||||||
|
@ -429,7 +415,7 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Nod
|
||||||
// We ignore code and pre.
|
// We ignore code and pre.
|
||||||
switch node.Type {
|
switch node.Type {
|
||||||
case html.TextNode:
|
case html.TextNode:
|
||||||
textNode(ctx, procs, node)
|
processTextNodes(ctx, procs, node)
|
||||||
case html.ElementNode:
|
case html.ElementNode:
|
||||||
if node.Data == "img" {
|
if node.Data == "img" {
|
||||||
next := node.NextSibling
|
next := node.NextSibling
|
||||||
|
@ -465,15 +451,16 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Nod
|
||||||
for n := node.FirstChild; n != nil; {
|
for n := node.FirstChild; n != nil; {
|
||||||
n = visitNode(ctx, procs, n)
|
n = visitNode(ctx, procs, n)
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
return node.NextSibling
|
return node.NextSibling
|
||||||
}
|
}
|
||||||
|
|
||||||
// textNode runs the passed node through various processors, in order to handle
|
// processTextNodes runs the passed node through various processors, in order to handle
|
||||||
// all kinds of special links handled by the post-processing.
|
// all kinds of special links handled by the post-processing.
|
||||||
func textNode(ctx *RenderContext, procs []processor, node *html.Node) {
|
func processTextNodes(ctx *RenderContext, procs []processor, node *html.Node) {
|
||||||
for _, processor := range procs {
|
for _, p := range procs {
|
||||||
processor(ctx, node)
|
p(ctx, node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -939,14 +926,11 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
// Path determines the type of link that will be rendered. It's unknown at this point whether
|
// Path determines the type of link that will be rendered. It's unknown at this point whether
|
||||||
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
|
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
|
||||||
// Gitea will redirect on click as appropriate.
|
// Gitea will redirect on click as appropriate.
|
||||||
path := "issues"
|
issuePath := util.Iif(ref.IsPull, "pulls", "issues")
|
||||||
if ref.IsPull {
|
|
||||||
path = "pulls"
|
|
||||||
}
|
|
||||||
if ref.Owner == "" {
|
if ref.Owner == "" {
|
||||||
link = createLink(util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue")
|
link = createLink(util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], issuePath, ref.Issue), reftext, "ref-issue")
|
||||||
} else {
|
} else {
|
||||||
link = createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue")
|
link = createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, issuePath, ref.Issue), reftext, "ref-issue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1207,7 +1191,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.AddCancel(func() {
|
ctx.AddCancel(func() {
|
||||||
closer.Close()
|
_ = closer.Close()
|
||||||
ctx.GitRepo = nil
|
ctx.GitRepo = nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,17 +144,6 @@ func TestRender_CrossReferences(t *testing.T) {
|
||||||
`<p><a href="`+inputURL+`" rel="nofollow"><code>0123456789/foo.txt (L2-L3)</code></a></p>`)
|
`<p><a href="`+inputURL+`" rel="nofollow"><code>0123456789/foo.txt (L2-L3)</code></a></p>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMisc_IsSameDomain(t *testing.T) {
|
|
||||||
setting.AppURL = markup.TestAppURL
|
|
||||||
|
|
||||||
sha := "b6dd6210eaebc915fd5be5579c58cce4da2e2579"
|
|
||||||
commit := util.URLJoin(markup.TestRepoURL, "commit", sha)
|
|
||||||
|
|
||||||
assert.True(t, markup.IsSameDomain(commit))
|
|
||||||
assert.False(t, markup.IsSameDomain("http://google.com/ncr"))
|
|
||||||
assert.False(t, markup.IsSameDomain("favicon.ico"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRender_links(t *testing.T) {
|
func TestRender_links(t *testing.T) {
|
||||||
setting.AppURL = markup.TestAppURL
|
setting.AppURL = markup.TestAppURL
|
||||||
|
|
||||||
|
|
|
@ -86,10 +86,10 @@ type RenderContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Links struct {
|
type Links struct {
|
||||||
AbsolutePrefix bool
|
AbsolutePrefix bool // add absolute URL prefix to auto-resolved links like "#issue", but not for pre-provided links and medias
|
||||||
Base string
|
Base string // base prefix for pre-provided links and medias (images, videos)
|
||||||
BranchPath string
|
BranchPath string // actually it is the ref path, eg: "branch/features/feat-12", "tag/v1.0"
|
||||||
TreePath string
|
TreePath string // the dir of the file, eg: "doc" if the file "doc/CHANGE.md" is being rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Links) Prefix() string {
|
func (l *Links) Prefix() string {
|
||||||
|
|
|
@ -25,7 +25,8 @@ type MarkupOption struct {
|
||||||
//
|
//
|
||||||
// in: body
|
// in: body
|
||||||
Mode string
|
Mode string
|
||||||
// Context to render
|
// URL path for rendering issue, media and file links
|
||||||
|
// Expected format: /subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}
|
||||||
//
|
//
|
||||||
// in: body
|
// in: body
|
||||||
Context string
|
Context string
|
||||||
|
@ -53,7 +54,8 @@ type MarkdownOption struct {
|
||||||
//
|
//
|
||||||
// in: body
|
// in: body
|
||||||
Mode string
|
Mode string
|
||||||
// Context to render
|
// URL path for rendering issue, media and file links
|
||||||
|
// Expected format: /subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}
|
||||||
//
|
//
|
||||||
// in: body
|
// in: body
|
||||||
Context string
|
Context string
|
||||||
|
|
47
options/gitignore/IAR
Normal file
47
options/gitignore/IAR
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Compiled binaries
|
||||||
|
*.o
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.hex
|
||||||
|
*.map
|
||||||
|
*.out
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Trash
|
||||||
|
*.bak
|
||||||
|
thumbs.db
|
||||||
|
*.~*
|
||||||
|
|
||||||
|
# IAR Settings
|
||||||
|
**/settings/*.crun
|
||||||
|
**/settings/*.dbgdt
|
||||||
|
**/settings/*.cspy
|
||||||
|
**/settings/*.cspy.*
|
||||||
|
**/settings/*.xcl
|
||||||
|
**/settings/*.dni
|
||||||
|
**/settings/*.wsdt
|
||||||
|
**/settings/*.wspos
|
||||||
|
|
||||||
|
# IAR Debug Exe
|
||||||
|
**/Exe/*.sim
|
||||||
|
|
||||||
|
# IAR Debug Obj
|
||||||
|
**/Obj/*.pbd
|
||||||
|
**/Obj/*.pbd.*
|
||||||
|
**/Obj/*.pbi
|
||||||
|
**/Obj/*.pbi.*
|
||||||
|
|
||||||
|
# IAR project "Debug" directory
|
||||||
|
Debug/
|
||||||
|
|
||||||
|
# IAR project "Release" directory
|
||||||
|
Release/
|
||||||
|
|
||||||
|
# IAR project settings directory
|
||||||
|
settings/
|
||||||
|
|
||||||
|
# IAR backup files
|
||||||
|
Backup*
|
||||||
|
|
||||||
|
# IAR .dep files
|
||||||
|
*.dep
|
|
@ -42,10 +42,3 @@ fastlane/report.xml
|
||||||
fastlane/Preview.html
|
fastlane/Preview.html
|
||||||
fastlane/screenshots/**/*.png
|
fastlane/screenshots/**/*.png
|
||||||
fastlane/test_output
|
fastlane/test_output
|
||||||
|
|
||||||
# Code Injection
|
|
||||||
#
|
|
||||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
|
||||||
# https://github.com/johnno1962/injectionforxcode
|
|
||||||
|
|
||||||
iOSInjectionProject/
|
|
||||||
|
|
|
@ -35,6 +35,3 @@ override.tf.json
|
||||||
# Ignore CLI configuration files
|
# Ignore CLI configuration files
|
||||||
.terraformrc
|
.terraformrc
|
||||||
terraform.rc
|
terraform.rc
|
||||||
|
|
||||||
# Ignore hcl file
|
|
||||||
.terraform.lock.hcl
|
|
||||||
|
|
|
@ -93,6 +93,7 @@ remove_all = Remove All
|
||||||
remove_label_str = Remove item "%s"
|
remove_label_str = Remove item "%s"
|
||||||
edit = Edit
|
edit = Edit
|
||||||
view = View
|
view = View
|
||||||
|
test = Test
|
||||||
|
|
||||||
enabled = Enabled
|
enabled = Enabled
|
||||||
disabled = Disabled
|
disabled = Disabled
|
||||||
|
@ -3225,6 +3226,10 @@ config.cache_adapter = Cache Adapter
|
||||||
config.cache_interval = Cache Interval
|
config.cache_interval = Cache Interval
|
||||||
config.cache_conn = Cache Connection
|
config.cache_conn = Cache Connection
|
||||||
config.cache_item_ttl = Cache Item TTL
|
config.cache_item_ttl = Cache Item TTL
|
||||||
|
config.cache_test = Test Cache
|
||||||
|
config.cache_test_failed = Failed to probe the cache: %v.
|
||||||
|
config.cache_test_slow = Cache test successful, but response is slow: %s.
|
||||||
|
config.cache_test_succeeded = Cache test successful, got a response in %s.
|
||||||
|
|
||||||
config.session_config = Session Configuration
|
config.session_config = Session Configuration
|
||||||
config.session_provider = Session Provider
|
config.session_provider = Session Provider
|
||||||
|
|
|
@ -1238,6 +1238,7 @@ file_view_rendered=レンダリング表示
|
||||||
file_view_raw=Rawデータを見る
|
file_view_raw=Rawデータを見る
|
||||||
file_permalink=パーマリンク
|
file_permalink=パーマリンク
|
||||||
file_too_large=このファイルは大きすぎるため、表示できません。
|
file_too_large=このファイルは大きすぎるため、表示できません。
|
||||||
|
file_is_empty=ファイルは空です。
|
||||||
code_preview_line_from_to=%[1]d 行目から %[2]d 行目 in %[3]s
|
code_preview_line_from_to=%[1]d 行目から %[2]d 行目 in %[3]s
|
||||||
code_preview_line_in=%[1]d 行目 in %[2]s
|
code_preview_line_in=%[1]d 行目 in %[2]s
|
||||||
invisible_runes_header=このファイルには不可視のUnicode文字が含まれています
|
invisible_runes_header=このファイルには不可視のUnicode文字が含まれています
|
||||||
|
@ -1442,6 +1443,7 @@ issues.new.clear_assignees=担当者をクリア
|
||||||
issues.new.no_assignees=担当者なし
|
issues.new.no_assignees=担当者なし
|
||||||
issues.new.no_reviewers=レビューアなし
|
issues.new.no_reviewers=レビューアなし
|
||||||
issues.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、イシューを作成できません。
|
issues.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、イシューを作成できません。
|
||||||
|
issues.edit.already_changed=イシューの変更を保存できません。 他のユーザーによって内容がすでに変更されているようです。 変更を上書きしないようにするため、ページを更新してからもう一度編集してください
|
||||||
issues.edit.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、内容を編集できません。
|
issues.edit.blocked_user=投稿者またはリポジトリのオーナーがあなたをブロックしているため、内容を編集できません。
|
||||||
issues.choose.get_started=始める
|
issues.choose.get_started=始める
|
||||||
issues.choose.open_external_link=オープン
|
issues.choose.open_external_link=オープン
|
||||||
|
@ -1757,6 +1759,7 @@ compare.compare_head=比較
|
||||||
pulls.desc=プルリクエストとコードレビューの有効化。
|
pulls.desc=プルリクエストとコードレビューの有効化。
|
||||||
pulls.new=新しいプルリクエスト
|
pulls.new=新しいプルリクエスト
|
||||||
pulls.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、プルリクエストを作成できません。
|
pulls.new.blocked_user=リポジトリのオーナーがあなたをブロックしているため、プルリクエストを作成できません。
|
||||||
|
pulls.edit.already_changed=プルリクエストの変更を保存できません。 他のユーザーによって内容がすでに変更されているようです。 変更を上書きしないようにするため、ページを更新してからもう一度編集してください
|
||||||
pulls.view=プルリクエストを表示
|
pulls.view=プルリクエストを表示
|
||||||
pulls.compare_changes=新規プルリクエスト
|
pulls.compare_changes=新規プルリクエスト
|
||||||
pulls.allow_edits_from_maintainers=メンテナーからの編集を許可する
|
pulls.allow_edits_from_maintainers=メンテナーからの編集を許可する
|
||||||
|
@ -1902,6 +1905,7 @@ pulls.recently_pushed_new_branches=%[2]s 、あなたはブランチ <strong>%[1
|
||||||
|
|
||||||
pull.deleted_branch=(削除済み):%s
|
pull.deleted_branch=(削除済み):%s
|
||||||
|
|
||||||
|
comments.edit.already_changed=コメントの変更を保存できません。 他のユーザーによって内容がすでに変更されているようです。 変更を上書きしないようにするため、ページを更新してからもう一度編集してください
|
||||||
|
|
||||||
milestones.new=新しいマイルストーン
|
milestones.new=新しいマイルストーン
|
||||||
milestones.closed=%s にクローズ
|
milestones.closed=%s にクローズ
|
||||||
|
|
155
package-lock.json
generated
155
package-lock.json
generated
|
@ -68,14 +68,17 @@
|
||||||
"@stoplight/spectral-cli": "6.11.1",
|
"@stoplight/spectral-cli": "6.11.1",
|
||||||
"@stylistic/eslint-plugin-js": "2.1.0",
|
"@stylistic/eslint-plugin-js": "2.1.0",
|
||||||
"@stylistic/stylelint-plugin": "2.1.2",
|
"@stylistic/stylelint-plugin": "2.1.2",
|
||||||
|
"@typescript-eslint/parser": "7.13.1",
|
||||||
"@vitejs/plugin-vue": "5.0.4",
|
"@vitejs/plugin-vue": "5.0.4",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
|
"eslint-import-resolver-typescript": "3.6.1",
|
||||||
"eslint-plugin-array-func": "4.0.0",
|
"eslint-plugin-array-func": "4.0.0",
|
||||||
"eslint-plugin-github": "5.0.0-2",
|
"eslint-plugin-github": "5.0.0-2",
|
||||||
"eslint-plugin-i": "2.29.1",
|
"eslint-plugin-i": "2.29.1",
|
||||||
"eslint-plugin-jquery": "1.5.1",
|
"eslint-plugin-jquery": "1.5.1",
|
||||||
"eslint-plugin-no-jquery": "2.7.0",
|
"eslint-plugin-no-jquery": "2.7.0",
|
||||||
"eslint-plugin-no-use-extend-native": "0.5.0",
|
"eslint-plugin-no-use-extend-native": "0.5.0",
|
||||||
|
"eslint-plugin-playwright": "1.6.2",
|
||||||
"eslint-plugin-regexp": "2.6.0",
|
"eslint-plugin-regexp": "2.6.0",
|
||||||
"eslint-plugin-sonarjs": "1.0.3",
|
"eslint-plugin-sonarjs": "1.0.3",
|
||||||
"eslint-plugin-unicorn": "53.0.0",
|
"eslint-plugin-unicorn": "53.0.0",
|
||||||
|
@ -92,6 +95,7 @@
|
||||||
"stylelint-declaration-strict-value": "1.10.4",
|
"stylelint-declaration-strict-value": "1.10.4",
|
||||||
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
||||||
"svgo": "3.3.2",
|
"svgo": "3.3.2",
|
||||||
|
"typescript": "5.4.5",
|
||||||
"updates": "16.1.1",
|
"updates": "16.1.1",
|
||||||
"vite-string-plugin": "1.3.1",
|
"vite-string-plugin": "1.3.1",
|
||||||
"vitest": "1.6.0"
|
"vitest": "1.6.0"
|
||||||
|
@ -2396,15 +2400,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "7.10.0",
|
"version": "7.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz",
|
||||||
"integrity": "sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==",
|
"integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "7.10.0",
|
"@typescript-eslint/scope-manager": "7.13.1",
|
||||||
"@typescript-eslint/types": "7.10.0",
|
"@typescript-eslint/types": "7.13.1",
|
||||||
"@typescript-eslint/typescript-estree": "7.10.0",
|
"@typescript-eslint/typescript-estree": "7.13.1",
|
||||||
"@typescript-eslint/visitor-keys": "7.10.0",
|
"@typescript-eslint/visitor-keys": "7.13.1",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2423,6 +2427,93 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
|
||||||
|
"version": "7.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz",
|
||||||
|
"integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/types": "7.13.1",
|
||||||
|
"@typescript-eslint/visitor-keys": "7.13.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
|
||||||
|
"version": "7.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz",
|
||||||
|
"integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
|
||||||
|
"version": "7.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz",
|
||||||
|
"integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/types": "7.13.1",
|
||||||
|
"@typescript-eslint/visitor-keys": "7.13.1",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"globby": "^11.1.0",
|
||||||
|
"is-glob": "^4.0.3",
|
||||||
|
"minimatch": "^9.0.4",
|
||||||
|
"semver": "^7.6.0",
|
||||||
|
"ts-api-utils": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
|
||||||
|
"version": "7.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz",
|
||||||
|
"integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/types": "7.13.1",
|
||||||
|
"eslint-visitor-keys": "^3.4.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": {
|
||||||
|
"version": "3.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
|
||||||
|
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "7.10.0",
|
"version": "7.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz",
|
||||||
|
@ -5338,6 +5429,31 @@
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-import-resolver-typescript": {
|
||||||
|
"version": "3.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz",
|
||||||
|
"integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"enhanced-resolve": "^5.12.0",
|
||||||
|
"eslint-module-utils": "^2.7.4",
|
||||||
|
"fast-glob": "^3.3.1",
|
||||||
|
"get-tsconfig": "^4.5.0",
|
||||||
|
"is-core-module": "^2.11.0",
|
||||||
|
"is-glob": "^4.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": "*",
|
||||||
|
"eslint-plugin-import": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-module-utils": {
|
"node_modules/eslint-module-utils": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
|
||||||
|
@ -5695,6 +5811,30 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-playwright": {
|
||||||
|
"version": "1.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-1.6.2.tgz",
|
||||||
|
"integrity": "sha512-mraN4Em3b5jLt01q7qWPyLg0Q5v3KAWfJSlEWwldyUXoa7DSPrBR4k6B6LROLqipsG8ndkwWMdjl1Ffdh15tag==",
|
||||||
|
"dev": true,
|
||||||
|
"workspaces": [
|
||||||
|
"examples"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"globals": "^13.23.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.6.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=8.40.0",
|
||||||
|
"eslint-plugin-jest": ">=25"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"eslint-plugin-jest": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-prettier": {
|
"node_modules/eslint-plugin-prettier": {
|
||||||
"version": "5.1.3",
|
"version": "5.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
|
||||||
|
@ -11865,7 +12005,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||||
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
|
@ -67,14 +67,17 @@
|
||||||
"@stoplight/spectral-cli": "6.11.1",
|
"@stoplight/spectral-cli": "6.11.1",
|
||||||
"@stylistic/eslint-plugin-js": "2.1.0",
|
"@stylistic/eslint-plugin-js": "2.1.0",
|
||||||
"@stylistic/stylelint-plugin": "2.1.2",
|
"@stylistic/stylelint-plugin": "2.1.2",
|
||||||
|
"@typescript-eslint/parser": "7.13.1",
|
||||||
"@vitejs/plugin-vue": "5.0.4",
|
"@vitejs/plugin-vue": "5.0.4",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
|
"eslint-import-resolver-typescript": "3.6.1",
|
||||||
"eslint-plugin-array-func": "4.0.0",
|
"eslint-plugin-array-func": "4.0.0",
|
||||||
"eslint-plugin-github": "5.0.0-2",
|
"eslint-plugin-github": "5.0.0-2",
|
||||||
"eslint-plugin-i": "2.29.1",
|
"eslint-plugin-i": "2.29.1",
|
||||||
"eslint-plugin-jquery": "1.5.1",
|
"eslint-plugin-jquery": "1.5.1",
|
||||||
"eslint-plugin-no-jquery": "2.7.0",
|
"eslint-plugin-no-jquery": "2.7.0",
|
||||||
"eslint-plugin-no-use-extend-native": "0.5.0",
|
"eslint-plugin-no-use-extend-native": "0.5.0",
|
||||||
|
"eslint-plugin-playwright": "1.6.2",
|
||||||
"eslint-plugin-regexp": "2.6.0",
|
"eslint-plugin-regexp": "2.6.0",
|
||||||
"eslint-plugin-sonarjs": "1.0.3",
|
"eslint-plugin-sonarjs": "1.0.3",
|
||||||
"eslint-plugin-unicorn": "53.0.0",
|
"eslint-plugin-unicorn": "53.0.0",
|
||||||
|
@ -91,6 +94,7 @@
|
||||||
"stylelint-declaration-strict-value": "1.10.4",
|
"stylelint-declaration-strict-value": "1.10.4",
|
||||||
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
||||||
"svgo": "3.3.2",
|
"svgo": "3.3.2",
|
||||||
|
"typescript": "5.4.5",
|
||||||
"updates": "16.1.1",
|
"updates": "16.1.1",
|
||||||
"vite-string-plugin": "1.3.1",
|
"vite-string-plugin": "1.3.1",
|
||||||
"vitest": "1.6.0"
|
"vitest": "1.6.0"
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
// @ts-check
|
|
||||||
import {devices} from '@playwright/test';
|
import {devices} from '@playwright/test';
|
||||||
|
import {env} from 'node:process';
|
||||||
|
|
||||||
const BASE_URL = process.env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
|
const BASE_URL = env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://playwright.dev/docs/test-configuration
|
|
||||||
* @type {import('@playwright/test').PlaywrightTestConfig}
|
|
||||||
*/
|
|
||||||
export default {
|
export default {
|
||||||
testDir: './tests/e2e/',
|
testDir: './tests/e2e/',
|
||||||
testMatch: /.*\.test\.e2e\.js/, // Match any .test.e2e.js files
|
testMatch: /.*\.test\.e2e\.ts/, // Match any .test.e2e.ts files
|
||||||
|
|
||||||
/* Maximum time one test can run for. */
|
/* Maximum time one test can run for. */
|
||||||
timeout: 30 * 1000,
|
timeout: 30 * 1000,
|
||||||
|
@ -24,13 +20,13 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
forbidOnly: Boolean(process.env.CI),
|
forbidOnly: Boolean(env.CI),
|
||||||
|
|
||||||
/* Retry on CI only */
|
/* Retry on CI only */
|
||||||
retries: process.env.CI ? 2 : 0,
|
retries: env.CI ? 2 : 0,
|
||||||
|
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: process.env.CI ? 'list' : [['list'], ['html', {outputFolder: 'tests/e2e/reports/', open: 'never'}]],
|
reporter: env.CI ? 'list' : [['list'], ['html', {outputFolder: 'tests/e2e/reports/', open: 'never'}]],
|
||||||
|
|
||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
use: {
|
use: {
|
|
@ -588,6 +588,8 @@ func CommonRoutes() *web.Route {
|
||||||
r.Get("/prerelease_specs.4.8.gz", rubygems.EnumeratePackagesPreRelease)
|
r.Get("/prerelease_specs.4.8.gz", rubygems.EnumeratePackagesPreRelease)
|
||||||
r.Get("/quick/Marshal.4.8/{filename}", rubygems.ServePackageSpecification)
|
r.Get("/quick/Marshal.4.8/{filename}", rubygems.ServePackageSpecification)
|
||||||
r.Get("/gems/{filename}", rubygems.DownloadPackageFile)
|
r.Get("/gems/{filename}", rubygems.DownloadPackageFile)
|
||||||
|
r.Get("/info/{packagename}", rubygems.GetPackageInfo)
|
||||||
|
r.Get("/versions", rubygems.GetAllPackagesVersions)
|
||||||
r.Group("/api/v1/gems", func() {
|
r.Group("/api/v1/gems", func() {
|
||||||
r.Post("/", rubygems.UploadPackageFile)
|
r.Post("/", rubygems.UploadPackageFile)
|
||||||
r.Delete("/yank", rubygems.DeletePackage)
|
r.Delete("/yank", rubygems.DeletePackage)
|
||||||
|
|
|
@ -6,6 +6,7 @@ package rubygems
|
||||||
import (
|
import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"compress/zlib"
|
"compress/zlib"
|
||||||
|
"crypto/md5"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -227,12 +228,7 @@ func UploadPackageFile(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var filename string
|
filename := makeGemFullFileName(rp.Name, rp.Version, rp.Metadata.Platform)
|
||||||
if rp.Metadata.Platform == "" || rp.Metadata.Platform == "ruby" {
|
|
||||||
filename = strings.ToLower(fmt.Sprintf("%s-%s.gem", rp.Name, rp.Version))
|
|
||||||
} else {
|
|
||||||
filename = strings.ToLower(fmt.Sprintf("%s-%s-%s.gem", rp.Name, rp.Version, rp.Metadata.Platform))
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = packages_service.CreatePackageAndAddFile(
|
_, _, err = packages_service.CreatePackageAndAddFile(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -300,6 +296,136 @@ func DeletePackage(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPackageInfo returns a custom text based format for the single rubygem with a line for each version of the rubygem
|
||||||
|
// ref: https://guides.rubygems.org/rubygems-org-compact-index-api/
|
||||||
|
func GetPackageInfo(ctx *context.Context) {
|
||||||
|
packageName := ctx.Params("packagename")
|
||||||
|
versions, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems, packageName)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(versions) == 0 {
|
||||||
|
apiError(ctx, http.StatusNotFound, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
infoContent, err := makePackageInfo(ctx, versions)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.PlainText(http.StatusOK, infoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllPackagesVersions returns a custom text based format containing information about all versions of all rubygems.
|
||||||
|
// ref: https://guides.rubygems.org/rubygems-org-compact-index-api/
|
||||||
|
func GetAllPackagesVersions(ctx *context.Context) {
|
||||||
|
packages, err := packages_model.GetPackagesByType(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &strings.Builder{}
|
||||||
|
out.WriteString("---\n")
|
||||||
|
for _, pkg := range packages {
|
||||||
|
versions, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems, pkg.Name)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(versions) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := makePackageInfo(ctx, versions)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// format: RUBYGEM [-]VERSION_PLATFORM[,VERSION_PLATFORM],...] MD5
|
||||||
|
_, _ = fmt.Fprintf(out, "%s ", pkg.Name)
|
||||||
|
for i, v := range versions {
|
||||||
|
sep := util.Iif(i == len(versions)-1, "", ",")
|
||||||
|
_, _ = fmt.Fprintf(out, "%s%s", v.Version, sep)
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprintf(out, " %x\n", md5.Sum([]byte(info)))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.PlainText(http.StatusOK, out.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePackageVersionRequirements(prefix string, reqs []rubygems_module.VersionRequirement, out *strings.Builder) {
|
||||||
|
out.WriteString(prefix)
|
||||||
|
if len(reqs) == 0 {
|
||||||
|
reqs = []rubygems_module.VersionRequirement{{Restriction: ">=", Version: "0"}}
|
||||||
|
}
|
||||||
|
for i, req := range reqs {
|
||||||
|
sep := util.Iif(i == 0, "", "&")
|
||||||
|
_, _ = fmt.Fprintf(out, "%s%s %s", sep, req.Restriction, req.Version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePackageVersionDependency(ctx *context.Context, version *packages_model.PackageVersion) (string, error) {
|
||||||
|
// format: VERSION[-PLATFORM] [DEPENDENCY[,DEPENDENCY,...]]|REQUIREMENT[,REQUIREMENT,...]
|
||||||
|
// DEPENDENCY: GEM:CONSTRAINT[&CONSTRAINT]
|
||||||
|
// REQUIREMENT: KEY:VALUE (always contains "checksum")
|
||||||
|
pd, err := packages_model.GetPackageDescriptor(ctx, version)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata := pd.Metadata.(*rubygems_module.Metadata)
|
||||||
|
fullFilename := makeGemFullFileName(pd.Package.Name, version.Version, metadata.Platform)
|
||||||
|
file, err := packages_model.GetFileForVersionByName(ctx, version.ID, fullFilename, "")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
blob, err := packages_model.GetBlobByID(ctx, file.BlobID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &strings.Builder{}
|
||||||
|
buf.WriteString(version.Version)
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
for i, dep := range metadata.RuntimeDependencies {
|
||||||
|
sep := util.Iif(i == 0, "", ",")
|
||||||
|
writePackageVersionRequirements(fmt.Sprintf("%s%s:", sep, dep.Name), dep.Version, buf)
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprintf(buf, "|checksum:%s", blob.HashSHA256)
|
||||||
|
if len(metadata.RequiredRubyVersion) != 0 {
|
||||||
|
writePackageVersionRequirements(",ruby:", metadata.RequiredRubyVersion, buf)
|
||||||
|
}
|
||||||
|
if len(metadata.RequiredRubygemsVersion) != 0 {
|
||||||
|
writePackageVersionRequirements(",rubygems:", metadata.RequiredRubygemsVersion, buf)
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePackageInfo(ctx *context.Context, versions []*packages_model.PackageVersion) (string, error) {
|
||||||
|
ret := "---\n"
|
||||||
|
for _, v := range versions {
|
||||||
|
dep, err := makePackageVersionDependency(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ret += dep + "\n"
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeGemFullFileName(gemName, version, platform string) string {
|
||||||
|
var basename string
|
||||||
|
if platform == "" || platform == "ruby" {
|
||||||
|
basename = fmt.Sprintf("%s-%s", gemName, version)
|
||||||
|
} else {
|
||||||
|
basename = fmt.Sprintf("%s-%s-%s", gemName, version, platform)
|
||||||
|
}
|
||||||
|
return strings.ToLower(basename) + ".gem"
|
||||||
|
}
|
||||||
|
|
||||||
func getVersionsByFilename(ctx *context.Context, filename string) ([]*packages_model.PackageVersion, error) {
|
func getVersionsByFilename(ctx *context.Context, filename string) ([]*packages_model.PackageVersion, error) {
|
||||||
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
||||||
OwnerID: ctx.Package.Owner.ID,
|
OwnerID: ctx.Package.Owner.ID,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
go_context "context"
|
go_context "context"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -19,36 +20,40 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const AppURL = "http://localhost:3000/"
|
||||||
AppURL = "http://localhost:3000/"
|
|
||||||
Repo = "gogits/gogs"
|
|
||||||
FullURL = AppURL + Repo + "/"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) {
|
func testRenderMarkup(t *testing.T, mode string, wiki bool, filePath, text, expectedBody string, expectedCode int) {
|
||||||
setting.AppURL = AppURL
|
setting.AppURL = AppURL
|
||||||
|
context := "/gogits/gogs"
|
||||||
|
if !wiki {
|
||||||
|
context += path.Join("/src/branch/main", path.Dir(filePath))
|
||||||
|
}
|
||||||
options := api.MarkupOption{
|
options := api.MarkupOption{
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
Text: text,
|
Text: text,
|
||||||
Context: Repo,
|
Context: context,
|
||||||
Wiki: true,
|
Wiki: wiki,
|
||||||
FilePath: filePath,
|
FilePath: filePath,
|
||||||
}
|
}
|
||||||
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markup")
|
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markup")
|
||||||
web.SetForm(ctx, &options)
|
web.SetForm(ctx, &options)
|
||||||
Markup(ctx)
|
Markup(ctx)
|
||||||
assert.Equal(t, responseBody, resp.Body.String())
|
assert.Equal(t, expectedBody, resp.Body.String())
|
||||||
assert.Equal(t, responseCode, resp.Code)
|
assert.Equal(t, expectedCode, resp.Code)
|
||||||
resp.Body.Reset()
|
resp.Body.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRenderMarkdown(t *testing.T, mode, text, responseBody string, responseCode int) {
|
func testRenderMarkdown(t *testing.T, mode string, wiki bool, text, responseBody string, responseCode int) {
|
||||||
setting.AppURL = AppURL
|
setting.AppURL = AppURL
|
||||||
|
context := "/gogits/gogs"
|
||||||
|
if !wiki {
|
||||||
|
context += "/src/branch/main"
|
||||||
|
}
|
||||||
options := api.MarkdownOption{
|
options := api.MarkdownOption{
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
Text: text,
|
Text: text,
|
||||||
Context: Repo,
|
Context: context,
|
||||||
Wiki: true,
|
Wiki: wiki,
|
||||||
}
|
}
|
||||||
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown")
|
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown")
|
||||||
web.SetForm(ctx, &options)
|
web.SetForm(ctx, &options)
|
||||||
|
@ -65,7 +70,7 @@ func TestAPI_RenderGFM(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
testCasesCommon := []string{
|
testCasesWiki := []string{
|
||||||
// dear imgui wiki markdown extract: special wiki syntax
|
// dear imgui wiki markdown extract: special wiki syntax
|
||||||
`Wiki! Enjoy :)
|
`Wiki! Enjoy :)
|
||||||
- [[Links, Language bindings, Engine bindings|Links]]
|
- [[Links, Language bindings, Engine bindings|Links]]
|
||||||
|
@ -74,20 +79,20 @@ func TestAPI_RenderGFM(t *testing.T) {
|
||||||
// rendered
|
// rendered
|
||||||
`<p>Wiki! Enjoy :)</p>
|
`<p>Wiki! Enjoy :)</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="` + FullURL + `wiki/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
|
<li><a href="http://localhost:3000/gogits/gogs/wiki/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
|
||||||
<li><a href="` + FullURL + `wiki/Tips" rel="nofollow">Tips</a></li>
|
<li><a href="http://localhost:3000/gogits/gogs/wiki/Tips" rel="nofollow">Tips</a></li>
|
||||||
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="https://github.com/ocornut/imgui/issues/786" rel="nofollow">https://github.com/ocornut/imgui/issues/786</a></li>
|
<li>Bezier widget (by <a href="http://localhost:3000/r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="https://github.com/ocornut/imgui/issues/786" rel="nofollow">https://github.com/ocornut/imgui/issues/786</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
`,
|
`,
|
||||||
// Guard wiki sidebar: special syntax
|
// Guard wiki sidebar: special syntax
|
||||||
`[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`,
|
`[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`,
|
||||||
// rendered
|
// rendered
|
||||||
`<p><a href="` + FullURL + `wiki/Guardfile-DSL---Configuring-Guard" rel="nofollow">Guardfile-DSL / Configuring-Guard</a></p>
|
`<p><a href="http://localhost:3000/gogits/gogs/wiki/Guardfile-DSL---Configuring-Guard" rel="nofollow">Guardfile-DSL / Configuring-Guard</a></p>
|
||||||
`,
|
`,
|
||||||
// special syntax
|
// special syntax
|
||||||
`[[Name|Link]]`,
|
`[[Name|Link]]`,
|
||||||
// rendered
|
// rendered
|
||||||
`<p><a href="` + FullURL + `wiki/Link" rel="nofollow">Name</a></p>
|
`<p><a href="http://localhost:3000/gogits/gogs/wiki/Link" rel="nofollow">Name</a></p>
|
||||||
`,
|
`,
|
||||||
// empty
|
// empty
|
||||||
``,
|
``,
|
||||||
|
@ -95,7 +100,7 @@ func TestAPI_RenderGFM(t *testing.T) {
|
||||||
``,
|
``,
|
||||||
}
|
}
|
||||||
|
|
||||||
testCasesDocument := []string{
|
testCasesWikiDocument := []string{
|
||||||
// wine-staging wiki home extract: special wiki syntax, images
|
// wine-staging wiki home extract: special wiki syntax, images
|
||||||
`## What is Wine Staging?
|
`## What is Wine Staging?
|
||||||
**Wine Staging** on website [wine-staging.com](http://wine-staging.com).
|
**Wine Staging** on website [wine-staging.com](http://wine-staging.com).
|
||||||
|
@ -111,31 +116,48 @@ Here are some links to the most important topics. You can find the full list of
|
||||||
<p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p>
|
<p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p>
|
||||||
<h2 id="user-content-quick-links">Quick Links</h2>
|
<h2 id="user-content-quick-links">Quick Links</h2>
|
||||||
<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
|
<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
|
||||||
<p><a href="` + FullURL + `wiki/Configuration" rel="nofollow">Configuration</a>
|
<p><a href="http://localhost:3000/gogits/gogs/wiki/Configuration" rel="nofollow">Configuration</a>
|
||||||
<a href="` + FullURL + `wiki/raw/images/icon-bug.png" rel="nofollow"><img src="` + FullURL + `wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p>
|
<a href="http://localhost:3000/gogits/gogs/wiki/raw/images/icon-bug.png" rel="nofollow"><img src="http://localhost:3000/gogits/gogs/wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p>
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(testCasesCommon); i += 2 {
|
for i := 0; i < len(testCasesWiki); i += 2 {
|
||||||
text := testCasesCommon[i]
|
text := testCasesWiki[i]
|
||||||
response := testCasesCommon[i+1]
|
response := testCasesWiki[i+1]
|
||||||
testRenderMarkdown(t, "gfm", text, response, http.StatusOK)
|
testRenderMarkdown(t, "gfm", true, text, response, http.StatusOK)
|
||||||
testRenderMarkup(t, "gfm", "", text, response, http.StatusOK)
|
testRenderMarkup(t, "gfm", true, "", text, response, http.StatusOK)
|
||||||
testRenderMarkdown(t, "comment", text, response, http.StatusOK)
|
testRenderMarkdown(t, "comment", true, text, response, http.StatusOK)
|
||||||
testRenderMarkup(t, "comment", "", text, response, http.StatusOK)
|
testRenderMarkup(t, "comment", true, "", text, response, http.StatusOK)
|
||||||
testRenderMarkup(t, "file", "path/test.md", text, response, http.StatusOK)
|
testRenderMarkup(t, "file", true, "path/test.md", text, response, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(testCasesDocument); i += 2 {
|
for i := 0; i < len(testCasesWikiDocument); i += 2 {
|
||||||
text := testCasesDocument[i]
|
text := testCasesWikiDocument[i]
|
||||||
response := testCasesDocument[i+1]
|
response := testCasesWikiDocument[i+1]
|
||||||
testRenderMarkdown(t, "gfm", text, response, http.StatusOK)
|
testRenderMarkdown(t, "gfm", true, text, response, http.StatusOK)
|
||||||
testRenderMarkup(t, "gfm", "", text, response, http.StatusOK)
|
testRenderMarkup(t, "gfm", true, "", text, response, http.StatusOK)
|
||||||
testRenderMarkup(t, "file", "path/test.md", text, response, http.StatusOK)
|
testRenderMarkup(t, "file", true, "path/test.md", text, response, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
testRenderMarkup(t, "file", "path/test.unknown", "## Test", "Unsupported render extension: .unknown\n", http.StatusUnprocessableEntity)
|
input := "[Link](test.md)\n![Image](image.png)"
|
||||||
testRenderMarkup(t, "unknown", "", "## Test", "Unknown mode: unknown\n", http.StatusUnprocessableEntity)
|
testRenderMarkdown(t, "gfm", false, input, `<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/test.md" rel="nofollow">Link</a>
|
||||||
|
<a href="http://localhost:3000/gogits/gogs/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/image.png" alt="Image"/></a></p>
|
||||||
|
`, http.StatusOK)
|
||||||
|
|
||||||
|
testRenderMarkdown(t, "gfm", false, input, `<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/test.md" rel="nofollow">Link</a>
|
||||||
|
<a href="http://localhost:3000/gogits/gogs/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/image.png" alt="Image"/></a></p>
|
||||||
|
`, http.StatusOK)
|
||||||
|
|
||||||
|
testRenderMarkup(t, "gfm", false, "", input, `<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/test.md" rel="nofollow">Link</a>
|
||||||
|
<a href="http://localhost:3000/gogits/gogs/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/image.png" alt="Image"/></a></p>
|
||||||
|
`, http.StatusOK)
|
||||||
|
|
||||||
|
testRenderMarkup(t, "file", false, "path/new-file.md", input, `<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/path/test.md" rel="nofollow">Link</a>
|
||||||
|
<a href="http://localhost:3000/gogits/gogs/media/branch/main/path/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/path/image.png" alt="Image"/></a></p>
|
||||||
|
`, http.StatusOK)
|
||||||
|
|
||||||
|
testRenderMarkup(t, "file", true, "path/test.unknown", "## Test", "Unsupported render extension: .unknown\n", http.StatusUnprocessableEntity)
|
||||||
|
testRenderMarkup(t, "unknown", true, "", "## Test", "Unknown mode: unknown\n", http.StatusUnprocessableEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
var simpleCases = []string{
|
var simpleCases = []string{
|
||||||
|
@ -160,7 +182,7 @@ func TestAPI_RenderSimple(t *testing.T) {
|
||||||
options := api.MarkdownOption{
|
options := api.MarkdownOption{
|
||||||
Mode: "markdown",
|
Mode: "markdown",
|
||||||
Text: "",
|
Text: "",
|
||||||
Context: Repo,
|
Context: "/gogits/gogs",
|
||||||
}
|
}
|
||||||
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown")
|
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown")
|
||||||
for i := 0; i < len(simpleCases); i += 2 {
|
for i := 0; i < len(simpleCases); i += 2 {
|
||||||
|
|
|
@ -7,63 +7,67 @@ package common
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/markup/markdown"
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
|
||||||
"mvdan.cc/xurls/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RenderMarkup renders markup text for the /markup and /markdown endpoints
|
// RenderMarkup renders markup text for the /markup and /markdown endpoints
|
||||||
func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPrefix, filePath string, wiki bool) {
|
func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPathContext, filePath string, wiki bool) {
|
||||||
var markupType string
|
// urlPathContext format is "/subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}"
|
||||||
relativePath := ""
|
// filePath is the path of the file to render if the end user is trying to preview a repo file (mode == "file")
|
||||||
|
// filePath will be used as RenderContext.RelativePath
|
||||||
|
|
||||||
if len(text) == 0 {
|
// for example, when previewing file "/gitea/owner/repo/src/branch/features/feat-123/doc/CHANGE.md", then filePath is "doc/CHANGE.md"
|
||||||
_, _ = ctx.Write([]byte(""))
|
// and the urlPathContext is "/gitea/owner/repo/src/branch/features/feat-123/doc"
|
||||||
return
|
|
||||||
|
var markupType, relativePath string
|
||||||
|
|
||||||
|
links := markup.Links{AbsolutePrefix: true}
|
||||||
|
if urlPathContext != "" {
|
||||||
|
links.Base = fmt.Sprintf("%s%s", httplib.GuessCurrentHostURL(ctx), urlPathContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case "markdown":
|
case "markdown":
|
||||||
// Raw markdown
|
// Raw markdown
|
||||||
if err := markdown.RenderRaw(&markup.RenderContext{
|
if err := markdown.RenderRaw(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
Links: markup.Links{
|
Links: links,
|
||||||
AbsolutePrefix: true,
|
|
||||||
Base: urlPrefix,
|
|
||||||
},
|
|
||||||
}, strings.NewReader(text), ctx.Resp); err != nil {
|
}, strings.NewReader(text), ctx.Resp); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case "comment":
|
case "comment":
|
||||||
// Comment as markdown
|
// Issue & comment content
|
||||||
markupType = markdown.MarkupName
|
markupType = markdown.MarkupName
|
||||||
case "gfm":
|
case "gfm":
|
||||||
// Github Flavored Markdown as document
|
// GitHub Flavored Markdown
|
||||||
markupType = markdown.MarkupName
|
markupType = markdown.MarkupName
|
||||||
case "file":
|
case "file":
|
||||||
// File as document based on file extension
|
markupType = "" // render the repo file content by its extension
|
||||||
markupType = ""
|
|
||||||
relativePath = filePath
|
relativePath = filePath
|
||||||
default:
|
default:
|
||||||
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode))
|
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(setting.AppSubURL+"/", urlPrefix) {
|
fields := strings.SplitN(strings.TrimPrefix(urlPathContext, setting.AppSubURL+"/"), "/", 5)
|
||||||
// check if urlPrefix is already set to a URL
|
if len(fields) == 5 && fields[2] == "src" && (fields[3] == "branch" || fields[3] == "commit" || fields[3] == "tag") {
|
||||||
linkRegex, _ := xurls.StrictMatchingScheme("https?://")
|
// absolute base prefix is something like "https://host/subpath/{user}/{repo}"
|
||||||
m := linkRegex.FindStringIndex(urlPrefix)
|
absoluteBasePrefix := fmt.Sprintf("%s%s/%s", httplib.GuessCurrentAppURL(ctx), fields[0], fields[1])
|
||||||
if m == nil {
|
|
||||||
urlPrefix = util.URLJoin(setting.AppURL, urlPrefix)
|
fileDir := path.Dir(filePath) // it is "doc" if filePath is "doc/CHANGE.md"
|
||||||
}
|
refPath := strings.Join(fields[3:], "/") // it is "branch/features/feat-12/doc"
|
||||||
|
refPath = strings.TrimSuffix(refPath, "/"+fileDir) // now we get the correct branch path: "branch/features/feat-12"
|
||||||
|
|
||||||
|
links = markup.Links{AbsolutePrefix: true, Base: absoluteBasePrefix, BranchPath: refPath, TreePath: fileDir}
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := map[string]string{}
|
meta := map[string]string{}
|
||||||
|
@ -81,12 +85,9 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := markup.Render(&markup.RenderContext{
|
if err := markup.Render(&markup.RenderContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
Repo: repoCtx,
|
Repo: repoCtx,
|
||||||
Links: markup.Links{
|
Links: links,
|
||||||
AbsolutePrefix: true,
|
|
||||||
Base: urlPrefix,
|
|
||||||
},
|
|
||||||
Metas: meta,
|
Metas: meta,
|
||||||
IsWiki: wiki,
|
IsWiki: wiki,
|
||||||
Type: markupType,
|
Type: markupType,
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
activities_model "code.gitea.io/gitea/models/activities"
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/cache"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/httplib"
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
@ -222,6 +223,14 @@ func SelfCheck(ctx *context.Context) {
|
||||||
|
|
||||||
ctx.Data["DatabaseCheckHasProblems"] = hasProblem
|
ctx.Data["DatabaseCheckHasProblems"] = hasProblem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
elapsed, err := cache.Test()
|
||||||
|
if err != nil {
|
||||||
|
ctx.Data["CacheError"] = err
|
||||||
|
} else if elapsed > cache.SlowCacheThreshold {
|
||||||
|
ctx.Data["CacheSlow"] = fmt.Sprint(elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplSelfCheck)
|
ctx.HTML(http.StatusOK, tplSelfCheck)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
system_model "code.gitea.io/gitea/models/system"
|
system_model "code.gitea.io/gitea/models/system"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/cache"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -42,6 +43,22 @@ func SendTestMail(ctx *context.Context) {
|
||||||
ctx.Redirect(setting.AppSubURL + "/admin/config")
|
ctx.Redirect(setting.AppSubURL + "/admin/config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCache test the cache settings
|
||||||
|
func TestCache(ctx *context.Context) {
|
||||||
|
elapsed, err := cache.Test()
|
||||||
|
if err != nil {
|
||||||
|
ctx.Flash.Error(ctx.Tr("admin.config.cache_test_failed", err))
|
||||||
|
} else {
|
||||||
|
if elapsed > cache.SlowCacheThreshold {
|
||||||
|
ctx.Flash.Warning(ctx.Tr("admin.config.cache_test_slow", elapsed))
|
||||||
|
} else {
|
||||||
|
ctx.Flash.Info(ctx.Tr("admin.config.cache_test_succeeded", elapsed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/admin/config")
|
||||||
|
}
|
||||||
|
|
||||||
func shadowPasswordKV(cfgItem, splitter string) string {
|
func shadowPasswordKV(cfgItem, splitter string) string {
|
||||||
fields := strings.Split(cfgItem, splitter)
|
fields := strings.Split(cfgItem, splitter)
|
||||||
for i := 0; i < len(fields); i++ {
|
for i := 0; i < len(fields); i++ {
|
||||||
|
|
|
@ -692,6 +692,7 @@ func registerRoutes(m *web.Route) {
|
||||||
m.Get("", admin.Config)
|
m.Get("", admin.Config)
|
||||||
m.Post("", admin.ChangeConfig)
|
m.Post("", admin.ChangeConfig)
|
||||||
m.Post("/test_mail", admin.SendTestMail)
|
m.Post("/test_mail", admin.SendTestMail)
|
||||||
|
m.Post("/test_cache", admin.TestCache)
|
||||||
m.Get("/settings", admin.ConfigSettings)
|
m.Get("/settings", admin.ConfigSettings)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,13 @@ package repository
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -83,3 +86,13 @@ func TestListUnadoptedRepositories_ListOptions(t *testing.T) {
|
||||||
assert.Equal(t, 2, count)
|
assert.Equal(t, 2, count)
|
||||||
assert.Equal(t, unadoptedList[1], repoNames[0])
|
assert.Equal(t, unadoptedList[1], repoNames[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAdoptRepository(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
assert.NoError(t, unittest.CopyDir(filepath.Join(setting.RepoRootPath, "user2", "repo1.git"), filepath.Join(setting.RepoRootPath, "user2", "test-adopt.git")))
|
||||||
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
_, err := AdoptRepository(db.DefaultContext, user2, user2, CreateRepoOptions{Name: "test-adopt"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
repoTestAdopt := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "test-adopt"})
|
||||||
|
assert.Equal(t, "sha1", repoTestAdopt.ObjectFormatName)
|
||||||
|
}
|
||||||
|
|
|
@ -229,8 +229,8 @@
|
||||||
<dt>{{ctx.Locale.Tr "admin.config.mailer_user"}}</dt>
|
<dt>{{ctx.Locale.Tr "admin.config.mailer_user"}}</dt>
|
||||||
<dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd>
|
<dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<dt class="tw-py-1">{{ctx.Locale.Tr "admin.config.send_test_mail"}}</dt>
|
<dt class="tw-py-1 tw-flex tw-items-center">{{ctx.Locale.Tr "admin.config.send_test_mail"}}</dt>
|
||||||
<dd>
|
<dd class="tw-py-0">
|
||||||
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post">
|
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
<div class="ui tiny input">
|
<div class="ui tiny input">
|
||||||
|
@ -260,6 +260,14 @@
|
||||||
<dt>{{ctx.Locale.Tr "admin.config.cache_item_ttl"}}</dt>
|
<dt>{{ctx.Locale.Tr "admin.config.cache_item_ttl"}}</dt>
|
||||||
<dd><code>{{.CacheItemTTL}}</code></dd>
|
<dd><code>{{.CacheItemTTL}}</code></dd>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
<div class="divider"></div>
|
||||||
|
<dt class="tw-py-1 tw-flex tw-items-center">{{ctx.Locale.Tr "admin.config.cache_test"}}</dt>
|
||||||
|
<dd class="tw-py-0">
|
||||||
|
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_cache" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<button class="ui tiny primary button">{{ctx.Locale.Tr "test"}}</button>
|
||||||
|
</form>
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -76,9 +76,11 @@
|
||||||
{{ctx.Locale.Tr "admin.dashboard.system_status"}}
|
{{ctx.Locale.Tr "admin.dashboard.system_status"}}
|
||||||
</h4>
|
</h4>
|
||||||
{{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}}
|
{{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}}
|
||||||
<div class="no-loading-indicator tw-hidden"></div>
|
<div class="ui attached table segment">
|
||||||
<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".no-loading-indicator" class="ui attached table segment">
|
<div class="no-loading-indicator tw-hidden"></div>
|
||||||
{{template "admin/system_status" .}}
|
<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".no-loading-indicator">
|
||||||
|
{{template "admin/system_status" .}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{template "admin/layout_footer" .}}
|
{{template "admin/layout_footer" .}}
|
||||||
|
|
|
@ -17,32 +17,40 @@
|
||||||
<div class="ui attached segment tw-hidden self-check-problem" id="self-check-by-frontend"></div>
|
<div class="ui attached segment tw-hidden self-check-problem" id="self-check-by-frontend"></div>
|
||||||
|
|
||||||
{{if .DatabaseCheckHasProblems}}
|
{{if .DatabaseCheckHasProblems}}
|
||||||
<div class="ui attached segment self-check-problem">
|
<div class="ui attached segment self-check-problem">
|
||||||
{{if .DatabaseType.IsMySQL}}
|
{{if .DatabaseType.IsMySQL}}
|
||||||
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div>
|
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div>
|
||||||
{{else if .DatabaseType.IsMSSQL}}
|
{{else if .DatabaseType.IsMSSQL}}
|
||||||
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div>
|
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .DatabaseCheckCollationMismatch}}
|
{{if .DatabaseCheckCollationMismatch}}
|
||||||
<div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div>
|
<div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .DatabaseCheckCollationCaseInsensitive}}
|
{{if .DatabaseCheckCollationCaseInsensitive}}
|
||||||
<div class="ui warning message">{{ctx.Locale.Tr "admin.self_check.database_collation_case_insensitive" .DatabaseCheckResult.DatabaseCollation}}</div>
|
<div class="ui warning message">{{ctx.Locale.Tr "admin.self_check.database_collation_case_insensitive" .DatabaseCheckResult.DatabaseCollation}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .DatabaseCheckInconsistentCollationColumns}}
|
{{if .DatabaseCheckInconsistentCollationColumns}}
|
||||||
<div class="ui red message">
|
<div class="ui red message">
|
||||||
<details>
|
<details>
|
||||||
<summary>{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}</summary>
|
<summary>{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}</summary>
|
||||||
<ul class="tw-w-full">
|
<ul class="tw-w-full">
|
||||||
{{range .DatabaseCheckInconsistentCollationColumns}}
|
{{range .DatabaseCheckInconsistentCollationColumns}}
|
||||||
<li>{{.}}</li>
|
<li>{{.}}</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .CacheError}}
|
||||||
|
<div class="ui red message">{{ctx.Locale.Tr "admin.config.cache_test_failed" .CacheError}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{if .CacheSlow}}
|
||||||
|
<div class="ui warning message">{{ctx.Locale.Tr "admin.config.cache_test_slow" .CacheSlow}}</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{/* only shown when there is no visible "self-check-problem" */}}
|
{{/* only shown when there is no visible "self-check-problem" */}}
|
||||||
<div class="ui attached segment tw-hidden self-check-no-problem">
|
<div class="ui attached segment tw-hidden self-check-no-problem">
|
||||||
{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}
|
{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}
|
||||||
|
|
4
templates/swagger/v1_json.tmpl
generated
4
templates/swagger/v1_json.tmpl
generated
|
@ -22404,7 +22404,7 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"Context": {
|
"Context": {
|
||||||
"description": "Context to render\n\nin: body",
|
"description": "URL path for rendering issue, media and file links\nExpected format: /subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}\n\nin: body",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"Mode": {
|
"Mode": {
|
||||||
|
@ -22427,7 +22427,7 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"Context": {
|
"Context": {
|
||||||
"description": "Context to render\n\nin: body",
|
"description": "URL path for rendering issue, media and file links\nExpected format: /subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}\n\nin: body",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"FilePath": {
|
"FilePath": {
|
||||||
|
|
|
@ -65,7 +65,7 @@ TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=
|
||||||
|
|
||||||
## Running individual tests
|
## Running individual tests
|
||||||
|
|
||||||
Example command to run `example.test.e2e.js` test file:
|
Example command to run `example.test.e2e.ts` test file:
|
||||||
|
|
||||||
_Note: unlike integration tests, this filtering is at the file level, not function_
|
_Note: unlike integration tests, this filtering is at the file level, not function_
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,10 @@ func TestMain(m *testing.M) {
|
||||||
os.Exit(exitVal)
|
os.Exit(exitVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestE2e should be the only test e2e necessary. It will collect all "*.test.e2e.js" files in this directory and build a test for each.
|
// TestE2e should be the only test e2e necessary. It will collect all "*.test.e2e.ts" files in this directory and build a test for each.
|
||||||
func TestE2e(t *testing.T) {
|
func TestE2e(t *testing.T) {
|
||||||
// Find the paths of all e2e test files in test directory.
|
// Find the paths of all e2e test files in test directory.
|
||||||
searchGlob := filepath.Join(filepath.Dir(setting.AppPath), "tests", "e2e", "*.test.e2e.js")
|
searchGlob := filepath.Join(filepath.Dir(setting.AppPath), "tests", "e2e", "*.test.e2e.ts")
|
||||||
paths, err := filepath.Glob(searchGlob)
|
paths, err := filepath.Glob(searchGlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
import {test, expect} from '@playwright/test';
|
import {test, expect} from '@playwright/test';
|
||||||
import {login_user, save_visual, load_logged_in_context} from './utils_e2e.js';
|
import {login_user, save_visual, load_logged_in_context} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.beforeAll(async ({browser}, workerInfo) => {
|
||||||
await login_user(browser, workerInfo, 'user2');
|
await login_user(browser, workerInfo, 'user2');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Load Homepage', async ({page}) => {
|
test('homepage', async ({page}) => {
|
||||||
const response = await page.goto('/');
|
const response = await page.goto('/');
|
||||||
await expect(response?.status()).toBe(200); // Status OK
|
await expect(response?.status()).toBe(200); // Status OK
|
||||||
await expect(page).toHaveTitle(/^Gitea: Git with a cup of tea\s*$/);
|
await expect(page).toHaveTitle(/^Gitea: Git with a cup of tea\s*$/);
|
||||||
await expect(page.locator('.logo')).toHaveAttribute('src', '/assets/img/logo.svg');
|
await expect(page.locator('.logo')).toHaveAttribute('src', '/assets/img/logo.svg');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Test Register Form', async ({page}, workerInfo) => {
|
test('register', async ({page}, workerInfo) => {
|
||||||
const response = await page.goto('/user/sign_up');
|
const response = await page.goto('/user/sign_up');
|
||||||
await expect(response?.status()).toBe(200); // Status OK
|
await expect(response?.status()).toBe(200); // Status OK
|
||||||
await page.type('input[name=user_name]', `e2e-test-${workerInfo.workerIndex}`);
|
await page.type('input[name=user_name]', `e2e-test-${workerInfo.workerIndex}`);
|
||||||
|
@ -29,7 +29,7 @@ test('Test Register Form', async ({page}, workerInfo) => {
|
||||||
save_visual(page);
|
save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Test Login Form', async ({page}, workerInfo) => {
|
test('login', async ({page}, workerInfo) => {
|
||||||
const response = await page.goto('/user/login');
|
const response = await page.goto('/user/login');
|
||||||
await expect(response?.status()).toBe(200); // Status OK
|
await expect(response?.status()).toBe(200); // Status OK
|
||||||
|
|
||||||
|
@ -37,14 +37,14 @@ test('Test Login Form', async ({page}, workerInfo) => {
|
||||||
await page.type('input[name=password]', `password`);
|
await page.type('input[name=password]', `password`);
|
||||||
await page.click('form button.ui.primary.button:visible');
|
await page.click('form button.ui.primary.button:visible');
|
||||||
|
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle
|
||||||
|
|
||||||
await expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`);
|
await expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`);
|
||||||
|
|
||||||
save_visual(page);
|
save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Test Logged In User', async ({browser}, workerInfo) => {
|
test('logged in user', async ({browser}, workerInfo) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
|
import {env} from 'node:process';
|
||||||
|
|
||||||
const ARTIFACTS_PATH = `tests/e2e/test-artifacts`;
|
const ARTIFACTS_PATH = `tests/e2e/test-artifacts`;
|
||||||
const LOGIN_PASSWORD = 'password';
|
const LOGIN_PASSWORD = 'password';
|
||||||
|
@ -20,7 +21,7 @@ export async function login_user(browser, workerInfo, user) {
|
||||||
await page.type('input[name=password]', LOGIN_PASSWORD);
|
await page.type('input[name=password]', LOGIN_PASSWORD);
|
||||||
await page.click('form button.ui.primary.button:visible');
|
await page.click('form button.ui.primary.button:visible');
|
||||||
|
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle
|
||||||
|
|
||||||
await expect(page.url(), {message: `Failed to login user ${user}`}).toBe(`${workerInfo.project.use.baseURL}/`);
|
await expect(page.url(), {message: `Failed to login user ${user}`}).toBe(`${workerInfo.project.use.baseURL}/`);
|
||||||
|
|
||||||
|
@ -44,8 +45,8 @@ export async function load_logged_in_context(browser, workerInfo, user) {
|
||||||
|
|
||||||
export async function save_visual(page) {
|
export async function save_visual(page) {
|
||||||
// Optionally include visual testing
|
// Optionally include visual testing
|
||||||
if (process.env.VISUAL_TEST) {
|
if (env.VISUAL_TEST) {
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle
|
||||||
// Mock page/version string
|
// Mock page/version string
|
||||||
await page.locator('footer div.ui.left').evaluate((node) => node.innerHTML = 'MOCK');
|
await page.locator('footer div.ui.left').evaluate((node) => node.innerHTML = 'MOCK');
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
|
@ -4,7 +4,11 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
|
@ -21,101 +25,167 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type tarFile struct {
|
||||||
|
Name string
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeArchiveFileTar(files []*tarFile) []byte {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
tarWriter := tar.NewWriter(buf)
|
||||||
|
for _, file := range files {
|
||||||
|
_ = tarWriter.WriteHeader(&tar.Header{
|
||||||
|
Typeflag: tar.TypeReg,
|
||||||
|
Name: file.Name,
|
||||||
|
Mode: 0o644,
|
||||||
|
Size: int64(len(file.Data)),
|
||||||
|
})
|
||||||
|
_, _ = tarWriter.Write(file.Data)
|
||||||
|
}
|
||||||
|
_ = tarWriter.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeArchiveFileGz(data []byte) []byte {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
gzWriter, _ := gzip.NewWriterLevel(buf, gzip.NoCompression)
|
||||||
|
_, _ = gzWriter.Write(data)
|
||||||
|
_ = gzWriter.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRubyGem(name, version string) []byte {
|
||||||
|
metadataContent := fmt.Sprintf(`--- !ruby/object:Gem::Specification
|
||||||
|
name: %s
|
||||||
|
version: !ruby/object:Gem::Version
|
||||||
|
version: %s
|
||||||
|
platform: ruby
|
||||||
|
authors:
|
||||||
|
- Gitea
|
||||||
|
autorequire:
|
||||||
|
bindir: bin
|
||||||
|
cert_chain: []
|
||||||
|
date: 2021-08-23 00:00:00.000000000 Z
|
||||||
|
dependencies:
|
||||||
|
- !ruby/object:Gem::Dependency
|
||||||
|
name: runtime-dep
|
||||||
|
requirement: !ruby/object:Gem::Requirement
|
||||||
|
requirements:
|
||||||
|
- - ">="
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: 1.2.0
|
||||||
|
- - "<"
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: '2.0'
|
||||||
|
type: :runtime
|
||||||
|
prerelease: false
|
||||||
|
version_requirements: !ruby/object:Gem::Requirement
|
||||||
|
requirements:
|
||||||
|
- - ">="
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: 1.2.0
|
||||||
|
- - "<"
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: '2.0'
|
||||||
|
- !ruby/object:Gem::Dependency
|
||||||
|
name: dev-dep
|
||||||
|
requirement: !ruby/object:Gem::Requirement
|
||||||
|
requirements:
|
||||||
|
- - "~>"
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: '5.2'
|
||||||
|
type: :development
|
||||||
|
prerelease: false
|
||||||
|
version_requirements: !ruby/object:Gem::Requirement
|
||||||
|
requirements:
|
||||||
|
- - "~>"
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: '5.2'
|
||||||
|
description: RubyGems package test
|
||||||
|
email: rubygems@gitea.io
|
||||||
|
executables: []
|
||||||
|
extensions: []
|
||||||
|
extra_rdoc_files: []
|
||||||
|
files:
|
||||||
|
- lib/gitea.rb
|
||||||
|
homepage: https://gitea.io/
|
||||||
|
licenses:
|
||||||
|
- MIT
|
||||||
|
metadata: {}
|
||||||
|
post_install_message:
|
||||||
|
rdoc_options: []
|
||||||
|
require_paths:
|
||||||
|
- lib
|
||||||
|
required_ruby_version: !ruby/object:Gem::Requirement
|
||||||
|
requirements:
|
||||||
|
- - ">="
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: 2.3.0
|
||||||
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
||||||
|
requirements:
|
||||||
|
- - ">="
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: '1.0'
|
||||||
|
requirements: []
|
||||||
|
rubyforge_project:
|
||||||
|
rubygems_version: 2.7.6.2
|
||||||
|
signing_key:
|
||||||
|
specification_version: 4
|
||||||
|
summary: Gitea package
|
||||||
|
test_files: []
|
||||||
|
`, name, version)
|
||||||
|
|
||||||
|
metadataGz := makeArchiveFileGz([]byte(metadataContent))
|
||||||
|
dataTarGz := makeArchiveFileGz(makeArchiveFileTar([]*tarFile{
|
||||||
|
{
|
||||||
|
Name: "lib/gitea.rb",
|
||||||
|
Data: []byte("class Gitea\nend"),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
checksumsYaml := fmt.Sprintf(`---
|
||||||
|
SHA256:
|
||||||
|
metadata.gz: %x
|
||||||
|
data.tar.gz: %x
|
||||||
|
SHA512:
|
||||||
|
metadata.gz: %x
|
||||||
|
data.tar.gz: %x
|
||||||
|
`, sha256.Sum256(metadataGz), sha256.Sum256(dataTarGz), sha512.Sum512(metadataGz), sha512.Sum512(dataTarGz))
|
||||||
|
|
||||||
|
files := []*tarFile{
|
||||||
|
{
|
||||||
|
Name: "data.tar.gz",
|
||||||
|
Data: dataTarGz,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "metadata.gz",
|
||||||
|
Data: metadataGz,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "checksums.yaml.gz",
|
||||||
|
Data: makeArchiveFileGz([]byte(checksumsYaml)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return makeArchiveFileTar(files)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPackageRubyGems(t *testing.T) {
|
func TestPackageRubyGems(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
|
||||||
packageName := "gitea"
|
testGemName := "gitea"
|
||||||
packageVersion := "1.0.5"
|
testGemVersion := "1.0.5"
|
||||||
packageFilename := "gitea-1.0.5.gem"
|
testGemContent := makeRubyGem(testGemName, testGemVersion)
|
||||||
|
testGemContentChecksum := fmt.Sprintf("%x", sha256.Sum256(testGemContent))
|
||||||
|
|
||||||
gemContent, _ := base64.StdEncoding.DecodeString(`bWV0YWRhdGEuZ3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
testAnotherGemName := "gitea-another"
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDA0NDQAMDAwMDAw
|
testAnotherGemVersion := "0.99"
|
||||||
MAAwMDAwMDAwADAwMDAwMDAxMDQxADE0MTEwNzcyMzY2ADAxMzQ0MQAgMAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhcgAwMHdoZWVsAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAd2hlZWwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwADAwMDAw
|
|
||||||
MDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf
|
|
||||||
iwgA9vQjYQID1VVNb9QwEL37V5he9pRsmlJAFlQckCoOXAriQIUix5nNmsYf2JOqKwS/nYmz2d3Q
|
|
||||||
qqCCKpFdadfjmfdm5nmcLMv4k9DXm6Wrv4BCcQ5GiPcelF5pJVE7y6w0IHirESS7hhDJJu4I+jhu
|
|
||||||
Mc53Tsd5kZ8y30lcuWAEH2KY7HHtQhQs4+cJkwwuwNdeB6JhtbaNDoLTL1MQsFJrqQnr8jNrJJJH
|
|
||||||
WZTHWfEiK094UYj0zYvp4Z9YAx5sA1ZpSCS3M30zeWwo2bG60FvUBjIKJts2GwMW76r0Yr9NzjN3
|
|
||||||
YhwsGX2Ozl4dpcWwvK9d43PQtDIv9igvHwSyIIwFmXHjqTqxLY8MPkCADmQk80p2EfZ6VbM6/ue6
|
|
||||||
/1D0Bq7/qeA/zh6W82leHmhFWUHn/JbsEfT6q7QbiCpoj8l0QcEUFLmX6kq2wBEiMjBSd+Pwt7T5
|
|
||||||
Ot0kuXYMbkD1KOuOBnWYb7hBsAP4bhlkFRqnqpWefMZ/pHCn6+WIFGq2dgY8EQq+RvRRLJcTyZJ1
|
|
||||||
WhHqGPTu7QdmACXdJFLwb9+ZdxErbSPKrqsMxJhAWCJ1qaqRdtu6yktcT/STsamG0qp7rsa5EL/K
|
|
||||||
MBua30uw4ynzExqYWRJDfx8/kQWN3PwsDh2jYLr1W+pZcAmCs9splvnz/Flesqhbq21bXcGG/OLh
|
|
||||||
+2fv/JTF3hgZyCW9OaZjxoZjdnBGfgKpxZyJ1QYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGF0
|
|
||||||
YS50YXIuZ3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDA0NDQAMDAwMDAwMAAw
|
|
||||||
MDAwMDAwADAwMDAwMDAwMjQyADE0MTEwNzcyMzY2ADAxMzM2MQAgMAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhcgAwMHdoZWVsAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAd2hlZWwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwADAwMDAwMDAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfiwgA
|
|
||||||
9vQjYQID7M/NCsMgDABgz32KrA/QxersK/Q17ExXIcyhlr7+HLv1sJ02KPhBCPk5JOyn881nsl2c
|
|
||||||
xI+gRDRaC3zbZ8RBCamlxGHolTFlX11kLwDFH6wp21hO2RYi/rD3bb5/7iCubFOCMbBtABzNkIjn
|
|
||||||
bvGlAnisOUE7EnOALUR2p7b06e6aV4iqqqrquJ4AAAD//wMA+sA/NQAIAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNoZWNr
|
|
||||||
c3Vtcy55YW1sLmd6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwNDQ0ADAwMDAwMDAAMDAw
|
|
||||||
MDAwMAAwMDAwMDAwMDQ1MAAxNDExMDc3MjM2NgAwMTQ2MTIAIDAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdXN0YXIAMDB3aGVlbAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAHdoZWVsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMDAwMAAwMDAwMDAwAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4sIAPb0
|
|
||||||
I2ECA2WQOa4UQAxE8znFXGCQ21vbPyMj5wRuL0Qk6EecnmZCyKyy9FSvXq/X4/u3ryj68Xg+f/Zn
|
|
||||||
VHzGlx+/P57qvU4XxWalBKftSXOgCjNYkdRycrC5Axem+W4HqS12PNEv7836jF9vnlHxwSyxKY+y
|
|
||||||
go0cPblyHzkrZ4HF1GSVhe7mOOoasXNk2fnbUxb+19Pp9tobD/QlJKMX7y204PREh6nQ5hG9Alw6
|
|
||||||
x4TnmtA+aekGfm6wAseog2LSgpR4Q7cYnAH3K4qAQa6A6JCC1gpuY7P+9YxE5SZ+j0eVGbaBTwBQ
|
|
||||||
iIqRUyyzLCoFCBdYNWxniapTavD97blXTzFvgoVoAsKBAtlU48cdaOmeZDpwV01OtcGwjscfeUrY
|
|
||||||
B9QBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`)
|
|
||||||
|
|
||||||
root := fmt.Sprintf("/api/packages/%s/rubygems", user.Name)
|
root := fmt.Sprintf("/api/packages/%s/rubygems", user.Name)
|
||||||
|
|
||||||
uploadFile := func(t *testing.T, expectedStatus int) {
|
uploadFile := func(t *testing.T, content []byte, expectedStatus int) {
|
||||||
req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/api/v1/gems", root), bytes.NewReader(gemContent)).
|
req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/api/v1/gems", root), bytes.NewReader(content)).
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
MakeRequest(t, req, expectedStatus)
|
MakeRequest(t, req, expectedStatus)
|
||||||
}
|
}
|
||||||
|
@ -123,7 +193,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`)
|
||||||
t.Run("Upload", func(t *testing.T) {
|
t.Run("Upload", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
uploadFile(t, http.StatusCreated)
|
uploadFile(t, testGemContent, http.StatusCreated)
|
||||||
|
|
||||||
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems)
|
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -133,34 +203,33 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, pd.SemVer)
|
assert.NotNil(t, pd.SemVer)
|
||||||
assert.IsType(t, &rubygems.Metadata{}, pd.Metadata)
|
assert.IsType(t, &rubygems.Metadata{}, pd.Metadata)
|
||||||
assert.Equal(t, packageName, pd.Package.Name)
|
assert.Equal(t, testGemName, pd.Package.Name)
|
||||||
assert.Equal(t, packageVersion, pd.Version.Version)
|
assert.Equal(t, testGemVersion, pd.Version.Version)
|
||||||
|
|
||||||
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
|
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, pfs, 1)
|
assert.Len(t, pfs, 1)
|
||||||
assert.Equal(t, packageFilename, pfs[0].Name)
|
assert.Equal(t, fmt.Sprintf("%s-%s.gem", testGemName, testGemVersion), pfs[0].Name)
|
||||||
assert.True(t, pfs[0].IsLead)
|
assert.True(t, pfs[0].IsLead)
|
||||||
|
|
||||||
pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
|
pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int64(4608), pb.Size)
|
assert.EqualValues(t, len(testGemContent), pb.Size)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("UploadExists", func(t *testing.T) {
|
t.Run("UploadExists", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
uploadFile(t, testGemContent, http.StatusConflict)
|
||||||
uploadFile(t, http.StatusConflict)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Download", func(t *testing.T) {
|
t.Run("Download", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
req := NewRequest(t, "GET", fmt.Sprintf("%s/gems/%s", root, packageFilename)).
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/gems/%s-%s.gem", root, testGemName, testGemVersion)).
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
assert.Equal(t, gemContent, resp.Body.Bytes())
|
assert.Equal(t, testGemContent, resp.Body.Bytes())
|
||||||
|
|
||||||
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems)
|
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -171,7 +240,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`)
|
||||||
t.Run("DownloadGemspec", func(t *testing.T) {
|
t.Run("DownloadGemspec", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
req := NewRequest(t, "GET", fmt.Sprintf("%s/quick/Marshal.4.8/%sspec.rz", root, packageFilename)).
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/quick/Marshal.4.8/%s-%s.gemspec.rz", root, testGemName, testGemVersion)).
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
@ -206,22 +275,63 @@ gAAAAP//MS06Gw==`)
|
||||||
enumeratePackages(t, "prerelease_specs.4.8.gz", b)
|
enumeratePackages(t, "prerelease_specs.4.8.gz", b)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("UploadAnother", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
uploadFile(t, makeRubyGem(testAnotherGemName, testAnotherGemVersion), http.StatusCreated)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PackageInfo", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/info/%s", root, testGemName)).AddBasicAuth(user.Name)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
expected := fmt.Sprintf(`---
|
||||||
|
1.0.5 runtime-dep:>= 1.2.0&< 2.0|checksum:%s,ruby:>= 2.3.0,rubygems:>= 1.0
|
||||||
|
`, testGemContentChecksum)
|
||||||
|
assert.Equal(t, expected, resp.Body.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Versions", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/versions", root)).AddBasicAuth(user.Name)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
assert.Equal(t, `---
|
||||||
|
gitea 1.0.5 08843c2dd0ea19910e6b056b98e38f1c
|
||||||
|
gitea-another 0.99 8b639e4048d282941485368ec42609be
|
||||||
|
`, resp.Body.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
deleteGemPackage := func(t *testing.T, packageName, packageVersion string) {
|
||||||
body := bytes.Buffer{}
|
body := bytes.Buffer{}
|
||||||
writer := multipart.NewWriter(&body)
|
writer := multipart.NewWriter(&body)
|
||||||
writer.WriteField("gem_name", packageName)
|
_ = writer.WriteField("gem_name", packageName)
|
||||||
writer.WriteField("version", packageVersion)
|
_ = writer.WriteField("version", packageVersion)
|
||||||
writer.Close()
|
_ = writer.Close()
|
||||||
|
|
||||||
req := NewRequestWithBody(t, "DELETE", fmt.Sprintf("%s/api/v1/gems/yank", root), &body).
|
req := NewRequestWithBody(t, "DELETE", fmt.Sprintf("%s/api/v1/gems/yank", root), &body).
|
||||||
SetHeader("Content-Type", writer.FormDataContentType()).
|
SetHeader("Content-Type", writer.FormDataContentType()).
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("DeleteAll", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
deleteGemPackage(t, testGemName, testGemVersion)
|
||||||
|
deleteGemPackage(t, testAnotherGemName, testAnotherGemVersion)
|
||||||
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems)
|
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRubyGems)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, pvs)
|
assert.Empty(t, pvs)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("PackageInfoAfterDelete", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/info/%s", root, testGemName)).AddBasicAuth(user.Name)
|
||||||
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("VersionsAfterDelete", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/versions", root)).AddBasicAuth(user.Name)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
assert.Equal(t, "---\n", resp.Body.String())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
31
tsconfig.json
Normal file
31
tsconfig.json
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"*",
|
||||||
|
"tests/e2e/**/*",
|
||||||
|
"tools/**/*",
|
||||||
|
"web_src/js/**/*",
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "nodenext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"lib": ["dom", "dom.iterable", "dom.asynciterable", "esnext"],
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"stripInternal": true,
|
||||||
|
"strict": false,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": false,
|
||||||
|
"exactOptionalPropertyTypes": false,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
plugins:
|
|
||||||
- eslint-plugin-vue
|
|
||||||
- eslint-plugin-vue-scoped-css
|
|
||||||
|
|
||||||
extends:
|
|
||||||
- ../../../.eslintrc.yaml
|
|
||||||
- plugin:vue/vue3-recommended
|
|
||||||
- plugin:vue-scoped-css/vue3-recommended
|
|
||||||
|
|
||||||
parserOptions:
|
|
||||||
sourceType: module
|
|
||||||
ecmaVersion: latest
|
|
||||||
|
|
||||||
env:
|
|
||||||
browser: true
|
|
||||||
|
|
||||||
rules:
|
|
||||||
vue/attributes-order: [0]
|
|
||||||
vue/html-closing-bracket-spacing: [2, {startTag: never, endTag: never, selfClosingTag: never}]
|
|
||||||
vue/max-attributes-per-line: [0]
|
|
||||||
vue/singleline-html-element-content-newline: [0]
|
|
||||||
vue-scoped-css/enforce-style-type: [0]
|
|
|
@ -797,7 +797,7 @@ export function initRepositoryActionView() {
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style> /* eslint-disable-line vue-scoped-css/enforce-style-type */
|
||||||
/* some elements are not managed by vue, so we need to use global style */
|
/* some elements are not managed by vue, so we need to use global style */
|
||||||
.job-status-rotate {
|
.job-status-rotate {
|
||||||
animation: job-status-rotate-keyframes 1s linear infinite;
|
animation: job-status-rotate-keyframes 1s linear infinite;
|
||||||
|
|
|
@ -153,7 +153,7 @@ export function initRepoCodeView() {
|
||||||
});
|
});
|
||||||
|
|
||||||
$(window).on('hashchange', () => {
|
$(window).on('hashchange', () => {
|
||||||
let m = window.location.hash.match(rangeAnchorRegex);
|
let m = rangeAnchorRegex.exec(window.location.hash.match);
|
||||||
const $linesEls = $(getLineEls());
|
const $linesEls = $(getLineEls());
|
||||||
let $first;
|
let $first;
|
||||||
if (m) {
|
if (m) {
|
||||||
|
@ -170,7 +170,7 @@ export function initRepoCodeView() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m = window.location.hash.match(singleAnchorRegex);
|
m = singleAnchorRegex.exec(window.location.hash.match);
|
||||||
if (m) {
|
if (m) {
|
||||||
$first = $linesEls.filter(`[rel=L${m[2]}]`);
|
$first = $linesEls.filter(`[rel=L${m[2]}]`);
|
||||||
if ($first.length) {
|
if ($first.length) {
|
||||||
|
|
|
@ -272,7 +272,7 @@ export function initRepoCommentForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
$list.find('.selected').html(`
|
$list.find('.selected').html(`
|
||||||
<a class="item muted sidebar-item-link" href=${htmlEscape(this.getAttribute('href'))}>
|
<a class="item muted sidebar-item-link" href="${htmlEscape(this.getAttribute('data-href'))}">
|
||||||
${icon}
|
${icon}
|
||||||
${htmlEscape(this.textContent)}
|
${htmlEscape(this.textContent)}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {isDocumentFragmentOrElementNode} from '../utils/dom.js';
|
||||||
import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg';
|
import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg';
|
||||||
|
|
||||||
window.customElements.define('overflow-menu', class extends HTMLElement {
|
window.customElements.define('overflow-menu', class extends HTMLElement {
|
||||||
updateItems = throttle(100, () => {
|
updateItems = throttle(100, () => { // eslint-disable-line unicorn/consistent-function-scoping -- https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2088
|
||||||
if (!this.tippyContent) {
|
if (!this.tippyContent) {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.classList.add('tippy-target');
|
div.classList.add('tippy-target');
|
||||||
|
|
Loading…
Reference in New Issue
Block a user