Full Gitea Actions CI/CD with cPanel git deployment
CI - Test & Lint / lint (push) Failing after 1m11s
CI - Test & Lint / test-schema (push) Failing after 8s
🚀 Deploy to cPanel via Git / ci-check (push) Failing after 8s
🚀 Deploy to cPanel via Git / deploy-to-cpanel (push) Has been skipped
🚀 Deploy to cPanel via Git / deploy-fallback (push) Has been skipped
🚀 Deploy to cPanel via Git / verify (push) Has been skipped
🌊 Sync cPanel Git + WordPress / sync-cpanel (push) Failing after 9s
CI - Test & Lint / security (push) Successful in 1m42s
🌊 Sync cPanel Git + WordPress / sync-nextcloud (push) Failing after 10s
CI - Test & Lint / lint (push) Failing after 1m11s
CI - Test & Lint / test-schema (push) Failing after 8s
🚀 Deploy to cPanel via Git / ci-check (push) Failing after 8s
🚀 Deploy to cPanel via Git / deploy-to-cpanel (push) Has been skipped
🚀 Deploy to cPanel via Git / deploy-fallback (push) Has been skipped
🚀 Deploy to cPanel via Git / verify (push) Has been skipped
🌊 Sync cPanel Git + WordPress / sync-cpanel (push) Failing after 9s
CI - Test & Lint / security (push) Successful in 1m42s
🌊 Sync cPanel Git + WordPress / sync-nextcloud (push) Failing after 10s
- CI workflow: PHP lint, security scan, schema validation - Deploy workflow: SSH -> cPanel git push + WP REST API fallback + verify - Sync workflow: cPanel sync + Nextcloud artifact upload - Health workflow: every 30min plugin health check - Scripts: verify-deploy, deploy-report, check-namespace, health-check, setup-secrets - README with full architecture documentation - Gitea Actions runner registered on Docker Swarm (contabo-swarm-runner)
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify hermes/v1 namespace is registered in WP REST API."""
|
||||
import json, sys
|
||||
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError:
|
||||
print("FAIL: Cannot parse WP index response")
|
||||
sys.exit(1)
|
||||
|
||||
ns = data.get("namespaces", [])
|
||||
if "hermes/v1" in ns:
|
||||
print("HEALTHY: hermes/v1 namespace registered")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"WARN: hermes/v1 NOT found. Namespaces: {ns}")
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract and print plugin info from WP REST API response."""
|
||||
import json, sys
|
||||
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError:
|
||||
print("FAIL: Cannot parse response")
|
||||
sys.exit(1)
|
||||
|
||||
if isinstance(data, dict):
|
||||
code = data.get("code", "")
|
||||
msg = data.get("message", str(data))
|
||||
print(f"API response: [{code}] {msg[:200]}")
|
||||
if code and "rest_plugin_install_error" not in code:
|
||||
sys.exit(0 if "existing_plugin" in code else 1)
|
||||
elif isinstance(data, list):
|
||||
for p in data:
|
||||
if isinstance(p, dict):
|
||||
print(f"{p.get('name','?')} v{p.get('version','?')} status={p.get('status','?')}")
|
||||
else:
|
||||
print(f"Unexpected: {type(data).__name__}")
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Health check for Hermes cPanel Agent via WP REST API.
|
||||
Usage: cat plugins.json | python3 health-check.py
|
||||
"""
|
||||
import json, sys
|
||||
|
||||
def check():
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError:
|
||||
print("FAIL: Cannot parse WP API response")
|
||||
return False
|
||||
|
||||
if isinstance(data, dict) and "code" in data:
|
||||
print(f"FAIL: WP API error - {data.get('message', 'unknown')}")
|
||||
return False
|
||||
|
||||
if not isinstance(data, list):
|
||||
print(f"WARN: Unexpected response format")
|
||||
return True
|
||||
|
||||
found = False
|
||||
for p in data:
|
||||
if isinstance(p, dict) and p.get("textdomain") == "hermes-ai-bridge":
|
||||
status = p.get("status", "unknown")
|
||||
ver = p.get("version", "?")
|
||||
name = p.get("name", "?")
|
||||
found = True
|
||||
if status == "active":
|
||||
print(f"HEALTHY: {name} v{ver} status={status}")
|
||||
return True
|
||||
else:
|
||||
print(f"DEGRADED: {name} v{ver} status={status}")
|
||||
return False
|
||||
|
||||
if not found:
|
||||
print("FAIL: hermes-cpanel-agent plugin not found on WordPress")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
ok = check()
|
||||
sys.exit(0 if ok else 1)
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Setup script: initializes required secrets and cPanel git remote.
|
||||
Run interactively. Output is safe to log.
|
||||
"""
|
||||
import json, os, sys
|
||||
|
||||
REQUIRED_SECRETS = {
|
||||
"WP_APP_PASSWORD": "WordPress Application Password for wmj user",
|
||||
"WP_URL": "WordPress site URL (default: https://ummah.falahos.my)",
|
||||
"WP_APP_USER": "WordPress username for app password (default: wmj)",
|
||||
"CPANEL_SSH_KEY": "cPanel Git SSH private key",
|
||||
"CPANEL_GIT_REMOTE": "cPanel git remote URL (e.g., ssh://user@host:port/path)",
|
||||
"CPANEL_HOST": "cPanel hostname for SSH key scan",
|
||||
"CPANEL_DEPLOY_HOOK_URL": "(Optional) cPanel deployment hook URL",
|
||||
"NEXTCLOUD_HERMES_PASS": "Nextcloud password for hermes user",
|
||||
}
|
||||
|
||||
SECRETS_TO_SET = [
|
||||
"WP_APP_PASSWORD",
|
||||
"WP_URL",
|
||||
"WP_APP_USER",
|
||||
"CPANEL_SSH_KEY",
|
||||
"CPANEL_GIT_REMOTE",
|
||||
"CPANEL_HOST",
|
||||
"NEXTCLOUD_HERMES_PASS",
|
||||
]
|
||||
|
||||
def main():
|
||||
print("=== Hermes cPanel Agent: Gitea Secrets Setup ===")
|
||||
print()
|
||||
print("The following secrets are required for CI/CD workflows:\n")
|
||||
for name, desc in REQUIRED_SECRETS.items():
|
||||
print(f" {name:30s} - {desc}")
|
||||
|
||||
print("\n---")
|
||||
print("To set each secret via Gitea API:")
|
||||
print()
|
||||
|
||||
repo = "wmj/hermes-cpanel-agent"
|
||||
for secret in SECRETS_TO_SET:
|
||||
print(f" # Set {secret}")
|
||||
print(f' curl -s -X PUT -u "wmj:YOUR_PASSWORD" \\')
|
||||
print(f' -H "Content-Type: application/json" \\')
|
||||
print(f' -d \'{{"data":"VALUE_BASE64"}}\' \\')
|
||||
print(f' "https://git.falahos.my/api/v1/repos/{repo}/actions/secrets/{secret}"')
|
||||
print()
|
||||
|
||||
print("=== Alternative: Set via Gitea Web UI ===")
|
||||
print(f" Go to: https://git.falahos.my/{repo}/settings/actions/secrets")
|
||||
print()
|
||||
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--init":
|
||||
print("WARNING: --init is interactive. Only run in a terminal session.")
|
||||
do_init(repo)
|
||||
|
||||
def do_init(repo):
|
||||
"""Interactively set secrets (requires terminal)."""
|
||||
import getpass
|
||||
pw = getpass.getpass("Gitea password for wmj: ")
|
||||
for secret in SECRETS_TO_SET:
|
||||
val = input(f"{secret} [{REQUIRED_SECRETS.get(secret, '')}]: ")
|
||||
if val:
|
||||
import base64
|
||||
encoded = base64.b64encode(val.encode()).decode()
|
||||
cmd = (
|
||||
f'curl -s -X PUT -u "wmj:{pw}" '
|
||||
f'-H "Content-Type: application/json" '
|
||||
f'-d \'{{"data":"{encoded}"}}\' '
|
||||
f'"https://git.falahos.my/api/v1/repos/{repo}/actions/secrets/{secret}"'
|
||||
)
|
||||
result = os.popen(cmd).read()
|
||||
print(f" → {result[:100]}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify Hermes cPanel Agent deployment and REST API availability."""
|
||||
import json, sys, os
|
||||
|
||||
def check_plugins():
|
||||
"""Verify plugin is installed and active via WP REST API."""
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError:
|
||||
return False, "Cannot parse WP REST API response"
|
||||
|
||||
if isinstance(data, dict) and "code" in data:
|
||||
return False, f"WP API error: {data.get('message', str(data))}"
|
||||
|
||||
if not isinstance(data, list):
|
||||
return False, f"Unexpected response: {type(data).__name__}"
|
||||
|
||||
for p in data:
|
||||
if isinstance(p, dict) and p.get("textdomain") == "hermes-ai-bridge":
|
||||
status = p.get("status", "unknown")
|
||||
name = p.get("name", "?")
|
||||
ver = p.get("version", "?")
|
||||
return True, f"OK: {name} v{ver} status={status}"
|
||||
|
||||
return False, "Plugin not found in WP plugins list"
|
||||
|
||||
if __name__ == "__main__":
|
||||
ok, msg = check_plugins()
|
||||
print(msg)
|
||||
sys.exit(0 if ok else 1)
|
||||
Reference in New Issue
Block a user