mirror of
https://github.com/Heretek-AI/heretek-actions.git
synced 2026-10-12 02:03:46 +00:00
Initial commit: Heretek Actions — agent-facing GitHub Actions
Complete implementation of all 7 phases: Phase 1: Foundation - Repository directory structure - .agent/schema.json — Standard JSON envelope schema - .github/CODEOWNERS, LICENSE (MIT), .gitignore - README.md shell Phase 2: Agent-Facing Actions (5 composite actions) - agent-context — Scans repo, detects stack, queries GitHub API - pr-summary — Analyzes PR diff, categorizes files, links issues - issue-triage — Classifies, labels, detects duplicates - check-status — Watches CI checks, produces merged status - review — Lightweight structural code analysis - Shared agent-envelope.sh helper for standardized .agent/output.json Phase 3: Pre-commit Hooks - .pre-commit-config.yaml — Universal hook config - pre-commit-ci.yml — CI workflow running pre-commit Phase 4: CI Composite Actions - rust-ci — fmt, clippy, test, coverage (opt), audit (opt) - js-ci — lint, typecheck, test (npm/pnpm/yarn/bun) - python-ci — lint (ruff), typecheck (mypy), test (pytest) Phase 5: Universal CI Orchestrator - check.yml — Auto-detects languages, routes to CI actions Phase 6: Universal Release - release action — Docker, Flatpak, npm, GitHub releases - release.yml — workflow_call reusable workflow Phase 7: Documentation - Full README with quick start, per-action docs, agent integration guide Co-Authored-By: Claude <[email protected]>
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
name: "Python CI"
|
||||
description: |
|
||||
Composite action for Python projects: setup Python, cache, install deps,
|
||||
lint (via ruff), typecheck (via mypy), and test (via pytest).
|
||||
Outputs .agent/output.json.
|
||||
|
||||
author: "Heretek-AI"
|
||||
|
||||
inputs:
|
||||
working-directory:
|
||||
description: "Working directory"
|
||||
default: "."
|
||||
required: false
|
||||
python-version:
|
||||
description: "Python version"
|
||||
default: "3.12"
|
||||
required: false
|
||||
package-manager:
|
||||
description: "Package manager: pip, pipenv, poetry, uv, or auto (default)"
|
||||
default: "auto"
|
||||
required: false
|
||||
lint-command:
|
||||
description: "Override lint command"
|
||||
default: ""
|
||||
required: false
|
||||
test-command:
|
||||
description: "Override test command"
|
||||
default: ""
|
||||
required: false
|
||||
lint-continue-on-error:
|
||||
description: "Continue if lint fails"
|
||||
default: "false"
|
||||
required: false
|
||||
test-continue-on-error:
|
||||
description: "Continue if tests fail"
|
||||
default: "false"
|
||||
required: false
|
||||
|
||||
outputs:
|
||||
agent_action:
|
||||
description: "Always 'ci-python'"
|
||||
value: "ci-python"
|
||||
status:
|
||||
description: "success | failure | partial"
|
||||
value: ${{ steps.envelope.outputs.status }}
|
||||
summary:
|
||||
description: "CI summary"
|
||||
value: ${{ steps.envelope.outputs.summary }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: "Detect Package Manager"
|
||||
id: detect
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
PM="${{ inputs.package-manager }}"
|
||||
if [ "$PM" = "auto" ]; then
|
||||
if grep -q "\[tool.poetry\]" pyproject.toml 2>/dev/null; then PM="poetry"
|
||||
elif [ -f "Pipfile" ]; then PM="pipenv"
|
||||
elif [ -f "uv.lock" ]; then PM="uv"
|
||||
else PM="pip"
|
||||
fi
|
||||
fi
|
||||
echo "pm=${PM}" >> "$GITHUB_OUTPUT"
|
||||
echo "manager: ${PM}"
|
||||
|
||||
- name: "Setup Python"
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ inputs.python-version }}
|
||||
|
||||
- name: "Cache Dependencies"
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ inputs.working-directory }}/.venv
|
||||
${{ inputs.working-directory }}/__pypackages__
|
||||
~/.cache/pip
|
||||
~/.cache/pypoetry
|
||||
key: ${{ runner.os }}-py${{ inputs.python-version }}-${{ hashFiles(format('{0}/{1}', inputs.working-directory, inputs.package-manager == 'poetry' && 'poetry.lock' || inputs.package-manager == 'pipenv' && 'Pipfile.lock' || 'pyproject.toml')) }}
|
||||
|
||||
- name: "Install Dependencies"
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
case "${{ steps.detect.outputs.pm }}" in
|
||||
poetry)
|
||||
pip install poetry
|
||||
poetry install
|
||||
;;
|
||||
pipenv)
|
||||
pip install pipenv
|
||||
pipenv install --dev
|
||||
;;
|
||||
uv)
|
||||
pip install uv
|
||||
uv sync
|
||||
;;
|
||||
*)
|
||||
pip install -e ".[dev]" 2>/dev/null || pip install -r requirements.txt 2>/dev/null || pip install -r requirements-dev.txt 2>/dev/null || true
|
||||
pip install ruff pytest mypy
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: "Lint (ruff)"
|
||||
id: lint
|
||||
continue-on-error: ${{ inputs.lint-continue-on-error == 'true' }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
CMD="${{ inputs.lint-command }}"
|
||||
if [ -z "$CMD" ]; then
|
||||
CMD="ruff check ."
|
||||
fi
|
||||
eval "$CMD" 2>&1 | tee .lint-output.txt
|
||||
echo "exit_code=$?" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: "TypeCheck (mypy)"
|
||||
id: typecheck
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
if [ -f "mypy.ini" ] || [ -f ".mypy.ini" ] || grep -q "\[tool.mypy" pyproject.toml 2>/dev/null; then
|
||||
mypy . 2>&1 | tee .typecheck-output.txt
|
||||
else
|
||||
echo "No mypy config — skipping typecheck"
|
||||
fi
|
||||
echo "exit_code=$?" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: "Test (pytest)"
|
||||
id: test
|
||||
continue-on-error: ${{ inputs.test-continue-on-error == 'true' }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
CMD="${{ inputs.test-command }}"
|
||||
if [ -z "$CMD" ]; then
|
||||
CMD="python -m pytest . --junitxml=test-results.xml -v"
|
||||
fi
|
||||
eval "$CMD" 2>&1 | tee .test-output.txt
|
||||
echo "exit_code=$?" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: "Generate Agent Envelope"
|
||||
id: envelope
|
||||
shell: bash
|
||||
env:
|
||||
LINT_EXIT: ${{ steps.lint.outputs.exit_code }}
|
||||
TC_EXIT: ${{ steps.typecheck.outputs.exit_code }}
|
||||
TEST_EXIT: ${{ steps.test.outputs.exit_code }}
|
||||
run: |
|
||||
${{ github.action_path }}/write-envelope.sh
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
source "${{ github.action_path }}/../agent-envelope.sh"
|
||||
|
||||
echo "📝 Generating Python CI envelope..."
|
||||
|
||||
LINT_OK=1; [ "${LINT_EXIT:-0}" = "0" ] 2>/dev/null && LINT_OK=0 || true
|
||||
TC_OK=1; [ "${TC_EXIT:-0}" = "0" ] 2>/dev/null && TC_OK=0 || true
|
||||
TEST_OK=1; [ "${TEST_EXIT:-0}" = "0" ] 2>/dev/null && TEST_OK=0 || true
|
||||
|
||||
# Parse test output for pass/fail counts
|
||||
TEST_PASS=0
|
||||
TEST_FAIL=0
|
||||
if [ -f .test-output.txt ]; then
|
||||
grep -E 'passed|failed' .test-output.txt | tail -1 | while IFS= read -r line; do
|
||||
if echo "$line" | grep -qP '\d+ passed'; then
|
||||
TEST_PASS=$(echo "$line" | grep -oP '\d+(?= passed)' || echo 0)
|
||||
fi
|
||||
if echo "$line" | grep -qP '\d+ failed'; then
|
||||
TEST_FAIL=$(echo "$line" | grep -oP '\d+(?= failed)' || echo 0)
|
||||
fi
|
||||
echo "TEST_PASS=${TEST_PASS}" >> "$GITHUB_ENV"
|
||||
echo "TEST_FAIL=${TEST_FAIL}" >> "$GITHUB_ENV"
|
||||
done
|
||||
fi
|
||||
|
||||
ERRORS=0
|
||||
[ "$LINT_OK" -eq 1 ] && ERRORS=$((ERRORS+1))
|
||||
[ "$TC_OK" -eq 1 ] && ERRORS=$((ERRORS+1))
|
||||
[ "$TEST_OK" -eq 1 ] && ERRORS=$((ERRORS+1))
|
||||
|
||||
if [ "$ERRORS" -gt 0 ]; then
|
||||
STATUS="failure"
|
||||
else
|
||||
STATUS="success"
|
||||
fi
|
||||
|
||||
SUMMARY="python-ci: lint=$([ "$LINT_OK" -eq 0 ] && echo '✓' || echo '✗') typecheck=$([ "$TC_OK" -eq 0 ] && echo '✓' || echo '✗') test=${TEST_PASS:-?}✓/${TEST_FAIL:-0}✗"
|
||||
|
||||
AGENT_OUTPUTS=$(cat <<OUTPUTS
|
||||
{
|
||||
"package_manager": "${{ steps.detect.outputs.pm }}",
|
||||
"python_version": "${{ inputs.python-version }}",
|
||||
"checks": {
|
||||
"lint": {"passed": $([ "$LINT_OK" -eq 0 ] && echo 'true' || echo 'false')},
|
||||
"typecheck": {"passed": $([ "$TC_OK" -eq 0 ] && echo 'true' || echo 'false')},
|
||||
"test": {"passed": $([ "$TEST_OK" -eq 0 ] && echo 'true' || echo 'false'), "passed_count": ${TEST_PASS:-0}, "failed_count": ${TEST_FAIL:-0}}
|
||||
}
|
||||
}
|
||||
OUTPUTS
|
||||
export AGENT_OUTPUTS
|
||||
|
||||
write_envelope "ci-python" "$STATUS" "$SUMMARY"
|
||||
|
||||
echo "status=${STATUS}" >> "$GITHUB_OUTPUT"
|
||||
echo "summary=${SUMMARY}" >> "$GITHUB_OUTPUT"
|
||||
Reference in New Issue
Block a user