Preview Markdown in pull requests with a GitHub Action

A reviewer should read the document a pull request changes, not a diff of asterisks. This action publishes each changed Markdown file as a rendered page and comments the links on the pull request.

Read the setup in the documentation

What it does

Given no list of files, the action asks git which Markdown files the pull request touched, publishes each one as a read-only page, and comments a table of file names, word counts and links. Files the branch deleted are skipped, so removing a document does not fail the run.

It is a composite action, `action.yml` at the root of the public repository, and what it runs is the command-line client from the same repository: no container to pull, no dependencies to install, and the same rendering the site and the API give.

The comment is headed Markdown preview. A reviewer opens each link in a browser like any other page, with no app to install and, for a link, no account to make — which is what lets somebody who never opens the diff review the words.

Why a rendered page

A diff is the right view for code and the wrong one for prose. It shows which words changed and hides whether the section still reads, whether a table lines up, or whether a numbered list restarts halfway down. The rendered page answers those at a glance, and a reviewer who does not write Markdown can read it at all.

The workflow

A complete workflow to copy is in `examples/publish-markdown.yml`. It runs on `pull_request` with a `paths` filter for `**.md`, so a pull request that changes no Markdown starts no runner. The checkout needs `fetch-depth: 0`, because the changed files are worked out against the pull request’s base commit, and the job needs `pull-requests: write` to post the comment.

The step itself is `uses: raudarlabs/transformpipe@main` with one input, `api-key: ${{ secrets.TP_API_KEY }}`. The key is a TransformPipe API key kept as a repository secret, and the action passes it to the client in an environment variable rather than as an argument, so it is not part of the command written to the log.

Inputs and outputs

`files` takes space-separated paths in place of the changed-files list, which is how the action publishes from a push to a branch rather than a pull request. `share` is `link` by default, `people` for named readers only, or `none` to keep the documents private. `merge: true` chains the files into one document, named by `name`.

`replaces` records a single document as a new version of an earlier one, `comment: false` skips the comment, and `host` points at another deployment.

Two outputs come back for the steps after it: `urls`, the published links one per line, and `documents`, the API’s answer for each document, as JSON. With nothing to publish the action says so, leaves `urls` empty and `documents` as an empty list, and exits successfully, so a later step can be guarded on `urls` rather than failing.

Links that stay true

Every run publishes new documents rather than overwriting earlier ones, so the link in an older comment keeps showing what that commit said. The cost is a document per file per run in the account, which holds 5,000 documents and 250 MB; old previews can be deleted from the history.

Publishing a public link needs a confirmed email address on the account, and until it is confirmed the step fails with that sentence. A pull request from a fork runs without the repository’s secrets, so the step has no key there; skip it with `if: github.event.pull_request.head.repo.fork == false`.