Files
silverwindandGitHub f757631a47 feat(actions): update actionslib, support self:, misc fixes (#39358)
Updates actionslib to https://gitea.com/gitea/actionslib/releases/tag/v1.2.1, moves workflow
parsing into it and aligns behaviour with GitHub.

1. `uses:` supports `self:` (Gitea-only feature) and `$/` paths.
1. `strategy`, `matrix`, `max-parallel` and `fail-fast` accept
expressions, including over `needs`. A job whose `name`, `runs-on` or
`continue-on-error` reads `needs` is resolved once they finish.
1. A job `if:` may only read `github`, `needs`, `vars` and `inputs` and
is decided before the matrix, as on github.com.
1. Matrix `fail-fast` cancels the other combinations, and `always()`
jobs keep running when a run is cancelled.
1. Invalid workflow files, including a malformed `on:` and unknown or
cyclic `needs`, show up on push as failed runs with the error.
1. A job whose `if:` or `concurrency:` fails to evaluate is skipped or
failed with the error, instead of staying blocked.
1. Reusable workflows: a missing and an unreadable repository fail
alike, public callers cannot use private workflows, nested jobs cannot
exceed the caller's token permissions.
1. Runner labels match case-insensitively, and `runs-on` accepts an
array from an expression.

Runner PR: https://gitea.com/gitea/runner/pulls/1247
Docs PR: https://gitea.com/gitea/docs/pulls/553
Fixes: https://github.com/go-gitea/gitea/issues/38990
Fixes: https://github.com/go-gitea/gitea/issues/39382
Fixes: https://github.com/go-gitea/gitea/issues/32364
Fixes: https://github.com/go-gitea/gitea/issues/36077
Fixes: https://github.com/go-gitea/gitea/issues/23277
Fixes: https://github.com/go-gitea/gitea/issues/29020
Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-09-25 00:06:42 +02:00

74 lines
1.9 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package jobparser
import (
"bytes"
"errors"
"io"
"gitea.dev/actionslib/pkg/model"
"go.yaml.in/yaml/v4"
)
// ReadWorkflow decodes a workflow file with its aliases expanded. Callers inspect the workflow's
// raw nodes by kind, and an alias is a kind none of them expect.
func ReadWorkflow(content []byte) (*model.Workflow, error) {
doc, err := resolveYamlAliases(content)
if err != nil {
return nil, err
}
return readWorkflowDoc(doc)
}
func readWorkflowDoc(doc *yaml.Node) (*model.Workflow, error) {
if doc.Kind == 0 {
return nil, io.EOF // what a yaml decoder reports for an empty file
}
w := new(model.Workflow)
if err := doc.Decode(w); err != nil {
return w, err
}
return w, validateJobConditions(w)
}
// decodeResolved is yaml.Unmarshal with aliases expanded first.
func decodeResolved(content []byte, out any) error {
doc, err := resolveYamlAliases(content)
if err != nil {
return err
}
return decodeYamlDoc(doc, out)
}
func decodeYamlDoc(doc *yaml.Node, out any) error {
if doc.Kind == 0 {
return nil // an empty document, as yaml.Unmarshal treats it
}
return doc.Decode(out)
}
// resolveYamlAliases parses content and replaces every alias with a copy of the node its anchor names.
func resolveYamlAliases(content []byte) (*yaml.Node, error) {
doc, err := model.ReadWorkflowNode(bytes.NewReader(content))
if err != nil && !errors.Is(err, io.EOF) {
return nil, err
}
return doc, rejectMergeKeys(doc)
}
// rejectMergeKeys refuses `<<: *anchor`, same as GitHub does
func rejectMergeKeys(node *yaml.Node) error {
for i, child := range node.Content {
if node.Kind == yaml.MappingNode && i%2 == 0 && child.Tag == "!!merge" {
return errors.New("merge keys (`<<`) are not supported, alias the whole value instead")
}
if err := rejectMergeKeys(child); err != nil {
return err
}
}
return nil
}