{"id":27785985,"url":"https://github.com/mskelton/multi-comment","last_synced_at":"2026-04-29T16:39:08.865Z","repository":{"id":288756286,"uuid":"968960659","full_name":"mskelton/multi-comment","owner":"mskelton","description":"Combine outputs from many jobs into a single comment","archived":false,"fork":false,"pushed_at":"2025-05-01T15:32:04.000Z","size":419,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-16T21:36:32.442Z","etag":null,"topics":["comment","github","github-actions"],"latest_commit_sha":null,"homepage":"https://github.com/marketplace/actions/multi-comment","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mskelton.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-19T04:32:13.000Z","updated_at":"2025-05-01T15:31:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"2213c2b4-f2a3-4c8c-a057-74c0ba8584c2","html_url":"https://github.com/mskelton/multi-comment","commit_stats":null,"previous_names":["mskelton/multi-comment"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/mskelton/multi-comment","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mskelton%2Fmulti-comment","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mskelton%2Fmulti-comment/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mskelton%2Fmulti-comment/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mskelton%2Fmulti-comment/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mskelton","download_url":"https://codeload.github.com/mskelton/multi-comment/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mskelton%2Fmulti-comment/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270914856,"owners_count":24667218,"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","status":"online","status_checked_at":"2025-08-17T02:00:09.016Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["comment","github","github-actions"],"created_at":"2025-04-30T15:37:18.437Z","updated_at":"2026-04-29T16:39:08.829Z","avatar_url":"https://github.com/mskelton.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Multi Comment Action\n\nCombine outputs from many jobs into a single comment\n\n## Usage\n\nCreate a file named `.github/multi-comment.yml` to define the section IDs that _can_ appear in the\ncomment.\n\n```yaml\nsections:\n  - test_results\n  - deploy_preview\n  - perf_stats\n```\n\nNow, in your GitHub action workflow, you can run the action with the comment `section` and `message`\nwhich will be posted.\n\n```yaml\n- name: Comment with test results\n  uses: mskelton/multi-comment@v1\n  with:\n    section: test_results\n    message: Hello world\n```\n\n### Comment from a file\n\nTo comment from a file on disk, you can use the `file-path` input instead of `message`.\n\n_If both a `message` and `file-path` are provided, `message` will take precedence._\n\n```yaml\n- name: Comment with test results\n  uses: mskelton/multi-comment@v1\n  with:\n    section: test_results\n    file-path: /path/to/file.txt\n```\n\n### Specifying the pull request\n\nIf you are running this action from a workflow without the `pull_request` context (e.g.\n`workflow_run`), you can use the `pr-number` input to specify which pull request to comment on.\n\n```yaml\n- name: Comment with test results\n  uses: mskelton/multi-comment@v1\n  with:\n    section: test_results\n    message: Hello world\n    pr-number: 123\n```\n\n### Inputs\n\n#### Action Inputs\n\n| Name        | Description                                                       | Required | Default                     |\n| ----------- | ----------------------------------------------------------------- | -------- | --------------------------- |\n| `section`   | The section ID that matches with the value in `multi-comment.yml` | ✅       |                             |\n| `message`   | Comment body                                                      |          |                             |\n| `file-path` | File path containing the comment body                             |          |                             |\n| `pr-number` | The pull request number where to create the comment               |          | current pull request number |\n| `token`     | GitHub auth token                                                 |          | `${{ github.token }}`       |\n| `config`    | Path to the config file                                           |          | `.github/multi-comment.yml` |\n\n#### Metadata (`mutli-comment.yml`)\n\n| Name       | Description                                                                | Required |\n| ---------- | -------------------------------------------------------------------------- | -------- |\n| `sections` | A list of section IDs that defines the order of comment sections           | ✅       |\n| `title`    | An optional title for the comment                                          |          |\n| `intro`    | An optional introduction for the comment that is displayed under the title |          |\n\n### Outputs\n\n| Name       | Description                                    |\n| ---------- | ---------------------------------------------- |\n| `id`       | Comment ID that was created or updated         |\n| `html_url` | URL of the comment that was created or updated |\n\n### Permissions\n\nDepending on the permissions granted to your token, you may lack some rights. To run successfully,\nthis actions needs at least:\n\n```yaml\npermissions:\n  pull-requests: write\n```\n\n## How does it work?\n\nI built this action to solve a problem we have at [Ramp](https://ramp.com/) of lots of CI outputs\nthat each need to post back to the PR via comments. The problem is, the more comments you have, the\nmore noisy it gets.\n\nThe idea was, what if you had a single comment that contained everything? Test results, deploy\npreview URLs, warnings, etc. When you try to build that though, there are some challenges.\n\nFirst, it should support workflows running in parallel, so the order in which the comments are\nposted is non deterministic. However, we want the order of sections in the comment to be consistent\nbetween runs. Additionally, we need to support updating the comment if you push a new commit, and\nthe test results are now passing instead of failed.\n\nThe GitHub issue comments API only supports sending a complete comment body when making updates, so\nif we just get the current value and send it back with our updates, its possible that two separate\njobs update the comment at the same time and one of the updates will be lost. To workaround this, we\nneed a way for jobs to acquire a \"lock\" on the comment, so that they can safely get the current\ncomment value, make edits, and push the updated value back to GitHub.\n\nWhat better locking mechanism than reactions! Turns out, the\n[create reaction](https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-an-issue-comment)\nAPI will return a `201 Created` status when a reaction is newly created and a `200 OK` when the\nreaction already exists. Using this subtle API detail, this action will attempt to acquire a lock by\ncreating the reaction and waiting for a `201` status code. If it receives a `200` status code, it\nwill sleep and retry until it is able to acquire a lock (it will fail after 30 seconds if it fails\nto acquire a lock). Once the lock is acquired, the existing comment will be downloaded, edited, and\npushed back to GitHub. After updating the comment, the lock is released by deleting the reaction.\n\nSimple right? 😉\n\n## Thanks\n\nThanks to\n[thollander/actions-comment-pull-request](https://github.com/thollander/actions-comment-pull-request)\nfor the original action this was based on. I have made many changes, but the foundation provided in\nthat action was very helpful!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmskelton%2Fmulti-comment","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmskelton%2Fmulti-comment","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmskelton%2Fmulti-comment/lists"}