diff --git a/.changeset/chatty-seals-smash.md b/.changeset/chatty-seals-smash.md new file mode 100644 index 00000000000..6c5623778bf --- /dev/null +++ b/.changeset/chatty-seals-smash.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +Add to CI changeset workflow an additional step to update the Jira issue associated with this PR and set the `fixVersions` for the issue with the upcoming core release version. #internal #wip diff --git a/.github/scripts/check-changeset-tags.sh b/.github/scripts/check-changeset-tags.sh index f82c16d5769..227db587d2c 100755 --- a/.github/scripts/check-changeset-tags.sh +++ b/.github/scripts/check-changeset-tags.sh @@ -23,6 +23,7 @@ fi CHANGESET_FILE_PATH=$1 tags_list=( "#nops" "#added" "#changed" "#removed" "#updated" "#deprecation_notice" "#breaking_change" "#db_update" "#wip" "#bugfix" "#internal" ) has_tags=false +found_tags=() if [[ ! -f "$CHANGESET_FILE_PATH" ]]; then echo "Error: File '$CHANGESET_FILE_PATH' does not exist." @@ -40,6 +41,7 @@ fi while IFS= read -r line; do for tag in "${tags_list[@]}"; do if [[ "$line" == *"$tag"* ]]; then + found_tags+=("$tag") echo "Found tag: $tag in $CHANGESET_FILE_PATH" has_tags=true fi @@ -51,3 +53,4 @@ if [[ "$has_tags" == false ]]; then fi echo "has_tags=$has_tags" >> $GITHUB_OUTPUT +echo "found_tags=$(jq -jR 'split(" ") | join(",")' <<< "${found_tags[*]}")" >> $GITHUB_OUTPUT diff --git a/.github/scripts/jira/package.json b/.github/scripts/jira/package.json new file mode 100644 index 00000000000..69f307c5509 --- /dev/null +++ b/.github/scripts/jira/package.json @@ -0,0 +1,15 @@ +{ + "name": "jira", + "version": "0.1.0", + "description": "Updates Jira issue with release information like the version and tags for a PR.", + "main": "update-jira-issue.js", + "type": "module", + "private": true, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@actions/core": "^1.10.1", + "node-fetch": "^2.7.0" + } +} diff --git a/.github/scripts/jira/pnpm-lock.yaml b/.github/scripts/jira/pnpm-lock.yaml new file mode 100644 index 00000000000..e6e4d82b150 --- /dev/null +++ b/.github/scripts/jira/pnpm-lock.yaml @@ -0,0 +1,78 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@actions/core': + specifier: ^1.10.1 + version: 1.10.1 + node-fetch: + specifier: ^2.7.0 + version: 2.7.0 + +packages: + + /@actions/core@1.10.1: + resolution: {integrity: sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==} + dependencies: + '@actions/http-client': 2.2.1 + uuid: 8.3.2 + dev: false + + /@actions/http-client@2.2.1: + resolution: {integrity: sha512-KhC/cZsq7f8I4LfZSJKgCvEwfkE8o1538VoBeoGzokVLLnbFDEAdFD3UhoMklxo2un9NJVBdANOresx7vTHlHw==} + dependencies: + tunnel: 0.0.6 + undici: 5.28.4 + dev: false + + /@fastify/busboy@2.1.1: + resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} + engines: {node: '>=14'} + dev: false + + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + + /tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + dev: false + + /undici@5.28.4: + resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} + engines: {node: '>=14.0'} + dependencies: + '@fastify/busboy': 2.1.1 + dev: false + + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false diff --git a/.github/scripts/jira/update-jira-issue.js b/.github/scripts/jira/update-jira-issue.js new file mode 100644 index 00000000000..5977381052f --- /dev/null +++ b/.github/scripts/jira/update-jira-issue.js @@ -0,0 +1,113 @@ +#!/usr/bin/env node + +import * as core from "@actions/core"; +import fetch from "node-fetch"; + +function parseIssueNumber(prTitle, commitMessage, branchName) { + const jiraIssueRegex = /[A-Z]{2,}-\d+/; + if (!!branchName && jiraIssueRegex.test(branchName.toUpperCase())) { + return branchName.toUpperCase().match(jiraIssueRegex)[0]; + } else if ( + !!commitMessage && + jiraIssueRegex.test(commitMessage.toUpperCase()) + ) { + return commitMessage.toUpperCase().match(jiraIssueRegex)[0]; + } else if (!!prTitle && jiraIssueRegex.test(prTitle.toUpperCase())) { + return prTitle.toUpperCase().match(jiraIssueRegex)[0]; + } else { + return null; + } +} + +function getLabels(tags) { + const labelPrefix = "core-release"; + return tags.map((tag) => { + return { + add: `${labelPrefix}/${tag.substring(1)}`, + }; + }); +} + +async function updateJiraIssue( + jiraHost, + jiraUserName, + jiraApiToken, + issueNumber, + tags, + fixVersionName +) { + const token = Buffer.from(`${jiraUserName}:${jiraApiToken}`).toString( + "base64" + ); + const bodyData = { + update: { + labels: getLabels(tags), + fixVersions: [{ set: [{ name: fixVersionName }] }], + }, + }; + + fetch(`https://${jiraHost}/rest/api/3/issue/${issueNumber}`, { + method: "PUT", + headers: { + Authorization: `Basic ${token}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify(bodyData), + }) + .then((response) => { + console.log(`Response: ${JSON.stringify(response)}`); + return response.text(); + }) + .then((text) => console.log(text)) + .catch((err) => console.error(err)); +} + +async function run() { + try { + const jiraHost = process.env.JIRA_HOST; + const jiraUserName = process.env.JIRA_USERNAME; + const jiraApiToken = process.env.JIRA_API_TOKEN; + const chainlinkVersion = process.env.CHAINLINK_VERSION; + const prTitle = process.env.PR_TITLE; + const commitMessage = process.env.COMMIT_MESSAGE; + const branchName = process.env.BRANCH_NAME; + // tags are not getting used at the current moment so will always default to [] + const tags = process.env.FOUND_TAGS + ? process.env.FOUND_TAGS.split(",") + : []; + + // Check for the existence of JIRA_HOST and JIRA_USERNAME and JIRA_API_TOKEN + if (!jiraHost || !jiraUserName || !jiraApiToken) { + core.setFailed( + "Error: Missing required environment variables: JIRA_HOST and JIRA_USERNAME and JIRA_API_TOKEN." + ); + return; + } + + // Checks for the Jira issue number and exit if it can't find it + const issueNumber = parseIssueNumber(prTitle, commitMessage, branchName); + if (!issueNumber) { + core.info( + "No JIRA issue number found in: PR title, commit message, or branch name. Please include the issue ID in one of these." + ); + core.notice( + "No JIRA issue number found in: PR title, commit message, or branch name. Please include the issue ID in one of these." + ); + return; + } + const fixVersionName = `chainlink-v${chainlinkVersion}`; + await updateJiraIssue( + jiraHost, + jiraUserName, + jiraApiToken, + issueNumber, + tags, + fixVersionName + ); + } catch (error) { + core.setFailed(error.message); + } +} + +run(); diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index c9b557a8bc9..df1c1ceb200 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -58,6 +58,51 @@ jobs: contracts-changeset: - added: 'contracts/.changeset/**' + - name: Check for changeset tags for core + id: changeset-tags + if: ${{ steps.files-changed.outputs.core-changeset == 'true' }} + shell: bash + run: bash ./.github/scripts/check-changeset-tags.sh ${{ steps.files-changed.outputs.core-changeset_files }} + + - name: Setup pnpm + uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + if: ${{ steps.files-changed.outputs.core == 'true' || steps.files-changed.outputs.shared == 'true' }} + with: + version: ^8.0.0 + + - name: Setup node + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + if: ${{ steps.files-changed.outputs.core == 'true' || steps.files-changed.outputs.shared == 'true' }} + with: + node-version: 20 + cache: pnpm + cache-dependency-path: ./pnpm-lock.yaml + + - name: Get next chainlink core version + id: chainlink-version + if: ${{ steps.files-changed.outputs.core == 'true' || steps.files-changed.outputs.shared == 'true' }} + run: | + pnpm install && pnpm changeset version + echo "chainlink_version=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Update Jira ticket for core + if: ${{ steps.files-changed.outputs.core == 'true' || steps.files-changed.outputs.shared == 'true' }} + shell: bash + working-directory: ./.github/scripts/jira + run: | + echo "COMMIT_MESSAGE=$(git log -1 --pretty=format:'%s')" >> $GITHUB_ENV + pnpm install && node update-jira-issue.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JIRA_HOST: ${{ secrets.JIRA_HOST }} + JIRA_USERNAME: ${{ secrets.JIRA_USERNAME }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + CHAINLINK_VERSION: ${{ steps.chainlink-version.outputs.chainlink_version }} + PR_TITLE: ${{ github.event.pull_request.title }} + BRANCH_NAME: ${{ github.event.pull_request.head.ref }} + - name: Make a comment uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0 if: ${{ steps.files-changed.outputs.core == 'true' || steps.files-changed.outputs.shared == 'true' }} @@ -99,12 +144,6 @@ jobs: echo "Please run pnpm changeset to add a changeset for contracts." exit 1 - - name: Check for changeset tags for core - id: changeset-tags - if: ${{ steps.files-changed.outputs.core-changeset == 'true' }} - shell: bash - run: bash ./.github/scripts/check-changeset-tags.sh ${{ steps.files-changed.outputs.core-changeset_files }} - - name: Make a comment uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0 if: ${{ steps.files-changed.outputs.core-changeset == 'true' }}