Repository: unicode-org/cldr
Pull request #4459: CLDR-15400 workflow: update jira on merge
Author: login: srl295
Created: 2025-03-06T17:29:32Z | Merged: 2025-03-06T20:37:01Z

- add a new workflow and tooling in tools/scripts/jira-updater which will update jira on merge
- document

CLDR-15400

- [ ] This PR completes the ticket.

<!--
Thank you for your pull request.
Please see https://cldr.unicode.org/index/process for general
information on contributing to CLDR.

1. Make sure the ticket is filed at
https://unicode-org.atlassian.net/projects/CLDR/
2. Update the PR title and first line of this
message to include the ticket ID (CLDR-_____)
3. You will be automatically asked to sign the contributors’
license before the PR is accepted.
- sign: https://cla-assistant.io/unicode-org/cldr
- license: https://www.unicode.org/copyright.html#License
-->

ALLOW_MANY_COMMITS=true


### Example

see the linked ticket. 

<img width="1188" alt="image" src="https://github.com/user-attachments/assets/1ab4fbc5-0adf-4177-aa8a-62e91e91b7d7" />



Diff:
diff --git a/.github/workflows/update-jira.yml b/.github/workflows/update-jira.yml
new file mode 100644
index 00000000000..9c88ff3fde3
--- /dev/null
+++ b/.github/workflows/update-jira.yml
@@ -0,0 +1,32 @@
+name: update-jira
+description: "Update Jira"
+on:
+    pull_request:
+        branches:
+            - main
+            - maint/*
+        types:
+            - closed
+
+jobs:
+    update_jira:
+        # we only want to run this on unicode-org/cldr
+        if: github.repository == 'unicode-org/cldr' && github.event.pull_request.merged == true
+        runs-on: ubuntu-latest
+        steps:
+        - name: Checkout CLDR
+          uses: actions/checkout@v4
+          with:
+            lfs: false
+            fetch-depth: 1
+        - name: 'Setup tool'
+          run: cd tools/scripts/jira-updater && npm ci
+        - name: 'Run tool'
+          env:
+            PR_TITLE: ${{ github.event.pull_request.title }}
+            PR_NUMBER: ${{ github.event.number }}
+            MERGED_TO: ${{ github.event.pull_request.base.ref }}
+            JIRA_HOST: ${{ secrets.JIRA_HOST }}
+            JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
+            JIRA_APITOKEN: ${{ secrets.JIRA_APITOKEN }}
+          run: cd tools/scripts/jira-updater && node update-jira.mjs
diff --git a/tools/scripts/jira-updater/.gitignore b/tools/scripts/jira-updater/.gitignore
new file mode 100644
index 00000000000..07e6e472cc7
--- /dev/null
+++ b/tools/scripts/jira-updater/.gitignore
@@ -0,0 +1 @@
+/node_modules
diff --git a/tools/scripts/jira-updater/README.md b/tools/scripts/jira-updater/README.md
new file mode 100644
index 00000000000..84ec35c8961
--- /dev/null
+++ b/tools/scripts/jira-updater/README.md
@@ -0,0 +1,23 @@
+# Jira Updater
+
+on merged commits, updates the `Merged` field in Jira
+
+called by the update-jira.yml workflow
+
+## setup
+
+Requires these secret env vars to be set:
+
+- JIRA_HOST: top level host including https://
+- JIRA_EMAIL: email for auth
+- JIRA_APITOKEN: API token, get yours at <https://id.atlassian.com/manage-profile/security/api-tokens>
+
+optional, JIRA_FIELD will override the field name from "Merged"
+
+## LICENSE
+
+Copyright © 2025 Unicode, Inc.
+For terms of use, see <http://www.unicode.org/copyright.html>
+SPDX-License-Identifier: Unicode-3.0
+
+see [LICENSE](../../../LICENSE)
diff --git a/tools/scripts/jira-updater/package-lock.json b/tools/scripts/jira-updater/package-lock.json
new file mode 100644
index 00000000000..b695d84444c
--- /dev/null
+++ b/tools/scripts/jira-updater/package-lock.json
@@ -0,0 +1,309 @@
+{
+  "name": "@unicode-org/jira-updater",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "@unicode-org/jira-updater",
+      "version": "1.0.0",
+      "license": "Unicode-3.0",
+      "dependencies": {
+        "jira.js": "^4.1.0"
+      }
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
+      "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
+      "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/formdata-node": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-6.0.3.tgz",
+      "integrity": "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==",
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/jira.js": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/jira.js/-/jira.js-4.1.0.tgz",
+      "integrity": "sha512-ImzoYrcLMm8r3L1Q2i7RsU52bCdekUW5upKC/qC6P3k2tk3bfDP4cUSEs5RVJT1DS3Fj2D2Z3r/zykZ4zwhnag==",
+      "dependencies": {
+        "axios": "^1.8.1",
+        "formdata-node": "^6.0.3",
+        "mime": "^4.0.6",
+        "tslib": "^2.8.1"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mime": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.6.tgz",
+      "integrity": "sha512-4rGt7rvQHBbaSOF9POGkk1ocRP16Md1x36Xma8sz8h8/vfCUI2OtEIeCqe4Ofes853x4xDoPiFLIT47J5fI/7A==",
+      "funding": [
+        "https://github.com/sponsors/broofa"
+      ],
+      "bin": {
+        "mime": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
+    }
+  }
+}
diff --git a/tools/scripts/jira-updater/package.json b/tools/scripts/jira-updater/package.json
new file mode 100644
index 00000000000..ec72b630067
--- /dev/null
+++ b/tools/scripts/jira-updater/package.json
@@ -0,0 +1,16 @@
+{
+  "name": "@unicode-org/jira-updater",
+  "version": "1.0.0",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "Steven R. Loomis <srloomis@unicode.org>",
+  "license": "Unicode-3.0",
+  "description": "",
+  "private": true,
+  "dependencies": {
+    "jira.js": "^4.1.0"
+  }
+}
diff --git a/tools/scripts/jira-updater/update-jira.mjs b/tools/scripts/jira-updater/update-jira.mjs
new file mode 100644
index 00000000000..bec1f164d5d
--- /dev/null
+++ b/tools/scripts/jira-updater/update-jira.mjs
@@ -0,0 +1,158 @@
+/**
+ * Copyright © 2025 Unicode, Inc.
+ * For terms of use, see http://www.unicode.org/copyright.html
+ * SPDX-License-Identifier: Unicode-3.0
+ */
+
+import { Version3Client } from 'jira.js';
+// import fs from 'node:fs';
+import process from 'node:process';
+
+const {
+  // from repo
+  JIRA_HOST, JIRA_EMAIL, JIRA_APITOKEN, JIRA_FIELD,
+  // from workflow
+  PR_TITLE, MERGED_TO, PR_NUMBER,
+  // Github stuff, we'll take what we can get
+  GITHUB_REPOSITORY,
+  GITHUB_ACTOR,
+  GITHUB_WORKFLOW,
+  GITHUB_SHA,
+} = process.env;
+
+
+const DONE_ICON = "✅";
+const GEAR_ICON = "⚙️";
+// const NONE_ICON = "∅";
+// const PACKAGE_ICON = "📦";
+// const SECTION_ICON = "📍";
+const TYPE_ICON = "📂";
+// const WARN_ICON = "⚠️";
+// const POINT_ICON = "👉";
+const MISSING_ICON = "❌";
+const LAND_ICON = "🛬";
+
+
+// DEBUG&&console.dir({JIRA_HOST, JIRA_EMAIL, JIRA_APITOKEN});
+if (!JIRA_HOST || !JIRA_EMAIL || !JIRA_APITOKEN) {
+  throw Error(`${MISSING_ICON} Configuration error: set JIRA_HOST, JIRA_EMAIL, JIRA_APITOKEN`);
+}
+
+if (!PR_TITLE) throw Error(`${MISSING_ICON} PR_TITLE unset, something is wrong`);
+if (!MERGED_TO) throw Error(`${MISSING_ICON} MERGED_TO unset, something is wrong`);
+
+const DEBUG = false;
+
+DEBUG && console.log(`Logging into ${config.host} as ${config.email}`);
+
+const client = new Version3Client({
+  host: JIRA_HOST,
+  authentication: {
+    basic: {
+      email: JIRA_EMAIL,
+      apiToken: JIRA_APITOKEN,
+    },
+  },
+  newErrorHandling: true,
+});
+
+async function main() {
+  const resp = /^([A-Z]+-[0-9]+)/.exec(PR_TITLE);
+  if (!resp || !resp[1]) throw Error(`${MISSING_ICON} Could not find JIRA ticket in ${PR_TITLE}`);
+  const issueIdOrKey = resp[1];
+  const mergedTo = MERGED_TO;
+
+  console.log(`${GEAR_ICON} Logging in`);
+
+  try {
+    await client.myself.getCurrentUser();
+  } catch (e) {
+    DEBUG && console.error(e);
+    throw Error(`${MISSING_ICON} JIRA Authentication error: ${e}`);
+  }
+
+  console.log(`${TYPE_ICON} Updating ${issueIdOrKey} on Jira with a merge to ${mergedTo}…`);
+
+  const ourField = JIRA_FIELD || 'Merged'; // could be a config option later
+
+  DEBUG && console.dir({ issueIdOrKey, mergedTo, ourField }); // our test setup
+
+  const fields = (await client.issueFields.getFields())
+    .filter(({ name }) => name === ourField);
+  if (fields.length !== 1) {
+    throw Error(`${MISSING_ICON} Looking for 1 custom field named ${ourField} but found ${fields.length}`);
+  }
+  const { id: fieldId } = fields[0];
+  DEBUG && console.dir({ fieldId });
+  try {
+    const resp = await client.issues.editIssue({
+      issueIdOrKey,
+      update: {
+        [fieldId]: [
+          { add: mergedTo },
+        ],
+      },
+    });
+    DEBUG && console.dir(resp);
+  } catch (e) {
+    DEBUG && console.error(e);
+    throw Error(`${MISSING_ICON} Error updating ${ourField}+${mergedTo} on ${issueIdOrKey}: ${e}`);
+  }
+  const digits = issueIdOrKey.split("-")[1];
+
+  // the Jira hostname is redacted, so use an old redirect!
+  console.log(`${DONE_ICON} Success: Updated ${ourField} += ${mergedTo} on https://unicode.org/cldr/trac/ticket/${digits}`);
+
+  // now, let's add a comment
+  await client.issueComments.addComment({
+    issueIdOrKey,
+    comment: {
+      version: 1,
+      type: "doc",
+      content: [
+
+        {
+          "type": "heading",
+          "attrs": {
+            "level": 4
+          },
+          "content": [
+            {
+              "type": "text",
+              "text": `${LAND_ICON} Merged PR`
+            }
+          ]
+        },
+
+
+        {
+          type: "paragraph",
+          content: [
+            {
+              type: "text",
+              text: `@${GITHUB_ACTOR} merged a PR to ${GITHUB_REPOSITORY}:${MERGED_TO}`
+            }
+          ]
+        },
+        {
+          type: "paragraph",
+          content: [
+            {
+              type: "text",
+              text: `${PR_TITLE}\n`
+            },
+            {
+              "type": "inlineCard",
+              "attrs": {
+                "url": `https://github.com/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}`,
+              }
+            }
+          ]
+        }
+
+      ]
+    }
+  });
+}
+
+main();


