diff --git a/automations/catalog/jira-issue-to-pr/manifest.json b/automations/catalog/jira-issue-to-pr/manifest.json index 1eae0168..2e5caa32 100644 --- a/automations/catalog/jira-issue-to-pr/manifest.json +++ b/automations/catalog/jira-issue-to-pr/manifest.json @@ -5,8 +5,8 @@ "description": "Watch a Jira Cloud project for issues with a configurable label and automatically open a GitHub pull request for each new issue found. The target GitHub repo is read from the ticket body - no repo parameter required at deploy time.", "requires": { "integrations": { - "jira": { - "message": "Reads the project for issues carrying the trigger label." + "atlassian-rovo": { + "message": "Provides the Atlassian Rovo MCP connection used to access Jira data." }, "github": { "message": "Opens a pull request on the repository named in the ticket." @@ -15,5 +15,5 @@ }, "popularityRank": 85, "estimatedSetupMinutes": 5, - "exampleImplementation": "Trigger: cron polling (e.g. every 5 minutes)\nRequired secrets: Jira API token, GitHub personal access token (repo + workflow scope)\n\n1. Collect Jira base URL, email, API token secret name, label to watch, and cron schedule from the user. No GitHub repo is needed at deploy time - each ticket must include the target repo (owner/repo) in its body.\n2. Poll POST /rest/api/3/search/jql on the Jira Cloud instance to find open issues carrying the configured label.\n3. Deduplicate against a KV-store-backed set of already-processed issue keys so re-runs never create duplicate PRs.\n4. For each new issue, start an independent OpenHands agent conversation that extracts the GitHub repo from the ticket body, clones it, creates a branch named after the Jira key, implements or scaffolds the requested change, and opens a pull request.\n5. Immediately after the conversation is created, post a Jira comment on the issue: 'I'm on it: '.\n6. Persist the processed issue key immediately after dispatching so the next poll skips it." + "exampleImplementation": "Trigger: cron polling (e.g. every 5 minutes)\nRequired integrations: Atlassian Rovo MCP for Jira access and a GitHub MCP connection. The poller also needs a Jira API token and a GitHub personal access token (repo + workflow scope) as secrets for its direct API calls.\n\n1. Connect Atlassian Rovo MCP and GitHub, then collect the Jira base URL, email, API token secret name, label to watch, and cron schedule from the user. No GitHub repo is needed at deploy time - each ticket must include the target repo (owner/repo) in its body.\n2. Poll POST /rest/api/3/search/jql on the Jira Cloud instance to find open issues carrying the configured label.\n3. Deduplicate against a KV-store-backed set of already-processed issue keys so re-runs never create duplicate PRs.\n4. For each new issue, start an independent OpenHands agent conversation that extracts the GitHub repo from the ticket body, clones it, creates a branch named after the Jira key, implements or scaffolds the requested change, and opens a pull request.\n5. Immediately after the conversation is created, post a Jira comment on the issue: 'I'm on it: '.\n6. Persist the processed issue key immediately after dispatching so the next poll skips it." } diff --git a/marketplaces/openhands-extensions.json b/marketplaces/openhands-extensions.json index 7428c2f5..dfed7887 100644 --- a/marketplaces/openhands-extensions.json +++ b/marketplaces/openhands-extensions.json @@ -607,7 +607,7 @@ "name": "upstream-fork-sync", "source": "./skills/upstream-fork-sync", "description": "Keep a long-lived fork in sync with its upstream. Creates a cron automation that fetches upstream changes, rebases local customizations on top, verifies the software works, and replaces the running version when the rebase is clean.", - "category": "integration", + "category": "integrations", "keywords": [ "fork", "upstream", diff --git a/skills/index.js b/skills/index.js index 741c9a50..5fe5d59c 100644 --- a/skills/index.js +++ b/skills/index.js @@ -559,7 +559,8 @@ export const SKILLS_CATALOG = [ "triggers": [ "/upstream-fork-sync:setup" ], - "content": "# Upstream Fork Sync Automation\n\nCreate a cron automation that keeps a long-lived fork current with its\nupstream source. On every run it fetches the latest upstream changes,\nrebases the fork's local customizations on top, runs a verification check,\nand replaces the deployed version only when the software still works.\n\nThis implements the \"long-lived fork\" pattern: instead of repeatedly\nre-deriving a customization, the local changes are preserved across\nupstream releases and kept working automatically.\n\nWindows PowerShell equivalents for the setup, packaging, upload, and API-check\nshell snippets are in `references/windows.md`.\n\n---\n\n## Prerequisites\n\n### Required secret\n\nVerify that the following secret is set in **OpenHands Settings -> Secrets**:\n\n| Secret name | Token type | Minimum permissions |\n|---|---|---|\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Classic PAT | `repo` |\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Fine-grained PAT | Contents: Read and Write, Metadata: Read |\n\nCheck with:\n```bash\ncurl -s https://api.github.com/user \\\n -H \"Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN\" \\\n | python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))\"\n```\n\nIf the token is missing or invalid, inform the user and stop.\n\n---\n\n## Setup Workflow\n\nFollow these steps in order.\n\n### Step 1 - Verify `GITHUB_PERSONAL_ACCESS_TOKEN`\n\nRun the `curl` check above.\n\n- If absent: *\"GITHUB_PERSONAL_ACCESS_TOKEN is not set. Please add it in\n OpenHands Settings -> Secrets.\"* Stop.\n- If the API returns `{\"message\": \"Bad credentials\"}`: tell the user the\n token is invalid and ask them to update it. Stop.\n\n### Step 2 - Collect configuration\n\nConfirm with the user:\n\n- **Repository** — the long-lived fork to keep synchronized (owner/repo).\n- **Upstream remote** (optional) — the remote the fork tracks. Defaults to\n the repository's GitHub parent.\n- **Local changes** (optional) — a plain-language description of the\n customizations to preserve across rebase.\n- **Verify command** (optional) — the command that confirms the software\n works (e.g. `make test`). If blank, infer a sensible check from the\n repository's build system.\n- **Sync schedule** — how often to run the sync. Default: nightly (`0 3 * * *`).\n\n### Step 3 - Create the automation\n\nCreate the automation via the prompt preset:\n\n```bash\ncurl -s -X POST \"$AUTOMATION_API_URL/v1/preset/prompt\" \\\n -H \"Authorization: Bearer $OPENHANDS_AUTOMATION_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"Upstream fork sync - '\"$REPO\"'\",\n \"prompt\": \"Fetch the latest upstream changes for the fork '\"$REPO\"' and rebase all local changes on top of upstream. Local changes to preserve: '\"$LOCAL_CHANGES\"'. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.\",\n \"repos\": [{\"url\": \"'\"$REPO\"'\", \"provider\": \"github\"}],\n \"trigger\": {\"type\": \"cron\", \"schedule\": \"'\"$SCHEDULE\"'\", \"timezone\": \"'\"$TIMEZONE\"'\"}\n }'\n```\n\nConfirm the automation was created (HTTP 201) and report its ID to the user.\n\n---\n\n## Runtime behavior\n\nOn each scheduled run the automation:\n\n1. Clones the fork and fetches the latest from its upstream remote.\n2. Rebases every local customization commit on top of the newest upstream\n HEAD, resolving conflicts in favor of the local changes where the\n description indicates intent.\n3. Runs the verification command. If none was supplied, infers one from the\n repo's build system (e.g. `make test`, `npm test`, `pytest`).\n4. On success, force-pushes the rebased branch and replaces the currently\n deployed version with the freshly built one.\n5. On failure, leaves the running version untouched and reports the conflict\n or failing check so a human can intervene.\n\n---\n\n## Notes\n\n- The automation is idempotent: a clean upstream with no new commits is a\n no-op.\n- Force-push targets the fork's working branch only, never upstream.\n- If a rebase conflict cannot be resolved automatically, the run fails safe\n and the previously deployed version keeps running." + "content": "# Upstream Fork Sync Automation\n\nCreate a cron automation that keeps a long-lived fork current with its\nupstream source. On every run it fetches the latest upstream changes,\nrebases the fork's local customizations on top, runs a verification check,\nand replaces the deployed version only when the software still works.\n\nThis implements the \"long-lived fork\" pattern: instead of repeatedly\nre-deriving a customization, the local changes are preserved across\nupstream releases and kept working automatically.\n\nWindows PowerShell equivalents for the setup, packaging, upload, and API-check\nshell snippets are in `references/windows.md`.\n\n---\n\n## Prerequisites\n\n### Required secret\n\nVerify that the following secret is set in **OpenHands Settings -> Secrets**:\n\n| Secret name | Token type | Minimum permissions |\n|---|---|---|\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Classic PAT | `repo` |\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Fine-grained PAT | Contents: Read and Write, Metadata: Read |\n\nCheck with:\n```bash\ncurl -s https://api.github.com/user \\\n -H \"Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN\" \\\n | python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))\"\n```\n\nIf the token is missing or invalid, inform the user and stop.\n\n---\n\n## Setup Workflow\n\nFollow these steps in order.\n\n### Step 1 - Verify `GITHUB_PERSONAL_ACCESS_TOKEN`\n\nRun the `curl` check above.\n\n- If absent: *\"GITHUB_PERSONAL_ACCESS_TOKEN is not set. Please add it in\n OpenHands Settings -> Secrets.\"* Stop.\n- If the API returns `{\"message\": \"Bad credentials\"}`: tell the user the\n token is invalid and ask them to update it. Stop.\n\n### Step 2 - Collect configuration\n\nConfirm with the user:\n\n- **Repository** — the long-lived fork to keep synchronized (owner/repo).\n- **Upstream remote** (optional) — the remote the fork tracks. Defaults to\n the repository's GitHub parent.\n- **Local changes** (optional) — a plain-language description of the\n customizations to preserve across rebase.\n- **Verify command** (optional) — the command that confirms the software\n works (e.g. `make test`). If blank, infer a sensible check from the\n repository's build system.\n- **Sync schedule** — how often to run the sync. Default: nightly (`0 3 * * *`).\n\n### Step 3 - Create the automation\n\nCreate the automation via the prompt preset:\n\n```bash\ncurl -s -X POST \"$AUTOMATION_API_URL/v1/preset/prompt\" \\\n -H \"Authorization: Bearer $OPENHANDS_AUTOMATION_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"Upstream fork sync - '\"$REPO\"'\",\n \"prompt\": \"Fetch the latest upstream changes for the fork '\"$REPO\"' and rebase all local changes on top of upstream. Local changes to preserve: '\"$LOCAL_CHANGES\"'. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.\",\n \"repos\": [{\"url\": \"'\"$REPO\"'\", \"provider\": \"github\"}],\n \"trigger\": {\"type\": \"cron\", \"schedule\": \"'\"$SCHEDULE\"'\", \"timezone\": \"'\"$TIMEZONE\"'\"}\n }'\n```\n\nConfirm the automation was created (HTTP 201) and report its ID to the user.\n\n---\n\n## Runtime behavior\n\nOn each scheduled run the automation:\n\n1. Clones the fork and fetches the latest from its upstream remote.\n2. Rebases every local customization commit on top of the newest upstream\n HEAD, resolving conflicts in favor of the local changes where the\n description indicates intent.\n3. Runs the verification command. If none was supplied, infers one from the\n repo's build system (e.g. `make test`, `npm test`, `pytest`).\n4. On success, force-pushes the rebased branch and replaces the currently\n deployed version with the freshly built one.\n5. On failure, leaves the running version untouched and reports the conflict\n or failing check so a human can intervene.\n\n---\n\n## Notes\n\n- The automation is idempotent: a clean upstream with no new commits is a\n no-op.\n- Force-push targets the fork's working branch only, never upstream.\n- If a rebase conflict cannot be resolved automatically, the run fails safe\n and the previously deployed version keeps running.", + "category": "integrations" }, { "name": "uv", diff --git a/tests/test_skills_catalog.py b/tests/test_skills_catalog.py index e349e608..ea4666b6 100644 --- a/tests/test_skills_catalog.py +++ b/tests/test_skills_catalog.py @@ -379,7 +379,7 @@ def test_index_is_up_to_date(self): "code-hosting": 8, "agent-authoring": 8, "code-quality": 6, - "integrations": 5, + "integrations": 6, "writing": 4, "design": 2, "other": 1,