From e292a81a35a5be93e790933550c811b027afd1e5 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Tue, 19 Nov 2024 23:11:01 -0500 Subject: [PATCH 1/4] Start on `announce-release` job Some users and developers, such as downstream package maintainers, would benefit from a way to subscribe on GitHub to notifications for releases associated with the `gitoxide` crate, i.e. the releases done via `release.yml`, but not of the many `gix-` crates that they would also be notified about if they watched releases via the GitHub watch feature. The idea here is to create a way for people to easily be notified by subscribing to a specific locked discussion thread that a GitHub Actions job posts after publishing a `gitoxide` release. I am testing this with EliahKagan#6, but that is just for testing. If this is used, the actual announcements "discussion" thread would be separate, and on GitoxideLabs/gitoxide (not on a fork). --- .github/workflows/release.yml | 81 ++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7323d36986e..3fdf945029c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,7 +51,7 @@ jobs: "v$manifest_version" ) echo 'OK: Release name/version agrees with Cargo.toml version.' ;; - TEST-* | *-DO-NOT-USE ) + TEST-* | *-DO-NOT-USE ) # NOTE: If changed, change it in `announce-release` below, too. echo 'OK: Release name/version is strange but marked as such.' ;; "$manifest_version" ) @@ -429,6 +429,85 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + announce-release: + runs-on: ubuntu-latest + + needs: [ create-release, publish-release ] + + permissions: + contents: read + discussions: write + + env: + VERSION: ${{ needs.create-release.outputs.version }} + + steps: + - name: Find the discussion ID + run: | + [[ "$DISCUSSION_URL" =~ ^https://github\.com/([^/:@]+)/([^/:@]+)/discussions/([0-9]+)$ ]] + owner="${BASH_REMATCH[1]}" + name="${BASH_REMATCH[2]}" + number="${BASH_REMATCH[3]}" + + id="$(gh api graphql -f query=' + query GetDiscussionId($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name) { + discussion(number: $number) { + id + } + } + }' -F owner="$owner" -F name="$name" -F number="$number" --jq .data.repository.discussion.id)" + + echo "DISCUSSION_ID=$id" >> "$GITHUB_ENV" + env: + DISCUSSION_URL: ${{ vars.RELEASE_ANNOUNCEMENTS_URL }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # FIXME: Also consider a release of any name as a test, if it has not been published! + - name: Avoid announcing a test in a non-test thread + run: | + case "$VERSION" in + TEST-* | *-DO-NOT-USE ) # NOTE: Should be the same pattern as in `create-release` above. + echo "Looks like we're testing releasing. Checking discussion title." + + title="$(gh api graphql -f query=' + query($id: ID!) { + node(id: $id) { + ... on Discussion { + title + } + } + }' -F id="$DISCUSSION_ID" --jq .data.node.title)" + + grep -Eiqz '^[[(]?test\b' <<<"$title" + ;; + esac + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Compose the comment + run: | + grep -Eqx '[[:alnum:]._+-]+' <<<"$VERSION" # Ensure the version needs no sanitization. + release_url="https://github.com/$REPOSITORY/releases/tag/$VERSION" + comment_body="\`gitoxide\` [$VERSION]($release_url) has been released." + echo "COMMENT_BODY=$comment_body" >> "$GITHUB_ENV" + env: + REPOSITORY: ${{ github.repository }} + + - name: Post the comment + run: | + gh api graphql -f query=' + mutation PostComment($discussionId: ID!, $body: String!) { + addDiscussionComment(input: {discussionId: $discussionId, body: $body}) { + comment { + id + body + } + } + }' -F discussionId="$DISCUSSION_ID" -F body="$COMMENT_BODY" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + installation: strategy: matrix: From 4c1f54071e5eab4c4ab8a7a92a6482b4f5b8ff20 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Wed, 20 Nov 2024 03:47:31 -0500 Subject: [PATCH 2/4] Do the title check if the release is still a draft For testing, this checks the discussion title only when the release is unpublished. Just for now, it does not also do the check in the previously covered case where the release is named to indicate that it exists to test releasing rather than being a "real" release. Really, we should test both, and we should probably check the name first, since when both apply, the name indicating that it is not even an actual release is the more important reason to avoid announcing it by commenting in a discussion for real release announcements. But the name check is temporarily commented out, in order to more easily test the draft status check. This checks whether the release is a draft by actually accessing the release, rather than repeating the check from the final step in the preceding `publish-release` job for whether it *should* be published, and also rather than storing and retrieving information about whether that job did publish it. The reason to actually check the release is that the draft status can be changed manually in either direction at any time: - Changing it between the jobs is plausible if one notices that something is unexpectedly broken (or unexpectedly okay). - But the more important reason is that we have to check each time, rather than using any state from the previous jobs we depend on, if we are to work as expected when the `announce-release` job itself is re-run (which makes sense to do, for example, if one has fixed a problem and manually marked a release non-draft). This change also refactors the steps, combining the two last steps in the job. The combination of the new `gh` invocation to check the draft status, and the consolation of those two steps, makes it so `GITHUB_TOKEN` is used in all steps and `REPOSITORY` is used in multiple steps. So this moves them from per-step `env` to job-level `env`. That leaves `DISCUSSION_URL` as the only step-level `env` key. Since it's not sensitive, nor likely to be used accidentally anywhere it shouldn't, it is also moved to the job-level `env` so it's easier to see what the job depends on. --- .github/workflows/release.yml | 54 +++++++++++++++++------------------ 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3fdf945029c..df5d0aee396 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -439,7 +439,10 @@ jobs: discussions: write env: + REPOSITORY: ${{ github.repository }} VERSION: ${{ needs.create-release.outputs.version }} + DISCUSSION_URL: ${{ vars.RELEASE_ANNOUNCEMENTS_URL }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Find the discussion ID @@ -459,43 +462,40 @@ jobs: }' -F owner="$owner" -F name="$name" -F number="$number" --jq .data.repository.discussion.id)" echo "DISCUSSION_ID=$id" >> "$GITHUB_ENV" - env: - DISCUSSION_URL: ${{ vars.RELEASE_ANNOUNCEMENTS_URL }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # FIXME: Also consider a release of any name as a test, if it has not been published! + # FIXME: Uncomment name-checking case, after testing the draft-checking case! - name: Avoid announcing a test in a non-test thread run: | case "$VERSION" in - TEST-* | *-DO-NOT-USE ) # NOTE: Should be the same pattern as in `create-release` above. - echo "Looks like we're testing releasing. Checking discussion title." - - title="$(gh api graphql -f query=' - query($id: ID!) { - node(id: $id) { - ... on Discussion { - title - } - } - }' -F id="$DISCUSSION_ID" --jq .data.node.title)" - - grep -Eiqz '^[[(]?test\b' <<<"$title" + # TEST-* | *-DO-NOT-USE ) # NOTE: Should be the same pattern as in `create-release` above. + # echo "The release name indicates testing, so we'll only post if the thread is for that." + # ;; + * ) + is_draft="$(gh release --repo="$REPOSITORY" view "$VERSION" --json isDraft --jq .isDraft)" + if [ "$is_draft" = false ]; then + exit 0 # OK to post in a non-test announcement thread. + fi + echo "The release is not published, so we'll only post if the thread is for testing." ;; esac - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Compose the comment + title="$(gh api graphql -f query=' + query($id: ID!) { + node(id: $id) { + ... on Discussion { + title + } + } + }' -F id="$DISCUSSION_ID" --jq .data.node.title)" + + grep -Eiqz '^[[(]?test\b' <<<"$title" + + - name: Post the comment run: | grep -Eqx '[[:alnum:]._+-]+' <<<"$VERSION" # Ensure the version needs no sanitization. release_url="https://github.com/$REPOSITORY/releases/tag/$VERSION" comment_body="\`gitoxide\` [$VERSION]($release_url) has been released." - echo "COMMENT_BODY=$comment_body" >> "$GITHUB_ENV" - env: - REPOSITORY: ${{ github.repository }} - - name: Post the comment - run: | gh api graphql -f query=' mutation PostComment($discussionId: ID!, $body: String!) { addDiscussionComment(input: {discussionId: $discussionId, body: $body}) { @@ -504,9 +504,7 @@ jobs: body } } - }' -F discussionId="$DISCUSSION_ID" -F body="$COMMENT_BODY" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + }' -F discussionId="$DISCUSSION_ID" -F body="$comment_body" installation: strategy: From fe5c3accaa5a4d5c668aa5583b58d5d18c31ab70 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Wed, 20 Nov 2024 05:23:21 -0500 Subject: [PATCH 3/4] Increase token permissions, to view draft releases Draft releases are viewable to users with write permissions on the repository. Accordingly, for a GitHub tokens for a GitHub Actions job to be able to view draft releases, it needs `contents: write`, even if (as here) it is neither changing any state associated with the release nor accessing other repository content in any way. (It does read and write the discussion, but `content` doesn't cover that.) Otherwise, attempting to view the release would have the same effect whether the release exists but is a draft, or does not exist at all. Even in testing, we do not want the job to go ahead and announce a nonexistent release, such as one that has been deleted (including deleted after the job has run but before it is re-run), into a discussion. So it can be useful to distingiush those cases. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df5d0aee396..a4f06bc01d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -435,7 +435,7 @@ jobs: needs: [ create-release, publish-release ] permissions: - contents: read + contents: write # Needed to distinguish unpublished (still draft) from missing releases. discussions: write env: From 6b852d17a5c8691be5adc019ebb7e50789561a5f Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Wed, 20 Nov 2024 06:01:43 -0500 Subject: [PATCH 4/4] Also do the title check if the release is named for testing Now that the case where the release's name does not cause it to be treated as a test release, but where the release is not announced to a non-test discussion thread because it was found not to be published (or was published but was marked back to draft), has been manually tested, this commit uncomments the name-check case. That is to say that this makes it so a release named as being for testing, rather than being a "real" release, will trigger the discussion title check. That way, releases that exist just to test releasing are not inadvertently announced to discussion threads intended for notifying about actual releases. This check is done first, since it is the primary case for not announcing a release that is otherwise eligible to be announced, and because the log message for it makes more sense when both reasons to do the title check apply. At least for now, when the release name indicates the release is for testing, the draft status of the release is not retrieved or examined. (However, as a possible future direction, it might be worthwhile to check all three things -- release name, release draft status, and whether the discussion is titled indicating it is for testing -- and report them in the GitHub Actions workflow/job log, for all combinations.) This commit also adds a comment on the `announce-release` job saying what it's for, as we have on the other `*-release` jobs. --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4f06bc01d7..b76dd8c5f26 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -429,6 +429,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Comment in a locked discussion that notifies about only `gitoxide` (e.g. not `gix-*`) releases. announce-release: runs-on: ubuntu-latest @@ -463,13 +464,12 @@ jobs: echo "DISCUSSION_ID=$id" >> "$GITHUB_ENV" - # FIXME: Uncomment name-checking case, after testing the draft-checking case! - name: Avoid announcing a test in a non-test thread run: | case "$VERSION" in - # TEST-* | *-DO-NOT-USE ) # NOTE: Should be the same pattern as in `create-release` above. - # echo "The release name indicates testing, so we'll only post if the thread is for that." - # ;; + TEST-* | *-DO-NOT-USE ) # NOTE: Should be the same pattern as in `create-release` above. + echo "The release name indicates testing, so we'll only post if the thread is for that." + ;; * ) is_draft="$(gh release --repo="$REPOSITORY" view "$VERSION" --json isDraft --jq .isDraft)" if [ "$is_draft" = false ]; then