feat: Gitea Actions compatibility test suite
Gitea Actions Compatibility Suite / aggregate (push) Successful in 0s
Gitea Actions Compatibility Suite / c8 (push) Successful in 2s
Gitea Actions Compatibility Suite / c9 (push) Successful in 58s
Gitea Actions Compatibility Suite / c10 (push) Successful in 0s
Gitea Actions Compatibility Suite / probe (push) Successful in 0s
Gitea Actions Compatibility Suite / c1 (push) Successful in 7s
Gitea Actions Compatibility Suite / c2 (push) Successful in 4s
Gitea Actions Compatibility Suite / c3 (push) Successful in 1m24s
Gitea Actions Compatibility Suite / c4 (push) Successful in 12s
Gitea Actions Compatibility Suite / c5 (push) Successful in 0s
Gitea Actions Compatibility Suite / c6 (push) Successful in 1s
Gitea Actions Compatibility Suite / c7 (push) Successful in 44s
Gitea Actions Compatibility Suite / aggregate (push) Successful in 0s
Gitea Actions Compatibility Suite / c8 (push) Successful in 2s
Gitea Actions Compatibility Suite / c9 (push) Successful in 58s
Gitea Actions Compatibility Suite / c10 (push) Successful in 0s
Gitea Actions Compatibility Suite / probe (push) Successful in 0s
Gitea Actions Compatibility Suite / c1 (push) Successful in 7s
Gitea Actions Compatibility Suite / c2 (push) Successful in 4s
Gitea Actions Compatibility Suite / c3 (push) Successful in 1m24s
Gitea Actions Compatibility Suite / c4 (push) Successful in 12s
Gitea Actions Compatibility Suite / c5 (push) Successful in 0s
Gitea Actions Compatibility Suite / c6 (push) Successful in 1s
Gitea Actions Compatibility Suite / c7 (push) Successful in 44s
A self-validating test suite that proves Gitea's implementations of GitHub-style actions behave like GitHub's. Push to any Gitea instance with a registered runner — on push or manual dispatch, it runs 10 independent test workflows covering checkout, artifacts, caching, language setup, expression contexts, workflow commands, services, composite/reusable workflows, control flow, and known divergences. Components: - lib/assert.sh — self-asserting shell helper library (assert_eq, assert_contains, assert_exists, assert_not_exists, assert_match, sha256_of) with verified negative fail path - 10 reusable workflows (on: workflow_call): 01-checkout, 02-artifacts, 03-cache, 04-setup-runtime, 05-contexts, 06-workflow-commands, 07-services, 08-composite-reusable, 09-control-flow, 10-gitea-divergences - 00-suite-runner.yml — sole entry (on: push + workflow_dispatch): probe gate (Gitea >= 1.22.0) -> c1-c10 parallel callers (c5/c8 forward secrets: inherit for act_runner #125) -> aggregate (if: always, skipped-as-failure) - Fixtures: LFS 5MB binary, git tag v1-test, 4 language lockfiles (npm/go/pip/maven), sparse-cone layout - Makefile: make lint (actionlint static gate) - README: prerequisites, per-test expectations, correction table, manual eyeball checklist for UI-only behaviors
This commit is contained in:
+167
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env bash
|
||||
# lib/assert.sh — self-asserting shell helper library (C11 core)
|
||||
#
|
||||
# Foundation library sourced by every test workflow (T4-T13). Provides uniform
|
||||
# [PASS]/[FAIL] logging to stderr and explicit non-zero return on failure.
|
||||
#
|
||||
# Design contract:
|
||||
# * All assert_* functions log [PASS]/[FAIL] to STDERR (never stdout).
|
||||
# * On success: print [PASS] to stderr and `return 0` (caller continues).
|
||||
# * On failure: print [FAIL] to stderr and `return 1` (caller decides to exit).
|
||||
# * sha256_of is the ONLY function that writes to stdout (the bare hash).
|
||||
# * The library never sets `set -e`; the caller owns flow control.
|
||||
# * Missing arguments always fail loudly — assertions never silently pass.
|
||||
#
|
||||
# Usage:
|
||||
# source lib/assert.sh
|
||||
# assert_eq "$got" "$want" "my note"
|
||||
# if ! assert_exists "./build/out"; then exit 1; fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bootstrap: this library relies on bash-specific features (`[[ ]]`, `=~`).
|
||||
# Detect bash early; if absent, fail loudly. This guard itself is written in
|
||||
# POSIX-compatible syntax so it parses under any shell.
|
||||
# ---------------------------------------------------------------------------
|
||||
if [ -z "${BASH_VERSION:-}" ]; then
|
||||
printf '[FAIL] assert.sh: requires bash, but BASH_VERSION is unset (current shell is not bash)\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Internal logging helpers. Centralised so the [PASS]/[FAIL] format is uniform
|
||||
# and grep-friendly across every assert function.
|
||||
# $1 - assert function name (e.g. assert_eq)
|
||||
# $2 - caller-supplied note
|
||||
# $3 - human-readable detail (actual vs expected, etc.)
|
||||
# ---------------------------------------------------------------------------
|
||||
__assert_pass() {
|
||||
printf '[PASS] %s: %s (%s)\n' "$1" "$2" "$3" >&2
|
||||
}
|
||||
|
||||
__assert_fail() {
|
||||
printf '[FAIL] %s: %s (%s)\n' "$1" "$2" "$3" >&2
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# assert_eq <actual> <expected> <note>
|
||||
# Pass when the two strings are byte-identical.
|
||||
# ---------------------------------------------------------------------------
|
||||
assert_eq() {
|
||||
if [ "$#" -lt 3 ]; then
|
||||
printf '[FAIL] assert_eq: missing arguments (usage: assert_eq <actual> <expected> <note>; got %d args)\n' "$#" >&2
|
||||
return 1
|
||||
fi
|
||||
local actual="$1" expected="$2" note="$3"
|
||||
if [ "$actual" = "$expected" ]; then
|
||||
__assert_pass "assert_eq" "$note" "'$actual' == '$expected'"
|
||||
return 0
|
||||
fi
|
||||
__assert_fail "assert_eq" "$note" "got '$actual', expected '$expected'"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# assert_contains <haystack> <needle> <note>
|
||||
# Pass when <needle> appears as a substring of <haystack>.
|
||||
# ---------------------------------------------------------------------------
|
||||
assert_contains() {
|
||||
if [ "$#" -lt 3 ]; then
|
||||
printf '[FAIL] assert_contains: missing arguments (usage: assert_contains <haystack> <needle> <note>; got %d args)\n' "$#" >&2
|
||||
return 1
|
||||
fi
|
||||
local haystack="$1" needle="$2" note="$3"
|
||||
if [[ "$haystack" == *"$needle"* ]]; then
|
||||
__assert_pass "assert_contains" "$note" "'$needle' found in '$haystack'"
|
||||
return 0
|
||||
fi
|
||||
__assert_fail "assert_contains" "$note" "'$needle' not found in '$haystack'"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# assert_exists <path> [note]
|
||||
# Pass when <path> exists on the filesystem (any type: file/dir/symlink).
|
||||
# ---------------------------------------------------------------------------
|
||||
assert_exists() {
|
||||
if [ "$#" -lt 1 ] || [ -z "${1:-}" ]; then
|
||||
printf '[FAIL] assert_exists: missing arguments (usage: assert_exists <path>; got %d args)\n' "$#" >&2
|
||||
return 1
|
||||
fi
|
||||
local path="$1"
|
||||
local note="${2:-$path}"
|
||||
if [ -e "$path" ]; then
|
||||
__assert_pass "assert_exists" "$note" "'$path' exists"
|
||||
return 0
|
||||
fi
|
||||
__assert_fail "assert_exists" "$note" "'$path' does not exist"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# assert_not_exists <path> [note]
|
||||
# Pass when <path> does NOT exist on the filesystem.
|
||||
# ---------------------------------------------------------------------------
|
||||
assert_not_exists() {
|
||||
if [ "$#" -lt 1 ] || [ -z "${1:-}" ]; then
|
||||
printf '[FAIL] assert_not_exists: missing arguments (usage: assert_not_exists <path>; got %d args)\n' "$#" >&2
|
||||
return 1
|
||||
fi
|
||||
local path="$1"
|
||||
local note="${2:-$path}"
|
||||
if [ ! -e "$path" ]; then
|
||||
__assert_pass "assert_not_exists" "$note" "'$path' absent"
|
||||
return 0
|
||||
fi
|
||||
__assert_fail "assert_not_exists" "$note" "'$path' exists (expected absent)"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# assert_match <actual> <regex> <note>
|
||||
# Pass when <actual> matches the bash extended regex <regex> via `[[ =~ ]]`.
|
||||
# Bash-specific — hence the bootstrap guard at the top of this file.
|
||||
# ---------------------------------------------------------------------------
|
||||
assert_match() {
|
||||
if [ "$#" -lt 3 ]; then
|
||||
printf '[FAIL] assert_match: missing arguments (usage: assert_match <actual> <regex> <note>; got %d args)\n' "$#" >&2
|
||||
return 1
|
||||
fi
|
||||
local actual="$1" regex="$2" note="$3"
|
||||
if [[ "$actual" =~ $regex ]]; then
|
||||
__assert_pass "assert_match" "$note" "'$actual' =~ /$regex/"
|
||||
return 0
|
||||
fi
|
||||
__assert_fail "assert_match" "$note" "'$actual' !~ /$regex/"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# sha256_of <path>
|
||||
# Print JUST the sha256 hex digest of <path> to stdout (capturable via
|
||||
# `$(sha256_of file)`). Prefers sha256sum, falls back to `shasum -a 256`.
|
||||
# This is the only function that writes to stdout; all errors go to stderr.
|
||||
# ---------------------------------------------------------------------------
|
||||
sha256_of() {
|
||||
if [ "$#" -lt 1 ] || [ -z "${1:-}" ]; then
|
||||
printf '[FAIL] sha256_of: missing arguments (usage: sha256_of <path>; got %d args)\n' "$#" >&2
|
||||
return 1
|
||||
fi
|
||||
local path="$1"
|
||||
if [ ! -f "$path" ]; then
|
||||
printf '[FAIL] sha256_of: not a regular file: '%s'\n' "$path" >&2
|
||||
return 1
|
||||
fi
|
||||
local line
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
line=$(sha256sum "$path") || { printf '[FAIL] sha256_of: sha256sum failed for '%s'\n' "$path" >&2; return 1; }
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
line=$(shasum -a 256 "$path") || { printf '[FAIL] sha256_of: shasum failed for '%s'\n' "$path" >&2; return 1; }
|
||||
else
|
||||
printf '[FAIL] sha256_of: neither sha256sum nor shasum is available\n' >&2
|
||||
return 1
|
||||
fi
|
||||
# sha256sum/shasum both emit "<hash> <file>"; strip everything from the
|
||||
# first space onward to yield the bare digest.
|
||||
printf '%s\n' "${line%% *}"
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
name: 'Composite Greet'
|
||||
description: 'Local composite action used by C8 to verify input/output round-trip'
|
||||
|
||||
inputs:
|
||||
who:
|
||||
description: 'Who to greet'
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
greeting:
|
||||
description: 'The greeting string'
|
||||
value: ${{ steps.greet.outputs.greeting }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- id: greet
|
||||
shell: bash
|
||||
env:
|
||||
WHO: ${{ inputs.who }}
|
||||
run: |
|
||||
# shellcheck disable=SC2154
|
||||
greeting="Hello, ${WHO}!"
|
||||
printf 'greeting=%s\n' "$greeting" >> "$GITHUB_OUTPUT"
|
||||
Reference in New Issue
Block a user