From 4f72bf2a4813ec0ed0f80461195828279e77c5a9 Mon Sep 17 00:00:00 2001 From: Bezalel Date: Tue, 7 Apr 2026 02:17:01 +0000 Subject: [PATCH 1/2] feat(ci): Add syntax guard to prevent broken Python from reaching main - scripts/syntax_guard.py runs py_compile on all .py files - Integrated into Forge CI workflow before green-path E2E - Catches syntax errors that could kill the consciousness loop Closes #82 --- .gitea/workflows/ci.yml | 5 +++++ scripts/syntax_guard.py | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100755 scripts/syntax_guard.py diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 0179967de..082b4f997 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -39,6 +39,11 @@ jobs: OPENAI_API_KEY: "" NOUS_API_KEY: "" + - name: Syntax guard + run: | + source .venv/bin/activate + python scripts/syntax_guard.py + - name: Green-path E2E run: | source .venv/bin/activate diff --git a/scripts/syntax_guard.py b/scripts/syntax_guard.py new file mode 100755 index 000000000..7c41dc9b4 --- /dev/null +++ b/scripts/syntax_guard.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +"""Syntax guard — compile all Python files to catch syntax errors before merge.""" +import py_compile +import sys +from pathlib import Path + +errors = [] +for p in Path(".").rglob("*.py"): + if ".venv" in p.parts or "__pycache__" in p.parts: + continue + try: + py_compile.compile(str(p), doraise=True) + except py_compile.PyCompileError as e: + errors.append(f"{p}: {e}") + print(f"SYNTAX ERROR: {p}: {e}", file=sys.stderr) + +if errors: + print(f"\n{len(errors)} file(s) with syntax errors", file=sys.stderr) + sys.exit(1) +print("All Python files compile successfully") -- 2.43.0 From ebdf9ae8df5a54b5e38d29cd5de2970b0675b062 Mon Sep 17 00:00:00 2001 From: Bezalel Date: Tue, 7 Apr 2026 06:28:01 +0000 Subject: [PATCH 2/2] feat(skills): add Gitea PR & Issue Workflow Automation skill Closes #171 --- .../devops/gitea-workflow-automation/SKILL.md | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 skills/devops/gitea-workflow-automation/SKILL.md diff --git a/skills/devops/gitea-workflow-automation/SKILL.md b/skills/devops/gitea-workflow-automation/SKILL.md new file mode 100644 index 000000000..68f62774b --- /dev/null +++ b/skills/devops/gitea-workflow-automation/SKILL.md @@ -0,0 +1,100 @@ +--- +name: gitea-workflow-automation +title: Gitea Workflow Automation +description: Automate Gitea issues, PRs, and repository workflows via the API for forge CI and backlog tracking. +trigger: When creating Gitea issues, pull requests, or automating forge repository workflows. +--- + +# Gitea Workflow Automation + +## Trigger +Use this skill when automating Gitea operations: creating issues, opening PRs, checking repository state, or integrating Gitea into CI/backlog workflows. + +## Prerequisites +- `GITEA_URL` environment variable set (e.g., `https://forge.alexanderwhitestone.com`) +- `GITEA_TOKEN` environment variable with a valid API token +- `GITEA_USER` or explicit owner/org name +- `curl` and `jq` available in the environment + +## Step-by-Step Workflow + +### 1. Verify Environment +```bash +: "${GITEA_URL?}" "${GITEA_TOKEN?}" "${GITEA_USER?}" +echo "Gitea env OK" +``` + +### 2. List Issues in a Repository +```bash +curl -s -H "Authorization: token ${GITEA_TOKEN}" \ + "${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/issues?state=open&limit=50" | jq '.[] | {number, title, state}' +``` + +### 3. Create an Issue +```bash +curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/issues" \ + -d "{\"title\":\"${TITLE}\",\"body\":\"${BODY}\",\"assignees\":[\"${ASSIGNEE}\"]} +``` +- Escape newlines in `BODY` if passing inline; prefer a JSON file for multi-line bodies. + +### 4. Create a Pull Request +```bash +curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/pulls" \ + -d "{\"title\":\"${TITLE}\",\"body\":\"${BODY}\",\"head\":\"${BRANCH}\",\"base\":\"${BASE_BRANCH}\"}" +``` + +### 5. Check PR Status / Diff +```bash +curl -s -H "Authorization: token ${GITEA_TOKEN}" \ + "${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}" | jq '{number, title, state, mergeable}' +``` + +### 6. Push Code Before Opening PR +```bash +git checkout -b "${BRANCH}" +git add . +git commit -m "${COMMIT_MSG}" +git push origin "${BRANCH}" +``` + +### 7. Add Comments to Issues/PRs +```bash +curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${GITEA_URL}/api/v1/repos/${OWNER}/${REPO}/issues/${NUMBER}/comments" \ + -d "{\"body\":\"${COMMENT_BODY}\"}" +``` + +## Verification Checklist +- [ ] Environment variables are exported and non-empty +- [ ] API responses are parsed with `jq` to confirm success +- [ ] Issue/PR numbers are captured from the JSON response for cross-linking +- [ ] Branch exists on remote before creating a PR +- [ ] Multi-line bodies are written to a temp JSON file to avoid escaping hell + +## Pitfalls +- **Trailing slashes in `GITEA_URL`:** Ensure `GITEA_URL` does not end with `/` or double slashes break URLs. +- **Branch not pushed:** Creating a PR for a local-only branch returns 422. +- **Escape hell:** For multi-line issue/PR bodies, write JSON to a file with `cat < /tmp/payload.json` and pass `@/tmp/payload.json` to curl instead of inline strings. +- **Token scope:** If operations fail with 403, verify the token has `repo` or `write:issue` scope. +- **Pagination:** Default limit is 30 issues; use `?limit=100` or paginate with `page=` for large backlogs. + +## Example: Full Issue Creation with File Body +```bash +cat <<'EOF' > /tmp/issue.json +{ + "title": "[Bezalel] Forge Health Check", + "body": "Build a diagnostic scanner for artifact integrity and permissions.\n\n- Detect .pyc without .py source\n- Detect world-readable sensitive files\n- Output JSON for CI consumption", + "assignees": ["bezalel"], + "labels": ["enhancement", "security"] +} +EOF +curl -s -X POST -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${GITEA_URL}/api/v1/repos/Timmy_Foundation/hermes-agent/issues" \ + -d @/tmp/issue.json | jq '.number' +``` -- 2.43.0