Reviews:
user: login: btangmu
body: It's going to be great knowing which tickets really have merged PRs!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-06T20:25:36Z
user: login: btangmu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T20:27:04Z
user: login: srl295
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T20:36:40Z

Review comments:
diff_hunk: @@ -0,0 +1,158 @@
+/**
+ * Copyright © 2025 Unicode, Inc.
+ * For terms of use, see http://www.unicode.org/copyright.html
+ * SPDX-License-Identifier: Unicode-3.0
+ */
+
+import { Version3Client } from 'jira.js';
+// import fs from 'node:fs';
+import process from 'node:process';
+
+const {
+  // from repo
+  JIRA_HOST, JIRA_EMAIL, JIRA_APITOKEN, JIRA_FIELD,
+  // from workflow
+  PR_TITLE, MERGED_TO, PR_NUMBER,
+  // Github stuff, we'll take what we can get
+  GITHUB_REPOSITORY,
+  GITHUB_ACTOR,
+  GITHUB_WORKFLOW,
+  GITHUB_SHA,
+} = process.env;
+
+
+const DONE_ICON = "✅";
+const GEAR_ICON = "⚙️";
+// const NONE_ICON = "∅";
+// const PACKAGE_ICON = "📦";
+// const SECTION_ICON = "📍";
+const TYPE_ICON = "📂";
+// const WARN_ICON = "⚠️";
+// const POINT_ICON = "👉";
path: tools/scripts/jira-updater/update-jira.mjs
user: login: btangmu
body: Cleaner to avoid commented-out code
created_at: 2025-03-06T20:20:52Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,158 @@
+/**
+ * Copyright © 2025 Unicode, Inc.
path: tools/scripts/jira-updater/update-jira.mjs
user: login: btangmu
body: Most source files don't have copyright notices, and that's advantageous in terms of not needing to update them or make them consistent. Not clear why this file would be exceptional. Very minor, though.
created_at: 2025-03-06T20:27:04Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,158 @@
+/**
+ * Copyright © 2025 Unicode, Inc.
path: tools/scripts/jira-updater/update-jira.mjs
user: login: srl295
body: They actually should- and our XML files do contain notices.

This is a separate part of the tooling. while I wrote it, it's possible someone will stumble upon it looking for Jira integration code.
created_at: 2025-03-06T20:36:40Z
author_association: MEMBER

PR comments:
user: login: jira-pull-request-webhook[bot]
created_at: 2025-03-06T17:33:28Z
body: Notice: the branch changed across the force-push!

- .github/workflows/update-jira.yml is different

[View Diff Across Force-Push](https://github.com/unicode-org/cldr/compare/unicode-org:8a9c6cb..unicode-org:3184bdb)

~ Your Friendly Jira-GitHub PR Checker Bot
author_association: NONE
user: login: srl295
created_at: 2025-03-06T17:33:57Z
body: <img width="864" alt="image" src="https://github.com/user-attachments/assets/7b93d858-80ab-4927-9fbc-aaae370ea359" />

author_association: MEMBER