{"id":24058606,"url":"https://github.com/sebastiaanz/github-status-embed-for-discord","last_synced_at":"2025-04-23T02:48:15.502Z","repository":{"id":43408248,"uuid":"316060896","full_name":"SebastiaanZ/github-status-embed-for-discord","owner":"SebastiaanZ","description":"Send an enhanced GitHub Actions status embed to a Discord webhook.","archived":false,"fork":false,"pushed_at":"2022-03-03T00:12:45.000Z","size":379,"stargazers_count":16,"open_issues_count":4,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-23T02:48:08.198Z","etag":null,"topics":["action","discord","docker","github","github-actions","linux","python","python-discord","status","webhook","workflow"],"latest_commit_sha":null,"homepage":"https://pythondiscord.com","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SebastiaanZ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-11-25T21:48:51.000Z","updated_at":"2024-02-21T09:23:34.000Z","dependencies_parsed_at":"2023-01-05T13:17:12.856Z","dependency_job_id":null,"html_url":"https://github.com/SebastiaanZ/github-status-embed-for-discord","commit_stats":{"total_commits":40,"total_committers":2,"mean_commits":20.0,"dds":"0.050000000000000044","last_synced_commit":"67f67a60934c0254efd1ed741b5ce04250ebd508"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SebastiaanZ%2Fgithub-status-embed-for-discord","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SebastiaanZ%2Fgithub-status-embed-for-discord/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SebastiaanZ%2Fgithub-status-embed-for-discord/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SebastiaanZ%2Fgithub-status-embed-for-discord/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SebastiaanZ","download_url":"https://codeload.github.com/SebastiaanZ/github-status-embed-for-discord/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250360251,"owners_count":21417717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["action","discord","docker","github","github-actions","linux","python","python-discord","status","webhook","workflow"],"created_at":"2025-01-09T05:59:11.943Z","updated_at":"2025-04-23T02:48:15.480Z","avatar_url":"https://github.com/SebastiaanZ.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Actions Status Embed for Discord\n_Send enhanced and informational GitHub Actions status embeds to Discord webhooks._\n\n## Why?\n\nThe default status embeds GitHub delivers for workflow runs are very basic: They contain the name of repository, the result of the workflow run (but not the name!), and the branch that served as the context for the workflow run. If the workflow was triggered by a pull request from a fork, the embed does not even differentatiate between branches on the fork and the base repository: The \"master\" branch in the example below actually refers to the \"master\" branch of a fork!\n\nAnother problem occurs when multiple workflows are chained: Github will send an embed to your webhook for each individual run result. While this is sometimes what you want, if you chain multiple workflows, the number of embeds you receive may flood your log channel or trigger Discord ratelimiting. Unfortunately, there's no finetuning either: With the standard GitHub webhook events, it's all or nothing.\n\n## Solution\n\nAs a solution to this problem, I decided to write a new action that sends an enhanced embed containing a lot more information about the workflow run. The design was inspired by both the status embed sent by Azure as well as the embeds GitHub sends for issue/pull request updates. Here's an example:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/SebastiaanZ/github-status-embed-for-discord/main/img/embed_comparison.png\" title=\"Embed Comparison\"\u003e\n  Comparison between a standard and an enhanced embed as provided by this action.\n\u003c/p\u003e\n\nAs you can see, the standard embeds on the left don't contain a lot of information, while the embed on the right shows the information you'd typically want for a check run on a pull request. While it would be possible to include even more information, there's also obviously a trade-off between the amount of information and the vertical space required to display the embed in Discord.\n\nHaving a custom action also lets you deliver embeds to webhooks when you want to. If you want, you can only send embeds for failing jobs or only at the end of your sequence of chained workflows.\n\n## General Workflow Runs \u0026 PRs\n\nWhen a workflow is triggered for a Pull Request, it's natural to include a bit of information about the Pull Request in the embed to give context to the result. However, when a workflow is triggered for another event, there's no Pull Request involved, which also means we can't include information about that non-existant PR in the embed. That's why the Action automatically tailores the embed towards a PR if PR information is provided and tailors it towards a general workflow run if not.\n\nSpot the difference:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/SebastiaanZ/github-status-embed-for-discord/main/img/type_comparison.png\" title=\"Type Comparison\"\u003e\n\u003c/p\u003e\n\n## Usage\n\nTo use the workflow, simply add it to your workflow and provide the appropriate arguments.\n\n### Example workflow file\n\n```yaml\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  send_embed:\n    runs-on: ubuntu-latest\n    name: Send an embed to Discord\n\n    steps:\n    - name: Run the GitHub Actions Status Embed Action\n      uses: SebastiaanZ/github-status-embed-for-discord@main\n      with:\n        # Discord webhook\n        webhook_id: '1234567890'  # Has to be provided as a string\n        webhook_token: ${{ secrets.webhook_token }}\n\n        # Optional arguments for PR-related events\n        # Note: There's no harm in including these lines for non-PR\n        # events, as non-existing paths in objects will evaluate to\n        # `null` silently and the github status embed action will\n        # treat them as absent.\n        pr_author_login: ${{ github.event.pull_request.user.login }}\n        pr_number: ${{ github.event.pull_request.number }}\n        pr_title: ${{ github.event.pull_request.title }}\n        pr_source: ${{ github.event.pull_request.head.label }}\n```\n\n### Command specification\n\n**Note:** The default values assume that the workflow you want to report the status of is also the workflow that is running this action. If this is not possible (e.g., because you don't have access to secrets in a `pull_request`-triggered workflow), you could use a `workflow_run` triggered workflow that reports the status of the workflow that triggered it. See the recipes section below for an example.\n\n| Argument | Description | Default |\n| --- | --- | :---: |\n| status | Status for the embed; one of [\"succes\", \"failure\", \"cancelled\"] | (required) |\n| webhook_id | ID of the Discord webhook (use a string) | (required) |\n| webhook_token | Token of the Discord webhook | (required) |\n| workflow_name | Name of the workflow | github.workflow |\n| run_id | Run ID of the workflow | github.run_id |\n| run_number | Run number of the workflow  | github.run_number |\n| actor | Actor who requested the workflow | github.actor |\n| repository | Repository; has to be in form `owner/repo` | github.repository |\n| ref | Branch or tag ref that triggered the workflow run | github.ref |\n| sha | Full commit SHA that triggered the workflow run. | github.sha |\n| pr_author_login | **Login** of the Pull Request author | (optional)¹ |\n| pr_number | Pull Request number | (optional)¹ |\n| pr_title | Title of the Pull Request | (optional)¹ |\n| pr_source | Source branch for the Pull Request | (optional)¹ |\n| debug | set to \"true\" to turn on debug logging | false |\n| dry_run | set to \"true\" to not send the webhook request | false |\n| pull_request_payload | PR payload in JSON format² **(deprecated)** | (deprecated)³ |\n\n1) The Action will determine whether to send an embed tailored towards a Pull Request Check Run or towards a general workflow run based on the presence of non-null values for the four pull request arguments. This means that you either have to provide **all** of them or **none** of them.\n\n    Do note that you can typically keep the arguments in the argument list even if your workflow is triggered for non-PR events, as GitHub's object notation (`name.name.name`) will silently return `null` if a name is unset. In the workflow example above, a `push` event would send an embed tailored to a general workflow run, as all the PR-related arguments would all be `null`.\n\n2) The pull request payload may be nested within an array, `[{...}]`. If the array contains multiple PR payloads, only the first one will be picked.\n\n3) Providing a JSON payload will take precedence over the individual pr arguments. If a JSON payload is present, it will be used and the individual pr arguments will be ignored, unless parsing the JSON fails.\n\n## Recipes\n\n### Reporting the status of a `pull_request`-triggered workflow\n\nOne complication with `pull_request`-triggered workflows is that your secrets won't be available if the workflow is triggered for a pull request made from a fork. As you'd typically provide the webhook token as a secret, this makes using this action in such a workflow slightly more complicated.\n\nHowever, GitHub has provided an additional workflow trigger specifically for this situation: [`workflow_run`](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#workflow_run). You can use this event to start a workflow whenever another workflow is being run or has just finished. As workflows triggered by `workflow_run` always run in the base repository, it has full access to your secrets.\n\nTo give your `workflow_run`-triggered workflow access to all the information we need to build a Pull Request status embed, you'll need to share some details from the original workflow in some way. One way to do that is by uploading an artifact. To do that, add these two steps to the end of your `pull_request`-triggered workflow:\n\n```yaml\nname: Lint \u0026 Test\n\non:\n  pull_request:\n\n\njobs:\n  lint-test:\n    runs-on: ubuntu-latest\n\n    steps:\n      # Your regular steps here\n\n      # -------------------------------------------------------------------------------\n\n      # Prepare the Pull Request Payload artifact. If this fails, we\n      # we fail silently using the `continue-on-error` option. It's\n      # nice if this succeeds, but if it fails for any reason, it\n      # does not mean that our lint-test checks failed.\n      - name: Prepare Pull Request Payload artifact\n        id: prepare-artifact\n        if: always() \u0026\u0026 github.event_name == 'pull_request'\n        continue-on-error: true\n        run: cat $GITHUB_EVENT_PATH | jq '.pull_request' \u003e pull_request_payload.json\n\n      # This only makes sense if the previous step succeeded. To\n      # get the original outcome of the previous step before the\n      # `continue-on-error` conclusion is applied, we use the\n      # `.outcome` value. This step also fails silently.\n      - name: Upload a Build Artifact\n        if: always() \u0026\u0026 steps.prepare-artifact.outcome == 'success'\n        continue-on-error: true\n        uses: actions/upload-artifact@v2\n        with:\n          name: pull-request-payload\n          path: pull_request_payload.json\n```\n\nThen, add a new workflow that is triggered whenever the workflow above is run:\n\n```yaml\nname: Status Embed\n\non:\n  workflow_run:\n    workflows:\n      - Lint \u0026 Test\n    types:\n      - completed\n\njobs:\n  status_embed:\n    name:  Send Status Embed to Discord\n    runs-on: ubuntu-latest\n\n    steps:\n      # Process the artifact uploaded in the `pull_request`-triggered workflow:\n      - name: Get Pull Request Information\n        id: pr_info\n        if: github.event.workflow_run.event == 'pull_request'\n        run: |\n          curl -s -H \"Authorization: token $GITHUB_TOKEN\" ${{ github.event.workflow_run.artifacts_url }} \u003e artifacts.json\n          DOWNLOAD_URL=$(cat artifacts.json | jq -r '.artifacts[] | select(.name == \"pull-request-payload\") | .archive_download_url')\n          [ -z \"$DOWNLOAD_URL\" ] \u0026\u0026 exit 1\n          wget --quiet --header=\"Authorization: token $GITHUB_TOKEN\" -O pull_request_payload.zip $DOWNLOAD_URL || exit 2\n          unzip -p pull_request_payload.zip \u003e pull_request_payload.json\n          [ -s pull_request_payload.json ] || exit 3\n          echo \"::set-output name=pr_author_login::$(jq -r '.user.login // empty' pull_request_payload.json)\"\n          echo \"::set-output name=pr_number::$(jq -r '.number // empty' pull_request_payload.json)\"\n          echo \"::set-output name=pr_title::$(jq -r '.title // empty' pull_request_payload.json)\"\n          echo \"::set-output name=pr_source::$(jq -r '.head.label // empty' pull_request_payload.json)\"\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      # Send an informational status embed to Discord instead of the\n      # standard embeds that Discord sends. This embed will contain\n      # more information and we can fine tune when we actually want\n      # to send an embed.\n      - name: GitHub Actions Status Embed for Discord\n        uses: SebastiaanZ/github-status-embed-for-discord@v0.2.1\n        with:\n          # Webhook token\n          webhook_id: '1234567'\n          webhook_token: ${{ secrets.webhook_token }}\n\n          # We need to provide the information of the workflow that\n          # triggered this workflow instead of this workflow.\n          workflow_name: ${{ github.event.workflow_run.name }}\n          run_id: ${{ github.event.workflow_run.id }}\n          run_number: ${{ github.event.workflow_run.run_number }}\n          status: ${{ github.event.workflow_run.conclusion }}\n          sha: ${{ github.event.workflow_run.head_sha }}\n\n          # Now we can use the information extracted in the previous step:\n          pr_author_login: ${{ steps.pr_info.outputs.pr_author_login }}\n          pr_number: ${{ steps.pr_info.outputs.pr_number }}\n          pr_title: ${{ steps.pr_info.outputs.pr_title }}\n          pr_source: ${{ steps.pr_info.outputs.pr_source }}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebastiaanz%2Fgithub-status-embed-for-discord","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsebastiaanz%2Fgithub-status-embed-for-discord","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebastiaanz%2Fgithub-status-embed-for-discord/lists"}