feat(actions): support $/ prefix in reusable workflow uses: (#38822)

Fixes: https://github.com/go-gitea/gitea/issues/38818

Accepts GitHub's `$/` self-repository prefix in a reusable workflow
`uses:`, alongside `./`.

Gitea's `./` already resolves against the caller's own source repo and
commit, which is what `$/` means, so the two are aliases here. Cycle
detection folds both prefixes onto one key.

Related PR for step-level support:
https://gitea.com/gitea/runner/pulls/1150
This commit is contained in:
silverwind
2026-08-07 17:39:36 +02:00
committed by GitHub
parent d4333eb043
commit 4c382cea59
4 changed files with 36 additions and 15 deletions
+6 -6
View File
@@ -15,7 +15,7 @@ import (
type UsesKind int
const (
// UsesKindLocalSameRepo is "./<dir>/foo.yml" - a path inside the calling repository.
// UsesKindLocalSameRepo is "./<dir>/foo.yml" or "$/<dir>/foo.yml" - a path inside the calling repository.
// For example: "./.gitea/workflows/foo.yml"
UsesKindLocalSameRepo UsesKind = iota + 1
// UsesKindLocalCrossRepo is "owner/repo/<dir>/foo.yml@ref" - a workflow in another repo on the same instance.
@@ -33,13 +33,13 @@ type UsesRef struct {
}
var (
reLocalSameRepo = regexp.MustCompile(`^\./([^@]+\.ya?ml)$`)
reLocalSameRepo = regexp.MustCompile(`^[.$]/([^@]+\.ya?ml)$`)
reLocalCrossRepo = regexp.MustCompile(`^([-.\w]+)/([-.\w]+)/([^@]+\.ya?ml)@(.+)$`)
)
// ParseUses parses the SYNTAX of a reusable workflow "uses:" value into a UsesRef. Two forms are supported:
// - "./<dir>/foo.yml" (UsesKindLocalSameRepo, no @ref)
// - "OWNER/REPO/<dir>/foo.yml@REF" (UsesKindLocalCrossRepo)
// - "./<dir>/foo.yml" or "$/<dir>/foo.yml" (UsesKindLocalSameRepo, no @ref)
// - "OWNER/REPO/<dir>/foo.yml@REF" (UsesKindLocalCrossRepo)
//
// It deliberately does NOT validate that <dir> is an allowed workflow directory: the allowed directories are instance-configurable (WORKFLOW_DIRS / SCOPED_WORKFLOW_DIRS).
// The caller (services/actions.ResolveUses) enforces the directory allowlist. The returned Path is the cleaned, repo-relative file path.
@@ -49,10 +49,10 @@ func ParseUses(s string) (*UsesRef, error) {
return nil, errors.New("empty uses value")
}
if strings.HasPrefix(s, "./") {
if strings.HasPrefix(s, "./") || strings.HasPrefix(s, "$/") {
m := reLocalSameRepo.FindStringSubmatch(s)
if m == nil {
return nil, fmt.Errorf(`invalid local "uses:" %q (expect ./<dir>/<file>.yml)`, s)
return nil, fmt.Errorf(`invalid local "uses:" %q (expect ./<dir>/<file>.yml or $/<dir>/<file>.yml)`, s)
}
p := m[1]
if path.Clean(p) != p {
+6
View File
@@ -53,6 +53,11 @@ func TestParseUses(t *testing.T) {
in: "./.gitea/custom_workflows/x.yaml",
want: UsesRef{Kind: UsesKindLocalSameRepo, Path: ".gitea/custom_workflows/x.yaml"},
},
{
name: "self-repo prefix",
in: "$/.gitea/workflows/build.yml",
want: UsesRef{Kind: UsesKindLocalSameRepo, Path: ".gitea/workflows/build.yml"},
},
{
name: "leading/trailing whitespace is trimmed",
in: " ./.gitea/workflows/build.yml ",
@@ -160,6 +165,7 @@ func TestParseUses(t *testing.T) {
// Same-repo malformed (note: a wrong *directory* parses and should be rejected by the caller)
{name: "same-repo with @ref", in: "./.gitea/workflows/build.yml@v1"},
{name: "self-repo with @ref", in: "$/.gitea/workflows/build.yml@v1"},
{name: "same-repo wrong extension", in: "./.gitea/workflows/build.txt"},
{name: "same-repo missing extension", in: "./.gitea/workflows/build"},
{name: "same-repo absolute path", in: "/.gitea/workflows/build.yml"},