Files
tqcq 083b1f36a8
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
feat: Gitea Actions compatibility test suite
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
2026-07-07 17:13:09 +00:00

168 lines
6.6 KiB
Bash

#!/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
}