mirror of
https://github.com/oSoWoSo/DistroHopper.git
synced 2026-11-13 08:54:01 +00:00
410 lines
11 KiB
YAML
410 lines
11 KiB
YAML
name: Check GitHub Actions
|
|
|
|
on:
|
|
schedule:
|
|
- cron: '0 6 * * 1'
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
check-actions:
|
|
name: Check action versions
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Check action versions
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
declare -A latest_cache
|
|
declare -A latest_sha_cache
|
|
|
|
total=0
|
|
outdated=0
|
|
warnings=0
|
|
|
|
summary_file="$GITHUB_STEP_SUMMARY"
|
|
|
|
{
|
|
echo "# GitHub Actions version check"
|
|
echo
|
|
echo "| Workflow | Action | Current | Latest | Status |"
|
|
echo "|---|---|---|---|---|"
|
|
} > "$summary_file"
|
|
|
|
# Return normalized numeric version without leading 'v'.
|
|
normalize_version() {
|
|
local version="$1"
|
|
|
|
version="${version#v}"
|
|
version="${version%%-*}"
|
|
|
|
if [[ "$version" =~ ^[0-9]+(\.[0-9]+){0,2}$ ]]; then
|
|
printf '%s\n' "$version"
|
|
fi
|
|
}
|
|
|
|
# Return success when version $1 is lower than $2.
|
|
version_lt() {
|
|
local a="$1"
|
|
local b="$2"
|
|
|
|
local -a av bv
|
|
IFS='.' read -ra av <<< "$a"
|
|
IFS='.' read -ra bv <<< "$b"
|
|
|
|
local max=${#av[@]}
|
|
(( ${#bv[@]} > max )) && max=${#bv[@]}
|
|
|
|
local i
|
|
local ai
|
|
local bi
|
|
|
|
for ((i = 0; i < max; i++)); do
|
|
ai="${av[i]:-0}"
|
|
bi="${bv[i]:-0}"
|
|
|
|
if (( 10#$ai < 10#$bi )); then
|
|
return 0
|
|
fi
|
|
|
|
if (( 10#$ai > 10#$bi )); then
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
# Get the latest stable release tag.
|
|
get_latest() {
|
|
local repo="$1"
|
|
|
|
if [[ -n "${latest_cache[$repo]+x}" ]]; then
|
|
printf '%s\n' "${latest_cache[$repo]}"
|
|
return
|
|
fi
|
|
|
|
local latest=""
|
|
|
|
# Try latest GitHub release.
|
|
latest="$(
|
|
gh api \
|
|
"repos/$repo/releases/latest" \
|
|
--jq '.tag_name' \
|
|
2>/dev/null || true
|
|
)"
|
|
|
|
# Ignore API error responses.
|
|
if [[ "$latest" == "{"* || "$latest" == "["* ||
|
|
! "$latest" =~ ^[^[:space:]]+$ ]]; then
|
|
latest=""
|
|
fi
|
|
|
|
# Fall back to version-like tags.
|
|
if [[ -z "$latest" ]]; then
|
|
latest="$(
|
|
gh api \
|
|
--paginate \
|
|
"repos/$repo/tags?per_page=100" \
|
|
--jq '.[].name' \
|
|
2>/dev/null |
|
|
grep -E '^v?[0-9]+(\.[0-9]+){0,2}$' |
|
|
sort -V |
|
|
tail -n1 || true
|
|
)"
|
|
fi
|
|
|
|
latest_cache["$repo"]="$latest"
|
|
|
|
printf '%s\n' "$latest"
|
|
}
|
|
|
|
# Resolve a tag/ref to the commit SHA.
|
|
get_ref_sha() {
|
|
local repo="$1"
|
|
local ref="$2"
|
|
|
|
local key="$repo@$ref"
|
|
|
|
if [[ -n "${latest_sha_cache[$key]+x}" ]]; then
|
|
printf '%s\n' "${latest_sha_cache[$key]}"
|
|
return
|
|
fi
|
|
|
|
local sha
|
|
|
|
sha="$(
|
|
gh api \
|
|
"repos/$repo/commits/$ref" \
|
|
--jq '.sha' \
|
|
2>/dev/null || true
|
|
)"
|
|
|
|
latest_sha_cache["$key"]="$sha"
|
|
|
|
printf '%s\n' "$sha"
|
|
}
|
|
|
|
check_action() {
|
|
local workflow="$1"
|
|
local repo="$2"
|
|
local current="$3"
|
|
|
|
total=$((total + 1))
|
|
|
|
local latest=""
|
|
local current_version=""
|
|
local latest_version=""
|
|
local status="OK"
|
|
|
|
# Branch / arbitrary reference
|
|
if [[ ! "$current" =~ ^v?[0-9]+(\.[0-9]+){0,2}$ &&
|
|
! "$current" =~ ^[0-9a-fA-F]{40}$ ]]; then
|
|
|
|
case "$current" in
|
|
main|master|develop|development|next|dev|nightly)
|
|
status="BRANCH"
|
|
;;
|
|
*)
|
|
status="REF"
|
|
;;
|
|
esac
|
|
|
|
warnings=$((warnings + 1))
|
|
|
|
printf '| `%s` | `%s` | `%s` | — | ⚠️ %s |\n' \
|
|
"$workflow" \
|
|
"$repo" \
|
|
"$current" \
|
|
"$status" \
|
|
>> "$summary_file"
|
|
|
|
return
|
|
fi
|
|
|
|
# Full commit SHA
|
|
if [[ "$current" =~ ^[0-9a-fA-F]{40}$ ]]; then
|
|
latest="$(get_latest "$repo")"
|
|
|
|
if [[ -z "$latest" ]]; then
|
|
warnings=$((warnings + 1))
|
|
|
|
printf '| `%s` | `%s` | `%s` | — | ⚠️ NO_RELEASE |\n' \
|
|
"$workflow" \
|
|
"$repo" \
|
|
"$current" \
|
|
>> "$summary_file"
|
|
|
|
return
|
|
fi
|
|
|
|
local latest_sha
|
|
latest_sha="$(get_ref_sha "$repo" "$latest")"
|
|
|
|
if [[ -z "$latest_sha" ]]; then
|
|
warnings=$((warnings + 1))
|
|
|
|
printf '| `%s` | `%s` | `%s` | `%s` | ⚠️ SHA_UNKNOWN |\n' \
|
|
"$workflow" \
|
|
"$repo" \
|
|
"$current" \
|
|
"$latest" \
|
|
>> "$summary_file"
|
|
|
|
return
|
|
fi
|
|
|
|
if [[ "$current" == "$latest_sha" ]]; then
|
|
status="OK"
|
|
else
|
|
status="SHA_OUTDATED"
|
|
outdated=$((outdated + 1))
|
|
fi
|
|
|
|
printf '| `%s` | `%s` | `%s` | `%s` | %s |\n' \
|
|
"$workflow" \
|
|
"$repo" \
|
|
"$current" \
|
|
"$latest" \
|
|
"$status" \
|
|
>> "$summary_file"
|
|
|
|
return
|
|
fi
|
|
|
|
# Version reference
|
|
current_version="$(normalize_version "$current")"
|
|
|
|
if [[ -z "$current_version" ]]; then
|
|
warnings=$((warnings + 1))
|
|
|
|
printf '| `%s` | `%s` | `%s` | — | ⚠️ UNKNOWN |\n' \
|
|
"$workflow" \
|
|
"$repo" \
|
|
"$current" \
|
|
>> "$summary_file"
|
|
|
|
return
|
|
fi
|
|
|
|
latest="$(get_latest "$repo")"
|
|
|
|
if [[ -z "$latest" ]]; then
|
|
warnings=$((warnings + 1))
|
|
|
|
printf '| `%s` | `%s` | `%s` | — | ⚠️ NO_RELEASE |\n' \
|
|
"$workflow" \
|
|
"$repo" \
|
|
"$current" \
|
|
>> "$summary_file"
|
|
|
|
return
|
|
fi
|
|
|
|
latest_version="$(normalize_version "$latest")"
|
|
|
|
if [[ -z "$latest_version" ]]; then
|
|
warnings=$((warnings + 1))
|
|
|
|
printf '| `%s` | `%s` | `%s` | `%s` | ⚠️ UNKNOWN_LATEST |\n' \
|
|
"$workflow" \
|
|
"$repo" \
|
|
"$current" \
|
|
"$latest" \
|
|
>> "$summary_file"
|
|
|
|
return
|
|
fi
|
|
|
|
# Major-only reference: @v6
|
|
if [[ "$current" =~ ^v?[0-9]+$ ]]; then
|
|
local current_major="${current#v}"
|
|
local latest_major="${latest_version%%.*}"
|
|
|
|
if (( latest_major > current_major )); then
|
|
status="NEW_MAJOR"
|
|
outdated=$((outdated + 1))
|
|
else
|
|
status="OK"
|
|
fi
|
|
|
|
# Full / partial version: @v6.1 or @v6.1.2
|
|
else
|
|
if version_lt "$current_version" "$latest_version"; then
|
|
status="OUTDATED"
|
|
outdated=$((outdated + 1))
|
|
fi
|
|
fi
|
|
|
|
printf '| `%s` | `%s` | `%s` | `%s` | %s |\n' \
|
|
"$workflow" \
|
|
"$repo" \
|
|
"$current" \
|
|
"$latest" \
|
|
"$status" \
|
|
>> "$summary_file"
|
|
}
|
|
|
|
echo "Scanning workflow files..."
|
|
echo
|
|
|
|
# Find every workflow file.
|
|
while IFS= read -r -d '' file; do
|
|
workflow="${file#./}"
|
|
echo "Checking $workflow"
|
|
|
|
# Find every `uses:` entry.
|
|
while IFS= read -r line; do
|
|
# Remove comments.
|
|
line="${line%%#*}"
|
|
|
|
# Extract everything after `uses:`.
|
|
value="${line#*uses:}"
|
|
|
|
# Trim leading whitespace.
|
|
value="${value#"${value%%[![:space:]]*}"}"
|
|
|
|
# Trim trailing whitespace.
|
|
value="${value%"${value##*[![:space:]]}"}"
|
|
|
|
# Remove optional YAML quotes.
|
|
if [[ "$value" == \"*\" ]]; then
|
|
value="${value#\"}"
|
|
value="${value%\"}"
|
|
elif [[ "$value" == \'*\' ]]; then
|
|
value="${value#\'}"
|
|
value="${value%\'}"
|
|
fi
|
|
|
|
# A valid external action/reusable workflow must contain @.
|
|
[[ "$value" == *@* ]] || continue
|
|
|
|
repo="${value%@*}"
|
|
ref="${value##*@}"
|
|
|
|
# Ignore local actions.
|
|
[[ "$repo" == ./* ]] && continue
|
|
|
|
# Ignore expressions.
|
|
[[ "$repo" == \$\{* ]] && continue
|
|
[[ "$ref" == \$\{* ]] && continue
|
|
|
|
# Normal GitHub repository:
|
|
if [[ "$repo" =~ ^[^/[:space:]]+/[^/[:space:]]+(/.*)?$ ]]; then
|
|
# For reusable workflows, extract the actual repository.
|
|
action_repo="${repo%%/.github/workflows/*}"
|
|
|
|
# Ordinary action.
|
|
if [[ "$action_repo" == "$repo" ]]; then
|
|
action_repo="$repo"
|
|
fi
|
|
|
|
check_action "$workflow" "$action_repo" "$ref"
|
|
fi
|
|
|
|
done < <(
|
|
grep -E '^[[:space:]]*-[[:space:]]*uses:[[:space:]]*[^#]+|^[[:space:]]*uses:[[:space:]]*[^#]+' \
|
|
"$file" || true
|
|
)
|
|
|
|
done < <(
|
|
find .github/workflows \
|
|
-type f \
|
|
\( -name '*.yml' -o -name '*.yaml' \) \
|
|
-print0
|
|
)
|
|
|
|
{
|
|
echo
|
|
echo "## Summary"
|
|
echo
|
|
echo "- Workflow files scanned: **$(find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) | wc -l)**"
|
|
echo "- Action references checked: **$total**"
|
|
echo "- Outdated: **$outdated**"
|
|
echo "- Warnings: **$warnings**"
|
|
} >> "$summary_file"
|
|
|
|
echo
|
|
echo "========================================"
|
|
echo "GitHub Actions version check"
|
|
echo "========================================"
|
|
echo "References: $total"
|
|
echo "Outdated: $outdated"
|
|
echo "Warnings: $warnings"
|
|
echo
|
|
|
|
if (( outdated > 0 )); then
|
|
echo "::error::$outdated outdated GitHub Actions found."
|
|
exit 1
|
|
fi
|
|
|
|
echo "All versioned GitHub Actions are up to date."
|