GitHub Action: publish a markdown report as a shareable link
One step in a workflow turns any generated markdown file — a test report, a bundle-size table, release notes, a bug report — into a rendered link with an expiry, and hands the URL to the next step. No account, no API token.
Updated
What does the action do?
It POSTs the file to the Docs MD share API, masks the returned edit token, and exposes the reading URL, raw URL, id and expiry as step outputs. It is a composite action (bash + curl + jq), so there is no container to pull and nothing to install.
- name: Publish report
id: report
uses: invisible-hand/share-markdown-action@v1
with:
file: bundle-report.md
expiry: 7d # 1d | 7d | 30d | never
- name: Link it from the job summary
run: echo "Readable report: ${{ steps.report.outputs.url }}" >> "$GITHUB_STEP_SUMMARY"What are the inputs and outputs?
| Input | Default | Notes |
|---|---|---|
| file | required | Markdown file, up to 120,000 characters |
| expiry | 7d | 1d, 7d, 30d or never |
| filename | basename of file | Shown on the page and used for downloads |
| share-id | — | Update this share in place instead of creating one |
| edit-token | — | Required with share-id; pass from a secret |
| api-url | https://docs-md.com | For a self-hosted instance |
| Output | Meaning |
|---|---|
| url | Rendered page for people |
| raw-url | text/markdown for scripts and AI assistants |
| id | Share id |
| edit-token | Masked in logs; store as a secret to update or delete later |
| expires-at | Unix epoch in milliseconds, empty for never |
How do I post a test report link on every pull request?
Generate the report, publish it, and comment the URL on the PR. Reviewers without a GitHub account for that repository — a client, a QA vendor — can still open it.
name: Test report
on: [pull_request]
jobs:
report:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test -- --reporter=markdown > test-report.md || true
- id: share
uses: invisible-hand/share-markdown-action@v1
with:
file: test-report.md
expiry: 7d
- uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
owner: context.repo.owner, repo: context.repo.repo,
issue_number: context.issue.number,
body: `Test report for ${context.sha.slice(0,7)}: ${{ steps.share.outputs.url }}`
})How do I keep one stable URL and update it on every run?
Create the share once (a first run of the action, or the editor), save the id and edit-token outputs as repository secrets, and pass them back in. The action then updates the existing page instead of creating a new one — a status page, a nightly benchmark table, a changelog draft.
- uses: invisible-hand/share-markdown-action@v1
with:
file: STATUS.md
share-id: ${{ secrets.STATUS_SHARE_ID }}
edit-token: ${{ secrets.STATUS_EDIT_TOKEN }}What are the limits?
- 120,000 characters per file; 20 shares per minute per IP (a busy monorepo with many parallel jobs should stagger publishes).
- Shares are public to anyone with the link. Never publish secrets, private hostnames or customer data.
- Expiry deletes the page at its URL; it does not recall copies already fetched.
- Pin to
@v1for stability or to a commit SHA for reproducibility. Source and issues: share-markdown-action repository.
Prefer a plain script? The API docs carry a 12-line bash equivalent. For documents an assistant writes rather than CI, see the agent handoff walkthrough.
FAQ
How do I publish a markdown file from GitHub Actions as a link?
Add one step: uses: invisible-hand/share-markdown-action@v1 with file: path/to/report.md. The step outputs url (a rendered page), raw-url (the markdown as text/markdown), id, edit-token and expires-at. No account, secret or token is needed to create a share.
Why not just use the GitHub job summary?
The job summary lives inside the Actions run, so only people with repository access can read it, and it disappears with the run logs. A Docs MD link can be sent to anyone — a client, a Slack channel, a release thread — and lives for the expiry you choose, independent of log retention.
Can the action update the same link on every run?
Yes. Create the share once, store its id and edit token as repository secrets, and pass them as share-id and edit-token. The action then sends a PATCH instead of a POST and the URL stays the same.
Is the edit token printed in the workflow log?
No. The action registers it with ::add-mask:: before anything is printed, so GitHub replaces it with *** in logs. It is still available as the edit-token output for a later step to store.
What does the action need on the runner?
bash, curl and jq, all present on ubuntu-latest, macos-latest and windows-latest with Git Bash. It is a composite action with no Docker image and no Node dependencies, so it adds about a second to a job.
Who can read a published report?
Anyone with the link. Shares are public URLs; there is no access control beyond the unguessable id. Keep secrets, tokens, private hostnames and customer data out of the file. Expiry deletes the page but does not recall copies already fetched.