From b77371df07a25c21e81f8b77734c23269825556f Mon Sep 17 00:00:00 2001 From: Prakriti Gupta Date: Fri, 30 Oct 2026 11:44:31 -0700 Subject: [PATCH 1/2] Add workflow to email release notes on tag push Sends the annotated tag message as the release announcement whenever a vX.Y.Z tag is pushed. Can also be run manually for an existing tag. --- .github/workflows/release-email.yml | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/release-email.yml diff --git a/.github/workflows/release-email.yml b/.github/workflows/release-email.yml new file mode 100644 index 00000000..d1551ddb --- /dev/null +++ b/.github/workflows/release-email.yml @@ -0,0 +1,61 @@ +name: Release Announcement Email + +on: + push: + tags: + - "v[0-9]*" + workflow_dispatch: + inputs: + tag: + description: "Existing release tag to announce, e.g. v1.10.0" + required: true + type: string + +permissions: + contents: read + +jobs: + announce: + name: Email release notes + runs-on: ubuntu-latest + + env: + TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} + + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # actions/checkout can leave the tag as a lightweight ref with no + # message, so fetch the annotated tag object explicitly. + - name: Fetch annotated tag + run: | + git fetch --force origin "refs/tags/${TAG}:refs/tags/${TAG}" + if [ "$(git cat-file -t "refs/tags/${TAG}")" != "tag" ]; then + echo "::error::${TAG} is not an annotated tag; nothing to announce" + exit 1 + fi + + # The release notes are the tag message itself. + - name: Compose email body + run: | + { + git for-each-ref "refs/tags/${TAG}" --format='%(contents)' + echo "Tag: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/tree/${TAG}" + } > release-email.txt + cat release-email.txt + + - name: Send email + uses: dawidd6/action-send-mail@v23 + with: + server_address: ${{ secrets.MAIL_SERVER }} + server_port: ${{ vars.MAIL_PORT || '465' }} + secure: ${{ (vars.MAIL_PORT || '465') == '465' }} + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + from: NGPS Releases <${{ secrets.MAIL_USERNAME }}> + to: ${{ vars.RELEASE_MAIL_TO }} + subject: "NGPS ${{ env.TAG }} released" + body: file://release-email.txt From 58e9b88c8e3f87b193de1723d3761b8823887776 Mon Sep 17 00:00:00 2001 From: Prakriti Gupta Date: Fri, 30 Oct 2026 11:54:44 -0700 Subject: [PATCH 2/2] Post release notes to Slack instead of email Caltech mail requires OAuth2 with Duo, which a workflow cannot complete, so announce releases through a Slack incoming webhook instead. The message is still the annotated tag message. --- .github/workflows/release-email.yml | 61 ---------------------------- .github/workflows/release-slack.yml | 63 +++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 61 deletions(-) delete mode 100644 .github/workflows/release-email.yml create mode 100644 .github/workflows/release-slack.yml diff --git a/.github/workflows/release-email.yml b/.github/workflows/release-email.yml deleted file mode 100644 index d1551ddb..00000000 --- a/.github/workflows/release-email.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Release Announcement Email - -on: - push: - tags: - - "v[0-9]*" - workflow_dispatch: - inputs: - tag: - description: "Existing release tag to announce, e.g. v1.10.0" - required: true - type: string - -permissions: - contents: read - -jobs: - announce: - name: Email release notes - runs-on: ubuntu-latest - - env: - TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} - - steps: - - name: Check out repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # actions/checkout can leave the tag as a lightweight ref with no - # message, so fetch the annotated tag object explicitly. - - name: Fetch annotated tag - run: | - git fetch --force origin "refs/tags/${TAG}:refs/tags/${TAG}" - if [ "$(git cat-file -t "refs/tags/${TAG}")" != "tag" ]; then - echo "::error::${TAG} is not an annotated tag; nothing to announce" - exit 1 - fi - - # The release notes are the tag message itself. - - name: Compose email body - run: | - { - git for-each-ref "refs/tags/${TAG}" --format='%(contents)' - echo "Tag: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/tree/${TAG}" - } > release-email.txt - cat release-email.txt - - - name: Send email - uses: dawidd6/action-send-mail@v23 - with: - server_address: ${{ secrets.MAIL_SERVER }} - server_port: ${{ vars.MAIL_PORT || '465' }} - secure: ${{ (vars.MAIL_PORT || '465') == '465' }} - username: ${{ secrets.MAIL_USERNAME }} - password: ${{ secrets.MAIL_PASSWORD }} - from: NGPS Releases <${{ secrets.MAIL_USERNAME }}> - to: ${{ vars.RELEASE_MAIL_TO }} - subject: "NGPS ${{ env.TAG }} released" - body: file://release-email.txt diff --git a/.github/workflows/release-slack.yml b/.github/workflows/release-slack.yml new file mode 100644 index 00000000..03274bd6 --- /dev/null +++ b/.github/workflows/release-slack.yml @@ -0,0 +1,63 @@ +name: Release Announcement + +on: + push: + tags: + - "v[0-9]*" + workflow_dispatch: + inputs: + tag: + description: "Existing release tag to announce, e.g. v1.10.0" + required: true + type: string + +permissions: + contents: read + +jobs: + announce: + name: Post release notes to Slack + runs-on: ubuntu-latest + + env: + TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} + + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # actions/checkout can leave the tag as a lightweight ref with no + # message, so fetch the annotated tag object explicitly. + - name: Fetch annotated tag + run: | + git fetch --force origin "refs/tags/${TAG}:refs/tags/${TAG}" + if [ "$(git cat-file -t "refs/tags/${TAG}")" != "tag" ]; then + echo "::error::${TAG} is not an annotated tag; nothing to announce" + exit 1 + fi + + # The release notes are the tag message itself. Slack treats &, < and > + # as markup, so escape them in the notes. + - name: Compose Slack message + run: | + git for-each-ref "refs/tags/${TAG}" --format='%(contents)' > release-notes.txt + jq -n --rawfile notes release-notes.txt \ + --arg tag "${TAG}" \ + --arg url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/tree/${TAG}" \ + '{ text: ( "*NGPS \($tag) released*\n\n" + + ($notes | sub("\n+$"; "") | gsub("&"; "&") | gsub("<"; "<") | gsub(">"; ">")) + + "\n\n<\($url)|View \($tag) on GitHub>" ) }' > slack-payload.json + cat slack-payload.json + + - name: Post to Slack + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + run: | + if [ -z "${SLACK_WEBHOOK_URL}" ]; then + echo "::error::SLACK_WEBHOOK_URL secret is not set" + exit 1 + fi + curl -fsS -X POST -H 'Content-type: application/json' \ + --data @slack-payload.json "${SLACK_WEBHOOK_URL}"