#!/usr/bin/env python3 # Simple webhook test sender for webhook_endpoint.py # Edit the CONFIG section and Run in Thonny. No CLI args needed. import json import hmac import hashlib import httpx import sqlite3 import os import time # -------------- CONFIG (edit these) -------------- # Your running server (uvicorn webhook_endpoint:app ...) SERVER_BASE = "http://127.0.0.1:8081" # Must match WEBHOOK_SECRET used by the server WEBHOOK_SECRET = "123456gfdsaqwertyuio" # Path to the server's STATE_PATH (so we can seed the PR mapping to skip Bugzilla) STATE_PATH = "/tmp/webhook_state.sqlite" # The real GitHub repo and PR you want to mirror (PR must exist on GitHub) REPO_FULL = "Koozali-SME-Server/smeserver-manager" # owner/repo PR_NUMBER = 9 # existing PR number on GitHub # Cosmetic fields (not used for mirroring logic; placeholders are fine) PR_TITLE = "Test PR from harness" PR_BODY = "Harness test body." BASE_BRANCH = "master" BASE_SHA = "9f8e7d6cafebabe0000deadbeef0000000000000" HEAD_BRANCH = "feature/test" HEAD_SHA = "a1b2c3ddeeddbb0000deadbeef0000000000000" PR_AUTHOR = "octocat" # GitHub login of PR author (cosmetic) # Seed the state DB so the server skips Bugzilla and treats event as "synchronize" SEED_STATE = True # ------------------------------------------------- def sign(secret: str, body: bytes) -> str: return "sha256=" + hmac.new(secret.encode("utf-8"), body, hashlib.sha256).hexdigest() def seed_state_db(state_path: str, pr_key: str, bug_id: int = 1): os.makedirs(os.path.dirname(state_path) or ".", exist_ok=True) conn = sqlite3.connect(state_path) cur = conn.cursor() cur.execute(""" CREATE TABLE IF NOT EXISTS pr_map ( pr_key TEXT PRIMARY KEY, bug_id INTEGER NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ) """) now = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) cur.execute(""" INSERT INTO pr_map (pr_key, bug_id, created_at, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(pr_key) DO UPDATE SET bug_id=excluded.bug_id, updated_at=excluded.updated_at """, (pr_key, bug_id, now, now)) conn.commit() conn.close() def build_payload(): owner, repo = REPO_FULL.split("/", 1) return { "action": "synchronize", # use synchronize to trigger mirroring without Bugzilla create "repository": { "full_name": REPO_FULL, "owner": {"login": owner}, "name": repo, }, "pull_request": { "number": PR_NUMBER, "html_url": f"https://github.com/{REPO_FULL}/pull/{PR_NUMBER}", "title": PR_TITLE, "body": PR_BODY, "user": {"login": PR_AUTHOR, "html_url": f"https://github.com/{PR_AUTHOR}"}, "base": {"ref": BASE_BRANCH, "sha": BASE_SHA}, "head": {"ref": HEAD_BRANCH, "sha": HEAD_SHA, "repo": {"owner": {"login": PR_AUTHOR}}}, "created_at": "2025-09-20T12:34:56Z", "labels": [], }, } def main(): pr_key = f"{REPO_FULL}#{PR_NUMBER}" if SEED_STATE: print(f"Seeding state: {STATE_PATH} -> {pr_key} = bug_id 1 (skips Bugzilla)") seed_state_db(STATE_PATH, pr_key, bug_id=1) # Optional health check (won't fail the run) try: r = httpx.get(f"{SERVER_BASE.rstrip('/')}/healthz", timeout=3) print("Healthz:", r.status_code, r.text) except Exception as e: print("Healthz check failed (continuing):", e) payload = build_payload() body = json.dumps(payload).encode("utf-8") headers = { "X-GitHub-Event": "pull_request", "X-Hub-Signature-256": sign(WEBHOOK_SECRET, body), "Content-Type": "application/json", } url = f"{SERVER_BASE.rstrip('/')}/webhook/github" print(f"POST {url} for {REPO_FULL} PR #{PR_NUMBER}") resp = httpx.post(url, data=body, headers=headers, timeout=30) print("Response:", resp.status_code, resp.text) if __name__ == "__main__": main()