mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-04-23 03:21:35 +02:00
Merge branch 'forgejo' into feat/xorm-EngineGroup
This commit is contained in:
commit
5faf339a1b
27 changed files with 765 additions and 346 deletions
|
@ -26,3 +26,8 @@ insert_final_newline = false
|
|||
|
||||
[options/locale/locale_*.ini]
|
||||
insert_final_newline = false
|
||||
|
||||
# Weblate JSON output defaults to four spaces
|
||||
[options/locale_next/locale_*.json]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
|
||||
runs-on: docker
|
||||
container:
|
||||
image: data.forgejo.org/renovate/renovate:39.185.0
|
||||
image: data.forgejo.org/renovate/renovate:39.195.1
|
||||
|
||||
steps:
|
||||
- name: Load renovate repo cache
|
||||
|
|
|
@ -223,7 +223,7 @@ jobs:
|
|||
ldap:
|
||||
image: data.forgejo.org/oci/test-openldap:latest
|
||||
pgsql:
|
||||
image: data.forgejo.org/oci/bitnami/postgresql:15
|
||||
image: data.forgejo.org/oci/bitnami/postgresql:16
|
||||
env:
|
||||
POSTGRESQL_DATABASE: test
|
||||
POSTGRESQL_PASSWORD: postgres
|
||||
|
|
12
Makefile
12
Makefile
|
@ -39,7 +39,7 @@ XGO_VERSION := go-1.21.x
|
|||
AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.0 # renovate: datasource=go
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.6 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.7 # renovate: datasource=go
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go
|
||||
|
@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour
|
|||
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.31.0 # renovate: datasource=go
|
||||
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go
|
||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go
|
||||
RENOVATE_NPM_PACKAGE ?= renovate@39.185.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
|
||||
RENOVATE_NPM_PACKAGE ?= renovate@39.195.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
|
||||
|
||||
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
|
||||
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
|
||||
|
@ -413,7 +413,7 @@ lint-frontend: lint-js lint-css
|
|||
lint-frontend-fix: lint-js-fix lint-css-fix
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: lint-go lint-go-vet lint-editorconfig lint-renovate lint-locale lint-disposable-emails
|
||||
lint-backend: lint-go lint-go-vet lint-editorconfig lint-renovate lint-locale lint-locale-usage lint-disposable-emails
|
||||
|
||||
.PHONY: lint-backend-fix
|
||||
lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig lint-disposable-emails-fix
|
||||
|
@ -458,7 +458,11 @@ lint-renovate: node_modules
|
|||
|
||||
.PHONY: lint-locale
|
||||
lint-locale:
|
||||
$(GO) run build/lint-locale.go
|
||||
$(GO) run build/lint-locale/lint-locale.go
|
||||
|
||||
.PHONY: lint-locale-usage
|
||||
lint-locale-usage:
|
||||
$(GO) run build/lint-locale-usage/lint-locale-usage.go --allow-missing-msgids
|
||||
|
||||
.PHONY: lint-md
|
||||
lint-md: node_modules
|
||||
|
|
331
build/lint-locale-usage/lint-locale-usage.go
Normal file
331
build/lint-locale-usage/lint-locale-usage.go
Normal file
|
@ -0,0 +1,331 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
goParser "go/parser"
|
||||
"go/token"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
tmplParser "text/template/parse"
|
||||
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/locale"
|
||||
fjTemplates "code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// this works by first gathering all valid source string IDs from `en-US` reference files
|
||||
// and then checking if all used source strings are actually defined
|
||||
|
||||
type OnMsgidHandler func(fset *token.FileSet, pos token.Pos, msgid string)
|
||||
|
||||
type LocatedError struct {
|
||||
Location string
|
||||
Kind string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e LocatedError) Error() string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(e.Location)
|
||||
sb.WriteString(":\t")
|
||||
if e.Kind != "" {
|
||||
sb.WriteString(e.Kind)
|
||||
sb.WriteString(": ")
|
||||
}
|
||||
sb.WriteString("ERROR: ")
|
||||
sb.WriteString(e.Err.Error())
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func isLocaleTrFunction(funcname string) bool {
|
||||
return funcname == "Tr" || funcname == "TrN"
|
||||
}
|
||||
|
||||
// the `Handle*File` functions follow the following calling convention:
|
||||
// * `fname` is the name of the input file
|
||||
// * `src` is either `nil` (then the function invokes `ReadFile` to read the file)
|
||||
// or the contents of the file as {`[]byte`, or a `string`}
|
||||
|
||||
func (omh OnMsgidHandler) HandleGoFile(fname string, src any) error {
|
||||
fset := token.NewFileSet()
|
||||
node, err := goParser.ParseFile(fset, fname, src, goParser.SkipObjectResolution)
|
||||
if err != nil {
|
||||
return LocatedError{
|
||||
Location: fname,
|
||||
Kind: "Go parser",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
// search for function calls of the form `anything.Tr(any-string-lit)`
|
||||
|
||||
call, ok := n.(*ast.CallExpr)
|
||||
if !ok || len(call.Args) != 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
funSel, ok := call.Fun.(*ast.SelectorExpr)
|
||||
if (!ok) || !isLocaleTrFunction(funSel.Sel.Name) {
|
||||
return true
|
||||
}
|
||||
|
||||
argLit, ok := call.Args[0].(*ast.BasicLit)
|
||||
if (!ok) || argLit.Kind != token.STRING {
|
||||
return true
|
||||
}
|
||||
|
||||
// extract string content
|
||||
arg, err := strconv.Unquote(argLit.Value)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// found interesting string
|
||||
omh(fset, argLit.ValuePos, arg)
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// derived from source: modules/templates/scopedtmpl/scopedtmpl.go, L169-L213
|
||||
func (omh OnMsgidHandler) handleTemplateNode(fset *token.FileSet, node tmplParser.Node) {
|
||||
switch node.Type() {
|
||||
case tmplParser.NodeAction:
|
||||
omh.handleTemplatePipeNode(fset, node.(*tmplParser.ActionNode).Pipe)
|
||||
case tmplParser.NodeList:
|
||||
nodeList := node.(*tmplParser.ListNode)
|
||||
omh.handleTemplateFileNodes(fset, nodeList.Nodes)
|
||||
case tmplParser.NodePipe:
|
||||
omh.handleTemplatePipeNode(fset, node.(*tmplParser.PipeNode))
|
||||
case tmplParser.NodeTemplate:
|
||||
omh.handleTemplatePipeNode(fset, node.(*tmplParser.TemplateNode).Pipe)
|
||||
case tmplParser.NodeIf:
|
||||
nodeIf := node.(*tmplParser.IfNode)
|
||||
omh.handleTemplateBranchNode(fset, nodeIf.BranchNode)
|
||||
case tmplParser.NodeRange:
|
||||
nodeRange := node.(*tmplParser.RangeNode)
|
||||
omh.handleTemplateBranchNode(fset, nodeRange.BranchNode)
|
||||
case tmplParser.NodeWith:
|
||||
nodeWith := node.(*tmplParser.WithNode)
|
||||
omh.handleTemplateBranchNode(fset, nodeWith.BranchNode)
|
||||
|
||||
case tmplParser.NodeCommand:
|
||||
nodeCommand := node.(*tmplParser.CommandNode)
|
||||
|
||||
omh.handleTemplateFileNodes(fset, nodeCommand.Args)
|
||||
|
||||
if len(nodeCommand.Args) != 2 {
|
||||
return
|
||||
}
|
||||
|
||||
nodeChain, ok := nodeCommand.Args[0].(*tmplParser.ChainNode)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
nodeString, ok := nodeCommand.Args[1].(*tmplParser.StringNode)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
nodeIdent, ok := nodeChain.Node.(*tmplParser.IdentifierNode)
|
||||
if !ok || nodeIdent.Ident != "ctx" {
|
||||
return
|
||||
}
|
||||
|
||||
if len(nodeChain.Field) != 2 || nodeChain.Field[0] != "Locale" || !isLocaleTrFunction(nodeChain.Field[1]) {
|
||||
return
|
||||
}
|
||||
|
||||
// found interesting string
|
||||
// the column numbers are a bit "off", but much better than nothing
|
||||
omh(fset, token.Pos(nodeString.Pos), nodeString.Text)
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (omh OnMsgidHandler) handleTemplatePipeNode(fset *token.FileSet, pipeNode *tmplParser.PipeNode) {
|
||||
if pipeNode == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: we can't pass `pipeNode.Cmds` to handleTemplateFileNodes due to incompatible argument types
|
||||
for _, node := range pipeNode.Cmds {
|
||||
omh.handleTemplateNode(fset, node)
|
||||
}
|
||||
}
|
||||
|
||||
func (omh OnMsgidHandler) handleTemplateBranchNode(fset *token.FileSet, branchNode tmplParser.BranchNode) {
|
||||
omh.handleTemplatePipeNode(fset, branchNode.Pipe)
|
||||
omh.handleTemplateFileNodes(fset, branchNode.List.Nodes)
|
||||
if branchNode.ElseList != nil {
|
||||
omh.handleTemplateFileNodes(fset, branchNode.ElseList.Nodes)
|
||||
}
|
||||
}
|
||||
|
||||
func (omh OnMsgidHandler) handleTemplateFileNodes(fset *token.FileSet, nodes []tmplParser.Node) {
|
||||
for _, node := range nodes {
|
||||
omh.handleTemplateNode(fset, node)
|
||||
}
|
||||
}
|
||||
|
||||
func (omh OnMsgidHandler) HandleTemplateFile(fname string, src any) error {
|
||||
var tmplContent []byte
|
||||
switch src2 := src.(type) {
|
||||
case nil:
|
||||
var err error
|
||||
tmplContent, err = os.ReadFile(fname)
|
||||
if err != nil {
|
||||
return LocatedError{
|
||||
Location: fname,
|
||||
Kind: "ReadFile",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
case []byte:
|
||||
tmplContent = src2
|
||||
case string:
|
||||
// SAFETY: we do not modify tmplContent below
|
||||
tmplContent = util.UnsafeStringToBytes(src2)
|
||||
default:
|
||||
panic("invalid type for 'src'")
|
||||
}
|
||||
|
||||
fset := token.NewFileSet()
|
||||
fset.AddFile(fname, 1, len(tmplContent)).SetLinesForContent(tmplContent)
|
||||
// SAFETY: we do not modify tmplContent2 below
|
||||
tmplContent2 := util.UnsafeBytesToString(tmplContent)
|
||||
|
||||
tmpl := template.New(fname)
|
||||
tmpl.Funcs(fjTemplates.NewFuncMap())
|
||||
tmplParsed, err := tmpl.Parse(tmplContent2)
|
||||
if err != nil {
|
||||
return LocatedError{
|
||||
Location: fname,
|
||||
Kind: "Template parser",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
omh.handleTemplateFileNodes(fset, tmplParsed.Tree.Root.Nodes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// This command assumes that we get started from the project root directory
|
||||
//
|
||||
// Possible command line flags:
|
||||
//
|
||||
// --allow-missing-msgids don't return an error code if missing message IDs are found
|
||||
//
|
||||
// EXIT CODES:
|
||||
//
|
||||
// 0 success, no issues found
|
||||
// 1 unable to walk directory tree
|
||||
// 2 unable to parse locale ini/json files
|
||||
// 3 unable to parse go or text/template files
|
||||
// 4 found missing message IDs
|
||||
//
|
||||
//nolint:forbidigo
|
||||
func main() {
|
||||
allowMissingMsgids := false
|
||||
for _, arg := range os.Args[1:] {
|
||||
if arg == "--allow-missing-msgids" {
|
||||
allowMissingMsgids = true
|
||||
}
|
||||
}
|
||||
|
||||
onError := func(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
msgids := make(container.Set[string])
|
||||
onMsgid := func(trKey, trValue string) error {
|
||||
msgids[trKey] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
localeFile := filepath.Join(filepath.Join("options", "locale"), "locale_en-US.ini")
|
||||
localeContent, err := os.ReadFile(localeFile)
|
||||
if err != nil {
|
||||
fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if err = locale.IterateMessagesContent(localeContent, onMsgid); err != nil {
|
||||
fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
localeFile = filepath.Join(filepath.Join("options", "locale_next"), "locale_en-US.json")
|
||||
localeContent, err = os.ReadFile(localeFile)
|
||||
if err != nil {
|
||||
fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if err := locale.IterateMessagesNextContent(localeContent, onMsgid); err != nil {
|
||||
fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
gotAnyMsgidError := false
|
||||
|
||||
omh := OnMsgidHandler(func(fset *token.FileSet, pos token.Pos, msgid string) {
|
||||
if !msgids.Contains(msgid) {
|
||||
gotAnyMsgidError = true
|
||||
fmt.Printf("%s:\tmissing msgid: %s\n", fset.Position(pos).String(), msgid)
|
||||
}
|
||||
})
|
||||
|
||||
if err := filepath.WalkDir(".", func(fpath string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
name := d.Name()
|
||||
if d.IsDir() {
|
||||
if name == "docker" || name == ".git" || name == "node_modules" {
|
||||
return fs.SkipDir
|
||||
}
|
||||
} else if name == "bindata.go" {
|
||||
// skip false positives
|
||||
} else if strings.HasSuffix(name, ".go") {
|
||||
onError(omh.HandleGoFile(fpath, nil))
|
||||
} else if strings.HasSuffix(name, ".tmpl") {
|
||||
if strings.HasPrefix(fpath, "tests") && strings.HasSuffix(name, ".ini.tmpl") {
|
||||
// skip false positives
|
||||
} else {
|
||||
onError(omh.HandleTemplateFile(fpath, nil))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
fmt.Printf("walkdir ERROR: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !allowMissingMsgids && gotAnyMsgidError {
|
||||
os.Exit(4)
|
||||
}
|
||||
}
|
44
build/lint-locale-usage/lint-locale-usage_test.go
Normal file
44
build/lint-locale-usage/lint-locale-usage_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func HandleGoFileWrapped(t *testing.T, fname, src string) []string {
|
||||
var ret []string
|
||||
omh := OnMsgidHandler(func(fset *token.FileSet, pos token.Pos, msgid string) {
|
||||
ret = append(ret, msgid)
|
||||
})
|
||||
require.NoError(t, omh.HandleGoFile(fname, src))
|
||||
return ret
|
||||
}
|
||||
|
||||
func HandleTemplateFileWrapped(t *testing.T, fname, src string) []string {
|
||||
var ret []string
|
||||
omh := OnMsgidHandler(func(fset *token.FileSet, pos token.Pos, msgid string) {
|
||||
ret = append(ret, msgid)
|
||||
})
|
||||
require.NoError(t, omh.HandleTemplateFile(fname, src))
|
||||
return ret
|
||||
}
|
||||
|
||||
func TestUsagesParser(t *testing.T) {
|
||||
t.Run("go, simple", func(t *testing.T) {
|
||||
assert.EqualValues(t,
|
||||
[]string{"what.an.example"},
|
||||
HandleGoFileWrapped(t, "<g1>", "package main\nfunc Render(ctx *context.Context) string { return ctx.Tr(\"what.an.example\"); }\n"))
|
||||
})
|
||||
|
||||
t.Run("template, simple", func(t *testing.T) {
|
||||
assert.EqualValues(t,
|
||||
[]string{"what.an.example"},
|
||||
HandleTemplateFileWrapped(t, "<t1>", "{{ ctx.Locale.Tr \"what.an.example\" }}\n"))
|
||||
})
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json" //nolint:depguard
|
||||
"fmt"
|
||||
"html"
|
||||
"io/fs"
|
||||
|
@ -15,9 +14,10 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/locale"
|
||||
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
"gopkg.in/ini.v1" //nolint:depguard
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -98,49 +98,28 @@ func checkValue(trKey, value string) []string {
|
|||
}
|
||||
|
||||
func checkLocaleContent(localeContent []byte) []string {
|
||||
// Same configuration as Forgejo uses.
|
||||
cfg := ini.Empty(ini.LoadOptions{
|
||||
IgnoreContinuation: true,
|
||||
})
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
errors := []string{}
|
||||
|
||||
if err := cfg.Append(localeContent); err != nil {
|
||||
if err := locale.IterateMessagesContent(localeContent, func(trKey, trValue string) error {
|
||||
errors = append(errors, checkValue(trKey, trValue)...)
|
||||
return nil
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
errors := []string{}
|
||||
for _, section := range cfg.Sections() {
|
||||
for _, key := range section.Keys() {
|
||||
var trKey string
|
||||
if section.Name() == "" || section.Name() == "DEFAULT" || section.Name() == "common" {
|
||||
trKey = key.Name()
|
||||
} else {
|
||||
trKey = section.Name() + "." + key.Name()
|
||||
}
|
||||
|
||||
errors = append(errors, checkValue(trKey, key.Value())...)
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
func checkLocaleNextContent(data map[string]any, trKey ...string) []string {
|
||||
func checkLocaleNextContent(localeContent []byte) []string {
|
||||
errors := []string{}
|
||||
for key, value := range data {
|
||||
currentKey := key
|
||||
if len(trKey) == 1 {
|
||||
currentKey = trKey[0] + "." + key
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
errors = append(errors, checkValue(currentKey, value)...)
|
||||
case map[string]any:
|
||||
errors = append(errors, checkLocaleNextContent(value, currentKey)...)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unexpected type during linting locale next: %s - %T", currentKey, value))
|
||||
}
|
||||
if err := locale.IterateMessagesNextContent(localeContent, func(trKey, trValue string) error {
|
||||
errors = append(errors, checkValue(trKey, trValue)...)
|
||||
return nil
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
|
@ -168,6 +147,7 @@ func main() {
|
|||
|
||||
localeContent, err := os.ReadFile(filepath.Join(localeDir, localeFile.Name()))
|
||||
if err != nil {
|
||||
fmt.Println(localeFile.Name())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
@ -195,15 +175,11 @@ func main() {
|
|||
for _, localeFile := range localeFiles {
|
||||
localeContent, err := os.ReadFile(filepath.Join(localeDir, localeFile.Name()))
|
||||
if err != nil {
|
||||
fmt.Println(localeFile.Name())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var localeData map[string]any
|
||||
if err := json.Unmarshal(localeContent, &localeData); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := checkLocaleNextContent(localeData); len(err) > 0 {
|
||||
if err := checkLocaleNextContent(localeContent); len(err) > 0 {
|
||||
fmt.Println(localeFile.Name())
|
||||
fmt.Println(strings.Join(err, "\n"))
|
||||
fmt.Println()
|
|
@ -69,26 +69,26 @@ func TestNextLocalizationPolicy(t *testing.T) {
|
|||
initRemoveTags()
|
||||
|
||||
t.Run("Nested locales", func(t *testing.T) {
|
||||
assert.Empty(t, checkLocaleNextContent(map[string]any{
|
||||
"settings": map[string]any{
|
||||
"hidden_comment_types_description": `Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "<user> added/removed <label>" comments.`,
|
||||
},
|
||||
}))
|
||||
assert.Empty(t, checkLocaleNextContent([]byte(`{
|
||||
"settings": {
|
||||
"hidden_comment_types_description": "Comment types checked here will not be shown inside issue pages. Checking \"Label\" for example removes all \"<user> added/removed <label>\" comments."
|
||||
}
|
||||
}`)))
|
||||
|
||||
assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent(map[string]any{
|
||||
"settings": map[string]any{
|
||||
"hidden_comment_types_description": `"<not-an-allowed-key> <label>"`,
|
||||
},
|
||||
}))
|
||||
assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent([]byte(`{
|
||||
"settings": {
|
||||
"hidden_comment_types_description": "\"<not-an-allowed-key> <label>\""
|
||||
}
|
||||
}`)))
|
||||
})
|
||||
|
||||
t.Run("Flat locales", func(t *testing.T) {
|
||||
assert.Empty(t, checkLocaleNextContent(map[string]any{
|
||||
"settings.hidden_comment_types_description": `Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "<user> added/removed <label>" comments.`,
|
||||
}))
|
||||
assert.Empty(t, checkLocaleNextContent([]byte(`{
|
||||
"settings.hidden_comment_types_description": "Comment types checked here will not be shown inside issue pages. Checking \"Label\" for example removes all \"<user> added/removed <label>\" comments."
|
||||
}`)))
|
||||
|
||||
assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent(map[string]any{
|
||||
"settings.hidden_comment_types_description": `"<not-an-allowed-key> <label>"`,
|
||||
}))
|
||||
assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent([]byte(`{
|
||||
"settings.hidden_comment_types_description": "\"<not-an-allowed-key> <label>\""
|
||||
}`)))
|
||||
})
|
||||
}
|
74
modules/locale/utils.go
Normal file
74
modules/locale/utils.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// extracted from `/build/lint-locale.go`, `/build/lint-locale-usage.go`
|
||||
|
||||
package locale
|
||||
|
||||
import (
|
||||
"encoding/json" //nolint:depguard
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/ini.v1" //nolint:depguard
|
||||
)
|
||||
|
||||
func IterateMessagesContent(localeContent []byte, onMsgid func(string, string) error) error {
|
||||
// Same configuration as Forgejo uses.
|
||||
cfg := ini.Empty(ini.LoadOptions{
|
||||
IgnoreContinuation: true,
|
||||
})
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
|
||||
if err := cfg.Append(localeContent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, section := range cfg.Sections() {
|
||||
for _, key := range section.Keys() {
|
||||
var trKey string
|
||||
if section.Name() == "" || section.Name() == "DEFAULT" || section.Name() == "common" {
|
||||
trKey = key.Name()
|
||||
} else {
|
||||
trKey = section.Name() + "." + key.Name()
|
||||
}
|
||||
if err := onMsgid(trKey, key.Value()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func iterateMessagesNextInner(onMsgid func(string, string) error, data map[string]any, trKey ...string) error {
|
||||
for key, value := range data {
|
||||
currentKey := key
|
||||
if len(trKey) == 1 {
|
||||
currentKey = trKey[0] + "." + key
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if err := onMsgid(currentKey, value); err != nil {
|
||||
return err
|
||||
}
|
||||
case map[string]any:
|
||||
if err := iterateMessagesNextInner(onMsgid, value, currentKey); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected type: %s - %T", currentKey, value)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IterateMessagesNextContent(localeContent []byte, onMsgid func(string, string) error) error {
|
||||
var localeData map[string]any
|
||||
if err := json.Unmarshal(localeContent, &localeData); err != nil {
|
||||
return err
|
||||
}
|
||||
return iterateMessagesNextInner(onMsgid, localeData)
|
||||
}
|
|
@ -12,4 +12,16 @@ type DispatchWorkflowOption struct {
|
|||
Ref string `json:"ref"`
|
||||
// Input keys and values configured in the workflow file.
|
||||
Inputs map[string]string `json:"inputs"`
|
||||
// Flag to return the run info
|
||||
// default: false
|
||||
ReturnRunInfo bool `json:"return_run_info"`
|
||||
}
|
||||
|
||||
// DispatchWorkflowRun represents a workflow run
|
||||
// swagger:model
|
||||
type DispatchWorkflowRun struct {
|
||||
// the workflow run id
|
||||
ID int64 `json:"id"`
|
||||
// the jobs name
|
||||
Jobs []string `json:"jobs"`
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
{
|
||||
"home.welcome.no_activity": "No activity",
|
||||
"home.welcome.activity_hint": "There is nothing in your feed yet. Your actions and activity from repositories that you watch will show up here.",
|
||||
"home.explore_repos": "Explore repositories",
|
||||
"home.explore_users": "Explore users",
|
||||
"home.explore_orgs": "Explore organizations",
|
||||
"repo.pulls.merged_title_desc": {
|
||||
"one": "merged %[1]d commit from <code>%[2]s</code> into <code>%[3]s</code> %[4]s",
|
||||
"other": "merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s"
|
||||
},
|
||||
"repo.pulls.title_desc": {
|
||||
"one": "wants to merge %[1]d commit from <code>%[2]s</code> into <code id=\"%[4]s\">%[3]s</code>",
|
||||
"other": "wants to merge %[1]d commits from <code>%[2]s</code> into <code id=\"%[4]s\">%[3]s</code>"
|
||||
},
|
||||
"search.milestone_kind": "Search milestones…",
|
||||
"incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.",
|
||||
"themes.names.forgejo-auto": "Forgejo (follow system theme)",
|
||||
"themes.names.forgejo-light": "Forgejo light",
|
||||
"themes.names.forgejo-dark": "Forgejo dark"
|
||||
"home.welcome.no_activity": "No activity",
|
||||
"home.welcome.activity_hint": "There is nothing in your feed yet. Your actions and activity from repositories that you watch will show up here.",
|
||||
"home.explore_repos": "Explore repositories",
|
||||
"home.explore_users": "Explore users",
|
||||
"home.explore_orgs": "Explore organizations",
|
||||
"repo.pulls.merged_title_desc": {
|
||||
"one": "merged %[1]d commit from <code>%[2]s</code> into <code>%[3]s</code> %[4]s",
|
||||
"other": "merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s"
|
||||
},
|
||||
"repo.pulls.title_desc": {
|
||||
"one": "wants to merge %[1]d commit from <code>%[2]s</code> into <code id=\"%[4]s\">%[3]s</code>",
|
||||
"other": "wants to merge %[1]d commits from <code>%[2]s</code> into <code id=\"%[4]s\">%[3]s</code>"
|
||||
},
|
||||
"search.milestone_kind": "Search milestones…",
|
||||
"incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.",
|
||||
"themes.names.forgejo-auto": "Forgejo (follow system theme)",
|
||||
"themes.names.forgejo-light": "Forgejo light",
|
||||
"themes.names.forgejo-dark": "Forgejo dark"
|
||||
}
|
||||
|
|
200
package-lock.json
generated
200
package-lock.json
generated
|
@ -32,7 +32,7 @@
|
|||
"idiomorph": "0.3.0",
|
||||
"jquery": "3.7.1",
|
||||
"katex": "0.16.21",
|
||||
"mermaid": "11.4.1",
|
||||
"mermaid": "11.5.0",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"minimatch": "10.0.1",
|
||||
"monaco-editor": "0.52.2",
|
||||
|
@ -63,13 +63,13 @@
|
|||
"devDependencies": {
|
||||
"@axe-core/playwright": "4.10.1",
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@stoplight/spectral-cli": "6.14.2",
|
||||
"@playwright/test": "1.51.0",
|
||||
"@stoplight/spectral-cli": "6.14.3",
|
||||
"@stylistic/eslint-plugin-js": "4.2.0",
|
||||
"@stylistic/stylelint-plugin": "3.1.2",
|
||||
"@typescript-eslint/parser": "8.26.0",
|
||||
"@vitejs/plugin-vue": "5.1.5",
|
||||
"@vitest/coverage-v8": "3.0.5",
|
||||
"@vitest/coverage-v8": "3.0.8",
|
||||
"@vitest/eslint-plugin": "1.1.25",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"eslint": "9.22.0",
|
||||
|
@ -88,7 +88,7 @@
|
|||
"eslint-plugin-vue-scoped-css": "2.9.0",
|
||||
"eslint-plugin-wc": "2.2.1",
|
||||
"globals": "16.0.0",
|
||||
"happy-dom": "17.4.3",
|
||||
"happy-dom": "17.4.4",
|
||||
"license-checker-rseidelsohn": "4.4.2",
|
||||
"markdownlint-cli": "0.44.0",
|
||||
"postcss-html": "1.8.0",
|
||||
|
@ -100,7 +100,7 @@
|
|||
"typescript": "5.7.3",
|
||||
"typescript-eslint": "8.26.0",
|
||||
"vite-string-plugin": "1.3.4",
|
||||
"vitest": "3.0.5"
|
||||
"vitest": "3.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
|
@ -1684,13 +1684,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.50.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz",
|
||||
"integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==",
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.0.tgz",
|
||||
"integrity": "sha512-dJ0dMbZeHhI+wb77+ljx/FeC8VBP6j/rj9OAojO08JI80wTZy6vRk9KvHKiDCUh4iMpEiseMgqRBIeW+eKX6RA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.50.1"
|
||||
"playwright": "1.51.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
@ -2130,15 +2130,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@stoplight/spectral-cli": {
|
||||
"version": "6.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.14.2.tgz",
|
||||
"integrity": "sha512-yn49Tkin/Zzjwt39CbQvj3NZVolvXrjO3OLNn+Yd+LhQE5C96QNsULuE4q98e7+WRcLu4gK+Z0l5CxYNtsoPtw==",
|
||||
"version": "6.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.14.3.tgz",
|
||||
"integrity": "sha512-vKy7d2yqBfOf94uB6KXzujDl6/qjXa8mCQ6cfsQ8xYsoArZN9iBHpS3271hR5IyTm3R1GwMgaSZ1h0sfZjZrZw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@stoplight/json": "~3.21.0",
|
||||
"@stoplight/path": "1.3.2",
|
||||
"@stoplight/spectral-core": "^1.19.4",
|
||||
"@stoplight/spectral-core": "^1.19.5",
|
||||
"@stoplight/spectral-formatters": "^1.4.1",
|
||||
"@stoplight/spectral-parsers": "^1.0.4",
|
||||
"@stoplight/spectral-ref-resolver": "^1.0.4",
|
||||
|
@ -3229,9 +3229,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.5.tgz",
|
||||
"integrity": "sha512-zOOWIsj5fHh3jjGwQg+P+J1FW3s4jBu1Zqga0qW60yutsBtqEqNEJKWYh7cYn1yGD+1bdPsPdC/eL4eVK56xMg==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz",
|
||||
"integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -3252,8 +3252,8 @@
|
|||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "3.0.5",
|
||||
"vitest": "3.0.5"
|
||||
"@vitest/browser": "3.0.8",
|
||||
"vitest": "3.0.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
|
@ -3293,15 +3293,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz",
|
||||
"integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz",
|
||||
"integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"chai": "^5.1.2",
|
||||
"@vitest/spy": "3.0.8",
|
||||
"@vitest/utils": "3.0.8",
|
||||
"chai": "^5.2.0",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
|
@ -3309,13 +3309,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz",
|
||||
"integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz",
|
||||
"integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/spy": "3.0.8",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.17"
|
||||
},
|
||||
|
@ -3363,9 +3363,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz",
|
||||
"integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz",
|
||||
"integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -3376,29 +3376,29 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz",
|
||||
"integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz",
|
||||
"integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "3.0.5",
|
||||
"pathe": "^2.0.2"
|
||||
"@vitest/utils": "3.0.8",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz",
|
||||
"integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz",
|
||||
"integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.5",
|
||||
"@vitest/pretty-format": "3.0.8",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.2"
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
|
@ -3415,9 +3415,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz",
|
||||
"integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz",
|
||||
"integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -3428,14 +3428,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz",
|
||||
"integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz",
|
||||
"integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.5",
|
||||
"loupe": "^3.1.2",
|
||||
"@vitest/pretty-format": "3.0.8",
|
||||
"loupe": "^3.1.3",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
|
@ -7590,9 +7590,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/happy-dom": {
|
||||
"version": "17.4.3",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.4.3.tgz",
|
||||
"integrity": "sha512-8mDGIKxi2hAg0DkEYjBHPi5QykWiqdNNQQWrwLXLfro1eAZk8+lSnzbUrnU25bamG9PjEQGoFrA32ezQNJQdww==",
|
||||
"version": "17.4.4",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.4.4.tgz",
|
||||
"integrity": "sha512-/Pb0ctk3HTZ5xEL3BZ0hK1AqDSAUuRQitOmROPHhfUYEWpmTImwfD8vFDGADmMAX0JYgbcgxWoLFKtsWhcpuVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -9489,37 +9489,43 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "11.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz",
|
||||
"integrity": "sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A==",
|
||||
"version": "11.5.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.5.0.tgz",
|
||||
"integrity": "sha512-IYhyukID3zzDj1EihKiN1lp+PXNImoJ3Iyz73qeDAgnus4BNGsJV1n471P4PyeGxPVONerZxignwGxGTSwZnlg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^7.0.1",
|
||||
"@iconify/utils": "^2.1.32",
|
||||
"@braintree/sanitize-url": "^7.0.4",
|
||||
"@iconify/utils": "^2.1.33",
|
||||
"@mermaid-js/parser": "^0.3.0",
|
||||
"@types/d3": "^7.4.3",
|
||||
"cytoscape": "^3.29.2",
|
||||
"cytoscape": "^3.29.3",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-fcose": "^2.2.0",
|
||||
"d3": "^7.9.0",
|
||||
"d3-sankey": "^0.12.3",
|
||||
"dagre-d3-es": "7.0.11",
|
||||
"dayjs": "^1.11.10",
|
||||
"dompurify": "^3.2.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"dompurify": "^3.2.4",
|
||||
"katex": "^0.16.9",
|
||||
"khroma": "^2.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^13.0.2",
|
||||
"marked": "^15.0.7",
|
||||
"roughjs": "^4.6.6",
|
||||
"stylis": "^4.3.1",
|
||||
"stylis": "^4.3.6",
|
||||
"ts-dedent": "^2.2.0",
|
||||
"uuid": "^9.0.1"
|
||||
"uuid": "^11.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mermaid/node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mermaid/node_modules/marked": {
|
||||
"version": "13.0.3",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz",
|
||||
"integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==",
|
||||
"version": "15.0.7",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.7.tgz",
|
||||
"integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
|
@ -10862,13 +10868,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.50.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz",
|
||||
"integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==",
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.0.tgz",
|
||||
"integrity": "sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.50.1"
|
||||
"playwright-core": "1.51.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
@ -10881,9 +10887,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.50.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz",
|
||||
"integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==",
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.0.tgz",
|
||||
"integrity": "sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
@ -13917,16 +13923,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
"uuid": "dist/esm/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
|
@ -14017,16 +14023,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz",
|
||||
"integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz",
|
||||
"integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cac": "^6.7.14",
|
||||
"debug": "^4.4.0",
|
||||
"es-module-lexer": "^1.6.0",
|
||||
"pathe": "^2.0.2",
|
||||
"pathe": "^2.0.3",
|
||||
"vite": "^5.0.0 || ^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -14538,31 +14544,31 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz",
|
||||
"integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz",
|
||||
"integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "3.0.5",
|
||||
"@vitest/mocker": "3.0.5",
|
||||
"@vitest/pretty-format": "^3.0.5",
|
||||
"@vitest/runner": "3.0.5",
|
||||
"@vitest/snapshot": "3.0.5",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"chai": "^5.1.2",
|
||||
"@vitest/expect": "3.0.8",
|
||||
"@vitest/mocker": "3.0.8",
|
||||
"@vitest/pretty-format": "^3.0.8",
|
||||
"@vitest/runner": "3.0.8",
|
||||
"@vitest/snapshot": "3.0.8",
|
||||
"@vitest/spy": "3.0.8",
|
||||
"@vitest/utils": "3.0.8",
|
||||
"chai": "^5.2.0",
|
||||
"debug": "^4.4.0",
|
||||
"expect-type": "^1.1.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.2",
|
||||
"pathe": "^2.0.3",
|
||||
"std-env": "^3.8.0",
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^0.3.2",
|
||||
"tinypool": "^1.0.2",
|
||||
"tinyrainbow": "^2.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vite-node": "3.0.5",
|
||||
"vite-node": "3.0.8",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -14578,8 +14584,8 @@
|
|||
"@edge-runtime/vm": "*",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@vitest/browser": "3.0.5",
|
||||
"@vitest/ui": "3.0.5",
|
||||
"@vitest/browser": "3.0.8",
|
||||
"@vitest/ui": "3.0.8",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
|
12
package.json
12
package.json
|
@ -31,7 +31,7 @@
|
|||
"idiomorph": "0.3.0",
|
||||
"jquery": "3.7.1",
|
||||
"katex": "0.16.21",
|
||||
"mermaid": "11.4.1",
|
||||
"mermaid": "11.5.0",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"minimatch": "10.0.1",
|
||||
"monaco-editor": "0.52.2",
|
||||
|
@ -62,13 +62,13 @@
|
|||
"devDependencies": {
|
||||
"@axe-core/playwright": "4.10.1",
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@stoplight/spectral-cli": "6.14.2",
|
||||
"@playwright/test": "1.51.0",
|
||||
"@stoplight/spectral-cli": "6.14.3",
|
||||
"@stylistic/eslint-plugin-js": "4.2.0",
|
||||
"@stylistic/stylelint-plugin": "3.1.2",
|
||||
"@typescript-eslint/parser": "8.26.0",
|
||||
"@vitejs/plugin-vue": "5.1.5",
|
||||
"@vitest/coverage-v8": "3.0.5",
|
||||
"@vitest/coverage-v8": "3.0.8",
|
||||
"@vitest/eslint-plugin": "1.1.25",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"eslint": "9.22.0",
|
||||
|
@ -87,7 +87,7 @@
|
|||
"eslint-plugin-vue-scoped-css": "2.9.0",
|
||||
"eslint-plugin-wc": "2.2.1",
|
||||
"globals": "16.0.0",
|
||||
"happy-dom": "17.4.3",
|
||||
"happy-dom": "17.4.4",
|
||||
"license-checker-rseidelsohn": "4.4.2",
|
||||
"markdownlint-cli": "0.44.0",
|
||||
"postcss-html": "1.8.0",
|
||||
|
@ -99,7 +99,7 @@
|
|||
"typescript": "5.7.3",
|
||||
"typescript-eslint": "8.26.0",
|
||||
"vite-string-plugin": "1.3.4",
|
||||
"vitest": "3.0.5"
|
||||
"vitest": "3.0.8"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
|
|
10
poetry.lock
generated
10
poetry.lock
generated
|
@ -404,13 +404,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "yamllint"
|
||||
version = "1.35.1"
|
||||
version = "1.36.0"
|
||||
description = "A linter for YAML files."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "yamllint-1.35.1-py3-none-any.whl", hash = "sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3"},
|
||||
{file = "yamllint-1.35.1.tar.gz", hash = "sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd"},
|
||||
{file = "yamllint-1.36.0-py3-none-any.whl", hash = "sha256:22ab6a7db9d5f0954bd7a50ef70a41c1465c61f955893070b0767c0ec6693b7b"},
|
||||
{file = "yamllint-1.36.0.tar.gz", hash = "sha256:3835a65994858679ea06fd998dd968c3f71935cd93742990405999d888e21130"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -423,4 +423,4 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "f0f2f5f83d2277f5c289c62b96a2526fc89a7d54a88ac5fd044a022486c1cbb7"
|
||||
content-hash = "c8ff4ba89b1588b4222e79f9832b3596b5c5d5a2fce8c5659b90bd31bd0f61b0"
|
||||
|
|
|
@ -6,7 +6,7 @@ python = "^3.10"
|
|||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
djlint = "1.36.4"
|
||||
yamllint = "1.35.1"
|
||||
yamllint = "1.36.0"
|
||||
codespell = "2.4.1"
|
||||
|
||||
[tool.djlint]
|
||||
|
|
|
@ -670,7 +670,8 @@ func DispatchWorkflow(ctx *context.APIContext) {
|
|||
return opt.Inputs[key]
|
||||
}
|
||||
|
||||
if err := workflow.Dispatch(ctx, inputGetter, ctx.Repo.Repository, ctx.Doer); err != nil {
|
||||
run, jobs, err := workflow.Dispatch(ctx, inputGetter, ctx.Repo.Repository, ctx.Doer)
|
||||
if err != nil {
|
||||
if actions_service.IsInputRequiredErr(err) {
|
||||
ctx.Error(http.StatusBadRequest, "workflow.Dispatch", err)
|
||||
} else {
|
||||
|
@ -679,5 +680,14 @@ func DispatchWorkflow(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusNoContent, nil)
|
||||
workflowRun := &api.DispatchWorkflowRun{
|
||||
ID: run.ID,
|
||||
Jobs: jobs,
|
||||
}
|
||||
|
||||
if opt.ReturnRunInfo {
|
||||
ctx.JSON(http.StatusCreated, workflowRun)
|
||||
} else {
|
||||
ctx.JSON(http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,3 +39,10 @@ type swaggerRunJobList struct {
|
|||
// in:body
|
||||
Body []*api.ActionRunJob `json:"body"`
|
||||
}
|
||||
|
||||
// DispatchWorkflowRun is a Workflow Run after dispatching
|
||||
// swagger:response DispatchWorkflowRun
|
||||
type swaggerDispatchWorkflowRun struct {
|
||||
// in:body
|
||||
Body *api.DispatchWorkflowRun `json:"body"`
|
||||
}
|
||||
|
|
|
@ -46,7 +46,8 @@ func ManualRunWorkflow(ctx *context_module.Context) {
|
|||
return ctx.Req.PostFormValue(formKey)
|
||||
}
|
||||
|
||||
if err := workflow.Dispatch(ctx, formKeyGetter, ctx.Repo.Repository, ctx.Doer); err != nil {
|
||||
_, _, err = workflow.Dispatch(ctx, formKeyGetter, ctx.Repo.Repository, ctx.Doer)
|
||||
if err != nil {
|
||||
if actions_service.IsInputRequiredErr(err) {
|
||||
ctx.Flash.Error(ctx.Locale.Tr("actions.workflow.dispatch.input_required", err.(actions_service.InputRequiredErr).Name))
|
||||
ctx.Redirect(location)
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/webhook"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
|
||||
|
@ -49,15 +50,15 @@ type Workflow struct {
|
|||
|
||||
type InputValueGetter func(key string) string
|
||||
|
||||
func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGetter, repo *repo_model.Repository, doer *user.User) error {
|
||||
func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGetter, repo *repo_model.Repository, doer *user.User) (r *actions_model.ActionRun, j []string, err error) {
|
||||
content, err := actions.GetContentFromEntry(entry.GitEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
wf, err := act_model.ReadWorkflow(bytes.NewReader(content))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
fullWorkflowID := ".forgejo/workflows/" + entry.WorkflowID
|
||||
|
@ -79,7 +80,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
|
|||
if len(name) == 0 {
|
||||
name = key
|
||||
}
|
||||
return InputRequiredErr{Name: name}
|
||||
return nil, nil, InputRequiredErr{Name: name}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
@ -92,9 +93,11 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
|
|||
}
|
||||
|
||||
if int64(len(inputs)) > setting.Actions.LimitDispatchInputs {
|
||||
return errors.New("to many inputs")
|
||||
return nil, nil, errors.New("to many inputs")
|
||||
}
|
||||
|
||||
jobNames := util.KeysOfMap(wf.Jobs)
|
||||
|
||||
payload := &structs.WorkflowDispatchPayload{
|
||||
Inputs: inputs,
|
||||
Ref: entry.Ref,
|
||||
|
@ -105,7 +108,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
|
|||
|
||||
p, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
run := &actions_model.ActionRun{
|
||||
|
@ -126,15 +129,15 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
|
|||
|
||||
vars, err := actions_model.GetVariablesOfRun(ctx, run)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
jobs, err := jobparser.Parse(content, jobparser.WithVars(vars))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return actions_model.InsertRun(ctx, run, jobs)
|
||||
return run, jobNames, actions_model.InsertRun(ctx, run, jobs)
|
||||
}
|
||||
|
||||
func GetWorkflowFromCommit(gitRepo *git.Repository, ref, workflowID string) (*Workflow, error) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"time"
|
||||
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
base "code.gitea.io/gitea/modules/migration"
|
||||
|
@ -543,11 +544,19 @@ func (g *GitlabDownloader) GetComments(commentable base.Commentable) ([]*base.Co
|
|||
}
|
||||
|
||||
for _, stateEvent := range stateEvents {
|
||||
// If the user is deleted, then `stateEvent.User == nil` holds. Fallback
|
||||
// to the Ghost user in that case.
|
||||
posterID := int64(user_model.GhostUserID)
|
||||
posterName := user_model.GhostUserName
|
||||
if stateEvent.User != nil {
|
||||
posterID = int64(stateEvent.User.ID)
|
||||
posterName = stateEvent.User.Username
|
||||
}
|
||||
comment := &base.Comment{
|
||||
IssueIndex: commentable.GetLocalIndex(),
|
||||
Index: int64(stateEvent.ID),
|
||||
PosterID: int64(stateEvent.User.ID),
|
||||
PosterName: stateEvent.User.Username,
|
||||
PosterID: posterID,
|
||||
PosterName: posterName,
|
||||
Content: "",
|
||||
Created: *stateEvent.CreatedAt,
|
||||
}
|
||||
|
|
|
@ -39,21 +39,13 @@
|
|||
<i class="icon">{{svg "octicon-filter" 16}}</i>
|
||||
<input name="search" placeholder="{{ctx.Locale.Tr "repo.filter_branch_and_tag"}}...">
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="ui grid">
|
||||
<div class="two column row">
|
||||
<a class="reference column" href="#" data-target=".base-branch-list">
|
||||
<span class="text black">
|
||||
{{svg "octicon-git-branch" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.branches"}}
|
||||
</span>
|
||||
</a>
|
||||
<a class="reference column" href="#" data-target=".base-tag-list">
|
||||
<span class="text black">
|
||||
{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.tags"}}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="branch-tag-tab">
|
||||
<a class="branch-tag-item muted" href="#" data-target=".base-branch-list">
|
||||
{{svg "octicon-git-branch"}}{{ctx.Locale.Tr "repo.branches"}}
|
||||
</a>
|
||||
<a class="branch-tag-item muted" href="#" data-target=".base-tag-list">
|
||||
{{svg "octicon-tag"}}{{ctx.Locale.Tr "repo.tags"}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="scrolling menu reference-list-menu base-branch-list">
|
||||
{{range .Branches}}
|
||||
|
@ -108,21 +100,13 @@
|
|||
<i class="icon">{{svg "octicon-filter" 16}}</i>
|
||||
<input name="search" placeholder="{{ctx.Locale.Tr "repo.filter_branch_and_tag"}}...">
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="ui grid">
|
||||
<div class="two column row">
|
||||
<a class="reference column" href="#" data-target=".head-branch-list">
|
||||
<span class="text black">
|
||||
{{svg "octicon-git-branch" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.branches"}}
|
||||
</span>
|
||||
</a>
|
||||
<a class="reference column" href="#" data-target=".head-tag-list">
|
||||
<span class="text black">
|
||||
{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.tags"}}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="branch-tag-tab">
|
||||
<a class="branch-tag-item muted" href="#" data-target=".head-branch-list">
|
||||
{{svg "octicon-git-branch"}}{{ctx.Locale.Tr "repo.branches"}}
|
||||
</a>
|
||||
<a class="branch-tag-item muted" href="#" data-target=".head-tag-list">
|
||||
{{svg "octicon-tag"}}{{ctx.Locale.Tr "repo.tags"}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="scrolling menu reference-list-menu head-branch-list">
|
||||
{{range .HeadBranches}}
|
||||
|
|
|
@ -15,21 +15,13 @@
|
|||
<i class="icon">{{svg "octicon-filter" 16}}</i>
|
||||
<input name="search" placeholder="{{ctx.Locale.Tr "repo.filter_branch_and_tag"}}...">
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="ui grid">
|
||||
<div class="two column row">
|
||||
<a class="reference column muted" href="#" data-target="#branch-list">
|
||||
<span class="text black">
|
||||
{{svg "octicon-git-branch" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.branches"}}
|
||||
</span>
|
||||
</a>
|
||||
<a class="reference column muted" href="#" data-target="#tag-list">
|
||||
<span class="text">
|
||||
{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.tags"}}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="branch-tag-tab">
|
||||
<a class="branch-tag-item muted" href="#" data-target="#branch-list">
|
||||
{{svg "octicon-git-branch"}}{{ctx.Locale.Tr "repo.branches"}}
|
||||
</a>
|
||||
<a class="branch-tag-item muted" href="#" data-target="#tag-list">
|
||||
{{svg "octicon-tag"}}{{ctx.Locale.Tr "repo.tags"}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="branch-tag-divider"></div>
|
||||
<div id="branch-list" class="scrolling menu reference-list-menu {{if not .Issue}}new-issue{{end}}">
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
<h4 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "user.block.title"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<p>{{ctx.Locale.Tr "user.block.info_1"}}</p>
|
||||
<ul>
|
||||
<li>{{ctx.Locale.Tr "user.block.info_2"}}</li>
|
||||
<li>{{ctx.Locale.Tr "user.block.info_3"}}</li>
|
||||
<li>{{ctx.Locale.Tr "user.block.info_4"}}</li>
|
||||
<li>{{ctx.Locale.Tr "user.block.info_5"}}</li>
|
||||
<li>{{ctx.Locale.Tr "user.block.info_6"}}</li>
|
||||
<li>{{ctx.Locale.Tr "user.block.info_7"}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ui segment">
|
||||
<form class="ui form ignore-dirty" action="{{$.Link}}" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="block" />
|
||||
<div id="search-user-box" class="field ui fluid search input">
|
||||
<input class="prompt tw-mr-2" name="blockee" placeholder="{{ctx.Locale.Tr "search.user_kind"}}" autocomplete="off" required>
|
||||
<button class="ui red button">{{ctx.Locale.Tr "user.block.block"}}</button>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "user.block.note.title"}}</label>
|
||||
<input name="note">
|
||||
<p class="help">{{ctx.Locale.Tr "user.block.note.info"}}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<h4 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "user.block.list"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<div class="flex-list">
|
||||
{{range .UserBlocks}}
|
||||
<div class="flex-item">
|
||||
<div class="flex-item-leading">
|
||||
{{ctx.AvatarUtils.Avatar .Blockee}}
|
||||
</div>
|
||||
<div class="flex-item-main">
|
||||
<div class="flex-item-title">
|
||||
<a class="item" href="{{.Blockee.HTMLURL}}">{{.Blockee.GetDisplayName}}</a>
|
||||
</div>
|
||||
{{if .Note}}
|
||||
<div class="flex-item-body">
|
||||
<i>{{ctx.Locale.Tr "user.block.note"}}:</i> {{.Note}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="flex-item-trailing">
|
||||
<button class="ui compact mini button show-modal" data-modal="#block-user-note-modal" data-modal-modal-blockee="{{.Blockee.Name}}" data-modal-modal-note="{{.Note}}">{{ctx.Locale.Tr "user.block.note.edit"}}</button>
|
||||
<form action="{{$.Link}}" method="post">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="unblock" />
|
||||
<input type="hidden" name="blockee" value="{{.Blockee.Name}}" />
|
||||
<button class="ui compact mini button">{{ctx.Locale.Tr "user.block.unblock"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="item">{{ctx.Locale.Tr "user.block.list.none"}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui small modal" id="block-user-note-modal">
|
||||
<div class="header">{{ctx.Locale.Tr "user.block.note.edit"}}</div>
|
||||
<div class="content">
|
||||
<form class="ui form" action="{{$.Link}}" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="note" />
|
||||
<input type="hidden" name="blockee" class="modal-blockee" />
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "user.block.note.title"}}</label>
|
||||
<input name="note" class="modal-note" />
|
||||
<p class="help">{{ctx.Locale.Tr "user.block.note.info"}}</p>
|
||||
</div>
|
||||
<div class="text right actions">
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "cancel"}}</button>
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "save"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
33
templates/swagger/v1_json.tmpl
generated
33
templates/swagger/v1_json.tmpl
generated
|
@ -23128,6 +23128,33 @@
|
|||
"description": "Git reference for the workflow",
|
||||
"type": "string",
|
||||
"x-go-name": "Ref"
|
||||
},
|
||||
"return_run_info": {
|
||||
"description": "Flag to return the run info",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-go-name": "ReturnRunInfo"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"DispatchWorkflowRun": {
|
||||
"description": "DispatchWorkflowRun represents a workflow run",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "the workflow run id",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"x-go-name": "ID"
|
||||
},
|
||||
"jobs": {
|
||||
"description": "the jobs name",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "Jobs"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
|
@ -28511,6 +28538,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"DispatchWorkflowRun": {
|
||||
"description": "DispatchWorkflowRun is a Workflow Run after dispatching",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/DispatchWorkflowRun"
|
||||
}
|
||||
},
|
||||
"EmailList": {
|
||||
"description": "EmailList",
|
||||
"schema": {
|
||||
|
|
|
@ -752,9 +752,18 @@ func TestWorkflowDispatchEvent(t *testing.T) {
|
|||
return ""
|
||||
}
|
||||
|
||||
err = workflow.Dispatch(db.DefaultContext, inputGetter, repo, user2)
|
||||
var r *actions_model.ActionRun
|
||||
var j []string
|
||||
r, j, err = workflow.Dispatch(db.DefaultContext, inputGetter, repo, user2)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
|
||||
|
||||
assert.Equal(t, "test", r.Title)
|
||||
assert.Equal(t, "dispatch.yml", r.WorkflowID)
|
||||
assert.Equal(t, sha, r.CommitSHA)
|
||||
assert.Equal(t, actions_module.GithubEventWorkflowDispatch, r.TriggerEvent)
|
||||
assert.Len(t, j, 1)
|
||||
assert.Equal(t, "test", j[0])
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1153,6 +1153,19 @@ td .commit-summary {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.branch-tag-tab {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
.branch-tag-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0 10px;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.repository.compare.pull .comment.form .content::before,
|
||||
.repository.compare.pull .comment.form .content::after {
|
||||
right: 100%;
|
||||
|
|
|
@ -265,10 +265,10 @@ export default sfc; // activate IDE's Vue plugin
|
|||
</div>
|
||||
<div v-if="showBranchesInDropdown" class="branch-tag-tab">
|
||||
<a class="branch-tag-item muted" :class="{active: mode === 'branches'}" href="#" @click="handleTabSwitch('branches')">
|
||||
<svg-icon name="octicon-git-branch" :size="16" class="tw-mr-1"/>{{ textBranches }}
|
||||
<svg-icon name="octicon-git-branch" :size="16"/>{{ textBranches }}
|
||||
</a>
|
||||
<a v-if="!noTag" class="branch-tag-item muted" :class="{active: mode === 'tags'}" href="#" @click="handleTabSwitch('tags')">
|
||||
<svg-icon name="octicon-tag" :size="16" class="tw-mr-1"/>{{ textTags }}
|
||||
<svg-icon name="octicon-tag" :size="16"/>{{ textTags }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="branch-tag-divider"/>
|
||||
|
@ -316,17 +316,6 @@ export default sfc; // activate IDE's Vue plugin
|
|||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.branch-tag-tab {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.branch-tag-item {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.branch-tag-item.active {
|
||||
border-color: var(--color-secondary);
|
||||
background: var(--color-menu);
|
||||
|
|
Loading…
Add table
Reference in a new issue