mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-20 01:53:57 +09:00
Backport https://github.com/go-gitea/gitea/pull/39787 by @lunny, including the go-yaml bump. The runner counterpart is https://gitea.com/gitea/runner/pulls/1321. Folded (`>`) scalars in workflows gained blank lines and had spaces turned into newlines every time Gitea re-serialized a job. - go-yaml main fixes the accumulating blank lines (https://github.com/yaml/go-yaml/pull/366) and the block scalar indentation indicators (https://github.com/yaml/go-yaml/pull/414), so `BlockSafeString` goes away - The job encoder no longer wraps lines, because go-yaml still wraps more-indented lines of folded scalars and turns a space into a newline Fixes https://github.com/go-gitea/gitea/issues/39775
141 lines
4.2 KiB
Go
141 lines
4.2 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package jobparser
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
|
|
"gitea.dev/actionslib/pkg/model"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// A step whose `run:` block starts with blank lines must still survive the
|
|
// Parse -> SingleWorkflow.Marshal -> Parse round-trip. Previously Marshal used a
|
|
// different indentation than SetJob, which made the encoder emit the block scalar
|
|
// with a wrong explicit indentation indicator (`run: |4`) that no longer parsed;
|
|
// the job then stayed silently blocked during concurrency evaluation.
|
|
func TestSingleWorkflowRoundTripRunBlockLeadingBlankLines(t *testing.T) {
|
|
const wf = `name: demo
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
app_name:
|
|
type: string
|
|
required: true
|
|
jobs:
|
|
build:
|
|
name: build
|
|
env:
|
|
IMAGE_TAG: ${{ inputs.app_name }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- if: ${{ inputs.app_name != '' }}
|
|
name: packages
|
|
run: |
|
|
|
|
|
|
echo start
|
|
echo done
|
|
`
|
|
sws, err := Parse([]byte(wf))
|
|
require.NoError(t, err)
|
|
require.Len(t, sws, 1)
|
|
|
|
// pin the original run block as the baseline
|
|
_, origJob := sws[0].Job()
|
|
require.Len(t, origJob.Steps, 1)
|
|
const wantRun = "\n\necho start\necho done\n"
|
|
require.Equal(t, wantRun, origJob.Steps[0].Run)
|
|
|
|
payload, err := sws[0].Marshal()
|
|
require.NoError(t, err)
|
|
|
|
// the serialized single workflow must be parseable again -- this is what the
|
|
// server does in EvaluateJobConcurrencyFillModel -> ParseJob. Before the fix
|
|
// Marshal emitted `run: |4`, which failed here and left the job blocked.
|
|
roundTripped, err := Parse(payload)
|
|
require.NoError(t, err, "serialized single workflow must round-trip; got payload:\n%s", payload)
|
|
require.Len(t, roundTripped, 1)
|
|
|
|
// the round-trip must preserve the run block byte-for-byte
|
|
_, gotJob := roundTripped[0].Job()
|
|
require.Len(t, gotJob.Steps, 1)
|
|
require.Equal(t, wantRun, gotJob.Steps[0].Run, "round-trip must preserve run content; got payload:\n%s", payload)
|
|
}
|
|
|
|
// Typing a step's continue-on-error as a bool used to reject the whole `jobs:` node.
|
|
func TestSingleWorkflowRoundTripStepContinueOnError(t *testing.T) {
|
|
const wf = `name: demo
|
|
on: push
|
|
jobs:
|
|
job1:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- id: quarantine
|
|
run: echo "q=true" >> "$GITHUB_OUTPUT"
|
|
- run: exit 1
|
|
continue-on-error: ${{ steps.quarantine.outputs.q == 'true' }}
|
|
- run: exit 1
|
|
continue-on-error: true
|
|
- run: exit 1
|
|
continue-on-error: TRUE
|
|
- run: exit 1
|
|
continue-on-error: false
|
|
- run: exit 1
|
|
continue-on-error: yes
|
|
`
|
|
want := []string{"", "${{ steps.quarantine.outputs.q == 'true' }}", "true", "true", "false", "yes"}
|
|
|
|
sws, err := Parse([]byte(wf))
|
|
require.NoError(t, err)
|
|
require.Len(t, sws, 1)
|
|
|
|
payload, err := sws[0].Marshal()
|
|
require.NoError(t, err)
|
|
|
|
rw, err := model.ReadWorkflow(bytes.NewReader(payload))
|
|
require.NoError(t, err, "payload:\n%s", payload)
|
|
steps := rw.Jobs["job1"].Steps
|
|
require.Len(t, steps, len(want))
|
|
for i, w := range want {
|
|
require.Equal(t, w, steps[i].RawContinueOnError, "step %d, payload:\n%s", i, payload)
|
|
}
|
|
}
|
|
|
|
// Folded (`>`) scalars in with/env used to gain blank lines and have spaces turned into newlines
|
|
// on every Marshal -> Parse cycle.
|
|
func TestSingleWorkflowRoundTripFoldedScalar(t *testing.T) {
|
|
const wf = `name: demo
|
|
on: push
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: ./.github/actions/build
|
|
with:
|
|
cfg: >
|
|
begin
|
|
first "indented" line with path dir/Installer Scripts/some long path/another long path/some file name.xzy,
|
|
second "indented" line
|
|
end
|
|
`
|
|
const want = "begin\n first \"indented\" line with path dir/Installer Scripts/some long path/another long path/some file name.xzy,\n second \"indented\" line\nend\n"
|
|
sws, err := Parse([]byte(wf))
|
|
require.NoError(t, err)
|
|
require.Len(t, sws, 1)
|
|
for range 3 {
|
|
payload, err := sws[0].Marshal()
|
|
require.NoError(t, err)
|
|
sws, err = Parse(payload)
|
|
require.NoError(t, err)
|
|
require.Len(t, sws, 1)
|
|
}
|
|
_, job := sws[0].Job()
|
|
var with map[string]string
|
|
require.NoError(t, job.Steps[0].With.Decode(&with))
|
|
require.Equal(t, want, with["cfg"])
|
|
}
|