diff --git a/.github/scripts/tag-jira.py b/.github/scripts/tag-jira.py new file mode 100644 index 00000000..b0b6c4d3 --- /dev/null +++ b/.github/scripts/tag-jira.py @@ -0,0 +1,73 @@ +import base64 +import json +import os +import re +import urllib.error +import urllib.request + +base_url = os.environ['JIRA_BASE_URL'] +tag = os.environ['RELEASE_TAG'] +auth = base64.b64encode( + f"{os.environ['JIRA_USER_EMAIL']}:{os.environ['JIRA_API_TOKEN']}".encode() +).decode() +headers = { + 'Authorization': f'Basic {auth}', + 'Content-Type': 'application/json', + 'Accept': 'application/json', +} + + +def jira(method, path, body=None): + url = f"{base_url}/rest/api/3{path}" + data = json.dumps(body).encode() if body else None + req = urllib.request.Request(url, data=data, headers=headers, method=method) + try: + with urllib.request.urlopen(req) as r: + text = r.read() + return json.loads(text) if text else None + except urllib.error.HTTPError as e: + print(f" WARN {e.code} {method} {path}: {e.read().decode()[:200]}") + return None + + +tickets = sorted(set(re.findall(r'AAI-\d+', os.environ['RELEASE_BODY']))) +if not tickets: + print("No AAI-* tickets found in release notes — nothing to tag.") + raise SystemExit(0) +print(f"Tickets in {tag}: {', '.join(tickets)}") + +project = jira('GET', '/project/AAI') +if not project: + raise SystemExit("ERROR: could not fetch AAI project from JIRA") + +versions = jira('GET', '/project/AAI/versions') or [] +existing = next((v for v in versions if v['name'] == tag), None) +if existing: + version_id = existing['id'] + print(f"Reusing existing JIRA version '{tag}' (id={version_id})") +else: + created = jira('POST', '/version', { + 'name': tag, + 'projectId': project['id'], + 'released': True, + }) + if not created: + raise SystemExit("ERROR: could not create JIRA version") + version_id = created['id'] + print(f"Created JIRA version '{tag}' (id={version_id})") + +for ticket in tickets: + issue = jira('GET', f'/issue/{ticket}?fields=fixVersions,summary') + if not issue: + print(f" SKIP {ticket}: issue not found or inaccessible") + continue + current = issue['fields'].get('fixVersions', []) + if any(v['id'] == version_id for v in current): + print(f" SKIP {ticket}: already tagged with {tag}") + continue + jira('PUT', f'/issue/{ticket}', { + 'fields': {'fixVersions': current + [{'id': version_id}]} + }) + print(f" DONE {ticket}: {issue['fields'].get('summary', '')[:60]}") + +print(f"\nTagged {len(tickets)} ticket(s) with fix version '{tag}'.") diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index ebf81fd9..7021ff96 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -14,6 +14,7 @@ jobs: outputs: release_created: ${{ steps.release.outputs.release_created }} tag_name: ${{ steps.release.outputs.tag_name }} + body: ${{ steps.release.outputs.body }} steps: - id: release uses: googleapis/release-please-action@v4 @@ -89,3 +90,22 @@ jobs: sbom: false cache-from: type=gha cache-to: type=gha,mode=max + + notify-jira: + name: Tag JIRA Tickets for Release + needs: release + if: needs.release.outputs.release_created == 'true' + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - name: Tag JIRA tickets with fix version + env: + JIRA_BASE_URL: https://biocloud.atlassian.net + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + RELEASE_TAG: ${{ needs.release.outputs.tag_name }} + RELEASE_BODY: ${{ needs.release.outputs.body }} + run: python3 .github/scripts/tag-jira.py