Skip to content

GitHub Action

github-actions[bot] edited this page Oct 19, 2026 · 28 revisions

GitHub Action

Generated from docs/action.md in the repository. Edit it there; changes made on the wiki are overwritten.

The Readmint Action renders your designs in your own repository, on a schedule or on demand, and commits the SVG (or PNG, WebP, APNG, GIF) files that changed. It uses the same loaders and renderer as readmint render (cli.md): for the same design, assets and data the Action writes byte for byte what the CLI prints.

Example workflow

name: Readmint

on:
  schedule:
    - cron: "17 3 * * *"
  workflow_dispatch:

permissions:
  contents: write

concurrency:
  group: readmint
  cancel-in-progress: false

jobs:
  render:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: MrLarkyy/Readmint@vX.Y.Z
        with:
          designs: |
            readmint/designs/card.yaml
            readmint/designs/languages.yaml

Use an exact release tag (vX.Y.Z); see Releases. The examples repository runs the profile templates as working workflows (components.md). Then put the printed snippet (below) into your README.

Inputs

input default meaning
designs (required unless manifest) design files, one per line, relative to the workspace; blank lines are ignored; 1 to 64; not together with manifest (an input error)
manifest none a render manifest instead of designs: a JSON file of named renders { name, design, props, variants }, a path relative to the workspace, checked like props-file (no .., no absolute or drive path, a link or junction leading outside refused); 1 to 64 renders, each written as <output-dir>/<name>.<ext> and, with variants: ["dark"], <name>.dark.<ext> (Render manifest)
output-dir readmint folder inside the workspace the output files go to; created when missing
formats svg output formats, comma or newline separated: svg, png, webp, apng, gif (see raster.md); each design is written once per format; the first one is what the snippet shows; png and apng together are an input error (both write .png)
scale 2 raster pixels per design pixel, 1..4 (up to three decimals), as the CLI --scale
frame-rate none frames per second of an animated raster output, a whole number in 1..60 (1..50 when gif is listed), as the CLI --frame-rate; none: 30, gif 25, lowered to fit the raster limits for a long animation or a file over 5 MiB (an info line in the log says so, raster.md)
quality none lossy WebP at this quality, a whole number in 1..100, as the CLI --quality; webp must be listed in formats; none: lossless
token ${{ github.token }} token for the GitHub data; not used with data-file; never printed and never given to git
login ${{ github.repository_owner }} the GitHub user whose data is loaded; needed with GITHUB_TOKEN, which belongs to no user
exclude-repositories none repositories (name or owner/name, separated by commas or lines) left out of the GitHub repository list, stars, forks, languages and pinned fallback (not the repository total, pushes or the calendar), as the CLI --exclude-repositories; not with data-file
language-commit-cap none own commits (whole number 0-2000, empty or 0 = off) read, one GitHub request each (about 0.3-0.6 s; with your own token private repositories add to the sums, language names and sums only), to fill github.languagesAuthored for the templates' languageShare: authored, as the CLI --language-commit-cap; not with data-file. See Languages from your own commits
cache-dir none folder outside the workspace (absolute, or .. relative to it) where the GitHub load keeps its validated answers and the commits read between runs, and the data-wakatime and data-registry loads their stats and entries, so a later run sends no request for them; see GitHub cache, WakaTime data and Registry data; with data-file only together with data-wakatime or data-registry; not the workspace or inside it
data-file none fixture data JSON (the CLI --data format) used instead of loading GitHub data: no token, no GitHub API (only images bound to data are fetched); a path relative to the workspace, checked like designs and data-json files (no .., no absolute or drive path, a link or junction leading outside refused), at most 8 MiB
data-json none JSON documents to bind, one per line as [<namespace>=]<source> (the CLI --data-json): a file inside the workspace or an https URL, each bound as { version: 0, document } under its namespace (default json), at most 8, added to the data-file or GitHub data (JSON data)
data-json-headers none headers for the data-json URLs, one per line as <namespace>:<Header-Name>=<ENV_NAME>: the value is read from the step's environment variable ENV_NAME, masked, never printed and sent only to that URL's origin
data-feed none RSS 2.0 or Atom 1.0 feeds to bind, one per line as [<namespace>=]<https URL> (the CLI --data-feed): each is bound as { version: 0, title, link, entries } under its namespace (default feed), at most 8, added after the data-json documents (Feed data)
data-feed-limits none entries kept per feed, one per line as <namespace>=<n> with n from 1 to 50 (the CLI --data-feed-limit); a feed without a line keeps 5
data-wakatime none WakaTime coding stats to bind, one per line as [<namespace>=]<ENV_NAME> (the CLI --data-wakatime): ENV_NAME names the step's environment variable that holds the API key, set from a secret with env:; masked, never printed and sent only to wakatime.com; each is bound as { version: 0, range, totalSeconds, humanReadableTotal, dailyAverageSeconds, languages, editors, operatingSystems } under its namespace (default wakatime), at most 4, added after the feeds (WakaTime data)
data-wakatime-ranges none range per WakaTime source, one per line as <namespace>=<range> with last_7_days, last_30_days, last_6_months, last_year or all_time (the CLI --data-wakatime-range); a source without a line takes last_7_days
data-registry none package registry entries to bind, one per line as [<namespace>=]npm:<name> or [<namespace>=]maven:<group>:<artifact> (the CLI --data-registry): no key; each namespace (default registry, at most 4, at most 20 npm and 20 Maven entries each) is bound as { version: 0, npm, maven } in the order given, null for an unknown entry, with a warning
props-file none settings JSON file (the CLI --props format: root component props by section) merged into every listed design and its dark twin or variant, so personal texts are set once per template; a path relative to the workspace, checked like data-file (no .., no absolute or drive path, a link or junction leading outside refused), at most 4 Mi characters of text, as --props; a section no listed design or render has is warned about, without its values
variant none renders each listed design (and each manifest render without variants) also as this named variant from the same file: dark sets its root prop theme to dark and writes <stem>.dark.<ext> (the CLI --variant); empty renders none. A <stem>.dark.<ext> design next to it wins (Outputs on disk and the dark variant)
locale none BCP 47 tag for the number, compact, percent and date filters, checked as the CLI --locale (en, de, fr, es, cs or a region tag of one); none: the design's locale, else en
commit-message Update Readmint graphics message of the commit; at most 4096 characters
branch the checked-out branch branch the commit is pushed to, without refs/; the checkout must be this branch at its latest commit on origin (see Pushing)
dry-run false true renders and writes the files but runs no git command

Booleans are YAML core booleans: true, True, TRUE, false, False, FALSE. A blank input takes its default. Every problem with the inputs is reported at once, before anything is rendered.

Outputs

output value
changed true when an output file differs from the last commit (in a dry run: when a file in the workspace changed)
commit SHA of the pushed commit; empty when nothing was committed and in a dry run
files the changed output files, one per line, relative to the workspace
snippet the <picture> HTML for every design, also printed in the log group README snippet

Outputs on disk and the dark variant

A design card.yaml is written to <output-dir>/card.svg: the file name without its last extension, plus the extension of each format (.svg, .png, .webp, .gif; apng writes .png). Two designs that would write the same file (also when only the letter case differs) are an input error.

A design file card.dark.yaml next to card.yaml (same folder, same extension) is its dark variant: it is rendered too, to <output-dir>/card.dark.svg, and the snippet switches to it when the reader's system is in dark mode. It is a design like any other; nothing in the design format or the renderer knows about light or dark. You do not need to list it; if you do, list its light design too, else it is an input error.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="readmint/card.dark.svg">
  <img alt="card" src="readmint/card.svg">
</picture>

Paths in the snippet are relative to the repository root (where the README is), each segment percent-encoded. The snippet shows the first listed format; when that is a raster format, the <img> carries the design's width and height in CSS pixels, so an image exported at scale 2 shows sharp at the design's size:

<img alt="card" src="readmint/card.png" width="420" height="120">

The variant input makes the same pairing without a second file: for every listed design that has no dark twin, <output-dir>/<stem>.dark.<ext> is rendered from the design itself with its root prop theme set to dark (the variant is applied to the design first, then the props-file section, so the result is the bytes of the twin that would exist with the same settings), and the snippet is the one of a design with a twin. A real card.dark.<ext> next to the design, listed or not, wins: it is rendered as always and the log says "card.dark.json" next to "card.json" wins over the dark variant. Every listed design without a twin must have a top-level component that declares the prop theme, else the run fails and nothing is written. The lines are those of the CLI Variants, the design name followed by (dark variant): card.json (dark variant): error unknown-property at /theme: variant dark: no top-level component of the design has the prop "theme", likewise error enum or error type when the prop does not accept dark, and error unknown-property at /: variant dark: the design has no top-level component layer. Without the input nothing changes: no extra file, no log line, the same snippet.

Outputs of designs you remove from the list are not deleted.

Render manifest

With the manifest input the outputs are listed explicitly instead of found from a list of design files: a JSON file of named renders, each a design with the root props to set on it. One design can serve many outputs, such as the six repository cards of a profile template, which differ only in the prop slot:

- uses: MrLarkyy/Readmint@vX.Y.Z
  with:
    manifest: readmint/designs/horizon/renders.json
    props-file: readmint/designs/horizon/settings.json

The format, the order (design, then the entry's props, then the variant, then the props-file section named like the render), the limits, the JSON Schema and the error lines are those of the CLI Render manifest; the Action loads it with the same code, so readmint render --manifest writes the bytes the Action writes. What differs is the workspace: the manifest and its designs are read only inside it, and the manifest's folder is the folder its designs lie in.

  • A render named repo-3 is written to <output-dir>/repo-3.<ext> for each format, and <output-dir>/repo-3.dark.<ext> for its variants: ["dark"]; a render without variants takes the variant input (refused with the CLI's duplicate-key line when the render's props set the prop the variant sets, before any data is loaded), [] renders none. No <name>.dark.<ext> file is looked up; the dark output is the entry's own design with the variant.
  • Two renders can never write one file (the names are unique ignoring letter case), and an output that two renders would write is an input error all the same.
  • The snippet is built from the names, one <picture> per render in the order of the manifest: the <img> has the render name as its alt text and the <source> the dark output.
  • The props-file section of a render is its name ("repo-3"); a section no render names is warned about by name. A section that sets a prop the render's props set wins over it, and the log names the section and the prop.
  • designs and manifest exclude each other: setting both is inputs designs and manifest are exclusive; set one; setting neither is input designs lists no design file, as before.

What a run does

  1. Reads and checks the inputs, then pairs the designs (or, with manifest, the renders of the loaded manifest) with their outputs and loads the props-file, so a bad settings file or a section no design has is reported before any git check or GitHub request. Then it reads every listed design (and dark twin) and checks its let names, components included, against the data namespaces of the run (github unless data-file is set, and every data-json, data-feed, data-wakatime and data-registry namespace): a clash fails the run as a data error, one line per clash naming the let entry, before any git check and before any request. A design that cannot be read is skipped here and reported by the render step.
  2. Unless it is a dry run: finds the branch to push to, checks its name and that the checkout is that branch's latest commit on origin, before anything is written.
  3. Loads the data once: the data-file, else the GitHub data of login, bound as github (see data-github.md). The load is given 5 minutes. Then each data-json source in the listed order, added under its namespace (JSON data), then each data-feed feed (Feed data), then each data-wakatime source (WakaTime data), then each data-registry namespace (Registry data).
  4. Loads and renders every design and dark variant (a twin file, or the variant input's render of the design), in every listed format (a raster export samples an animated design at frame-rate and stays within the limits of raster.md). Design files and their assets are read only inside the workspace, also through links (asset-loading.md). If any design fails, nothing is written and the run fails: the output set is never half updated.
  5. Writes each output whose bytes differ from the file already there (a file with the same bytes is not touched). The output folder must resolve inside the workspace and an existing output must be a regular file, not a link or a folder; this is checked before the first write.
  6. Prints the snippet.
  7. Unless it is a dry run: stages the output files and, when any differs from the last commit, commits only those files (anything else you staged in an earlier step stays staged and out of the commit) as github-actions[bot] and pushes the commit to origin. A second run over unchanged designs and data makes no commit.

Renderer warnings are printed as ::warning:: lines and do not fail the run.

Settings file

props-file names a JSON file, a path relative to the workspace and confined to it like data-file (no .., no absolute or drive path, a link or junction leading outside refused, at most 4 Mi characters of text, as --props), { "<section>": { "<prop>": value } }, read once before any git command or GitHub request. The section of a design is its file name without the extension and without .dark, so a light design and its dark twin share the entry (with a manifest it is the name of the render, light and dark alike), and the output is the same as the CLI readmint render <design> --props <file> gives (cli.md). A section no listed design has is a ::warning:: line naming the section only; a file that cannot be read or is invalid, or a prop a design does not declare or a wrongly typed value, fails the run before anything is written, with the section and prop in the message, never the value; a section that sets a prop the variant sets (theme) wins over the variant and is warned about, naming the section and the prop; a render error at a set prop, or inside its component with the value in the message, keeps its path but its message names only the section and props.

Data and the token

By default the Action loads the data of the repository owner with the workflow's GITHUB_TOKEN. That token belongs to no user, so login is needed (its default is the repository owner) and the data is what the token may see. With a personal access token in a secret (token: ${{ secrets.READMINT_TOKEN }}), the data can include contribution counts of private repositories; in a public repository they are then published in the SVG. See data-github.md before you use a personal token. The owner must be a user; an organisation login fails to load.

The profile status, the pinned repositories, the recent pushes and the weekly commits (github.status, github.pinned, github.pushes) come from two extra requests. A token that may not read them leaves those fields empty and the templates show their plain fallback lines; the run goes on and the log gets one ::warning:: line naming the missing parts (never the token). Private repositories never appear in them, with any token (data-github.md).

The token is used for the GitHub data only. It is never printed: log lines, errors and step outputs are redacted (also its base64 forms), and the GitHub loader never puts it into an error. It is never given to git, neither as an argument nor in git's environment, and never sent to an image host.

A design whose image src is bound to data ("{{ github.avatarUrl }}") has that image fetched before the render, in both data modes, with its own request: no token, cookie or other credential, no redirect, https on an allowed host only (avatars.githubusercontent.com), each URL once per run (so a dark twin shares the fetch), at most 32. The bytes are inlined in the SVG. The rules and error codes: asset-loading.md.

GitHub cache

With cache-dir the GitHub load keeps its validated answers in that folder (created with mode 0o700 when missing): a re-run within an hour reuses the whole load and sends only the viewer request, and every commit read by language-commit-cap is kept for 7 days, so a later run sends no request for it. A runner starts empty, so restore the folder with actions/cache:

- uses: actions/cache@v4
  with:
    path: ${{ runner.temp }}/readmint-cache
    key: readmint-${{ github.run_id }}
    restore-keys: readmint-
- uses: MrLarkyy/Readmint@vX.Y.Z
  with:
    designs: readmint/card.json
    language-commit-cap: 200
    cache-dir: ${{ runner.temp }}/readmint-cache

What it holds: the validated public-profile answers (profile, public repositories, public pins and pushes, weekly commits) and the per-commit language sums, plus what the token's own contribution counts and commit sums reveal (private contribution counts, private repository language sums); no private repository name, pin, commit headline or organisation-only status, and never the token. A hit still sends one GraphQL request, the viewer lookup that names the token's owner. An Actions cache can be restored by the repository's workflows, and pull-request runs restore the caches of the base branch, so use a personal token only where that is acceptable. Entries are validated again on every read; a bad one is a miss. The folder must lie outside the workspace, so it is never staged, committed or published by a later step: use a path under runner.temp (an absolute path, or .. relative to the workspace; ~ is not expanded). The cache is best effort: a failed write is ignored, while a path that exists and is not a folder fails the run before any request. Expired entries are not pruned.

JSON data

data-json binds any JSON document, a file in the repository or a public https URL, with the JSON source of data-sources.md, the load readmint --data-json uses:

- uses: MrLarkyy/Readmint@vX.Y.Z
  env:
    STATS_API_KEY: ${{ secrets.STATS_API_KEY }}
  with:
    designs: readmint/designs/card.yaml
    data-json: |
      counts=readmint/counts.json
      api=https://api.example.com/v1/stats
    data-json-headers: |
      api:X-Api-Key=STATS_API_KEY
  • Each document is bound as { version: 0, document: <the parsed JSON> } under its namespace: bind counts.document.stars. Without <namespace>= the namespace is json.
  • A file source is a path relative to the workspace, checked like designs (no .., no absolute or drive path) and then by real path: a link or junction that leads outside the workspace is refused. At most 1 MiB.
  • A URL must be https://; at most 3 redirects (https only), a JSON or text/plain content type in UTF-8, 1 MiB, 10 s.
  • A header value comes only from the step's environment, by the variable name in data-json-headers (set it from a secret with env:, never in with:). The Action registers each value with ::add-mask:: before any load, redacts it from every line it prints, and sends it only to the origin of its URL (a redirect to another origin drops it).
  • The JSON namespaces are added to the data-file data or to github: github cannot be a JSON namespace unless data-file is given, and a data-file key equal to a JSON namespace fails the run, naming it.
  • The documents are loaded on every run (no cache); the same documents give the same bytes.

Feed data

data-feed binds the latest entries of an RSS 2.0 or Atom 1.0 feed, a public https URL, with the feed source of data-sources.md, the load readmint --data-feed uses:

- uses: MrLarkyy/Readmint@vX.Y.Z
  with:
    designs: readmint/designs/card.yaml
    data-feed: |
      blog=https://blog.example/feed.xml
    data-feed-limits: |
      blog=3
  • Each feed is bound as { version: 0, title, link, entries } under its namespace, entries as plain text { title, link, published, summary } newest first: bind blog.entries[0].title. Without <namespace>= the namespace is feed; a limit is 1 to 50 and a feed without one keeps 5.
  • A URL must be https://; at most 3 redirects (https only), an XML or text/plain content type, 1 MiB, 10 s. No credentials are sent. A URL that holds a key must come from a secret (blog=${{ secrets.BLOG_FEED_URL }}), as a header value does: the runner prints with: values and masks only secrets. The Action itself never prints a feed URL, and errors name the namespace only.
  • The feed namespaces are added to the data-file data or to github, as the JSON ones: github cannot be a feed namespace unless data-file is given, a data-file key equal to a feed namespace fails the run, naming it, and a namespace cannot be both a data-json and a data-feed namespace.
  • The feeds are loaded on every run (no cache).

WakaTime data

data-wakatime binds the coding stats of your WakaTime account (time spent coding, its biggest languages, editors and operating systems) with the WakaTime source of data-sources.md, the load readmint --data-wakatime uses. Keep the API key in a repository secret, pass it to the step with env:, and name the variable in the input, never the key:

- uses: MrLarkyy/Readmint@vX.Y.Z
  env:
    WAKATIME_API_KEY: ${{ secrets.WAKATIME_API_KEY }}
  with:
    designs: readmint/coding-time-card.design.json
    data-wakatime: WAKATIME_API_KEY
    data-wakatime-ranges: |
      wakatime=last_30_days
  • Each line is [<namespace>=]<ENV_NAME>; without <namespace>= the namespace is wakatime, at most 4 sources. A value shaped like a key (waka_..., a UUID) is an input error that does not echo it. The variable's value is read only for that load; unset or empty, the run fails naming the variable, before any request.
  • Before any load the Action masks every form of each key (as set, trimmed, its base64 and the Authorization value sent), so neither its log nor a later step's prints it. The key goes only to https://wakatime.com/api/v1/users/current/stats/<range>, in the Authorization header; no redirect is followed.
  • Each source is bound as { version: 0, range, totalSeconds, humanReadableTotal, dailyAverageSeconds, languages, editors, operatingSystems } under its namespace, each list of { name, percent, seconds }, most time first, at most 20. The coding-time card template renders it (components.md).
  • When WakaTime is still calculating (HTTP 202, or stats not up to date), the data it has, or the empty object, is bound, the log gets one ::warning:: line naming the namespace, and the run succeeds; that answer is not cached.
  • The WakaTime namespaces are added to the data-file data or to github, after the feeds: github cannot be a WakaTime namespace unless data-file is given, a data-file key equal to one (or to a data-json or data-feed namespace) fails the run naming it before any request, and a namespace cannot also be a data-json or data-feed namespace.
  • With cache-dir each complete answer (the normalised data only, never the key; the cache key holds a digest of the key) is kept for an hour, so a re-run sends no request; cache-dir then also goes with data-file.
  • A dry run with data-file and data-wakatime needs no GitHub token; it still needs the key.

Registry data

data-registry binds the latest versions of npm packages and Maven Central artifacts with the registry source of data-sources.md, the load readmint --data-registry uses. No key or token is involved:

- uses: MrLarkyy/Readmint@vX.Y.Z
  with:
    designs: readmint/libraries-card.design.json
    data-registry: |
      npm:react
      npm:@types/node
      maven:org.jetbrains.kotlin:kotlin-stdlib
  • Each line is [<namespace>=]npm:<name> or [<namespace>=]maven:<group>:<artifact>; without <namespace>= the namespace is registry. Entries of one namespace form one source, at most 20 npm and 20 Maven entries each and 4 namespaces. An invalid name is an input error listed with the others, naming the entry by position, never its value.
  • Each namespace is bound as { version: 0, npm, maven }: npm entries { name, version, weeklyDownloads }, maven entries { group, artifact, latestVersion, updated }, in the order given. The libraries card template renders it (components.md).
  • Only registry.npmjs.org, api.npmjs.org and repo1.maven.org are contacted, without credentials; no redirect is followed.
  • A package or artifact the registry does not know is null in its list, the log gets one ::warning:: line naming the namespace and the first entries, and the run succeeds; that entry is not cached. Any other failure (rate limit, server error, bad answer, 30 s timeout) fails the run as Registry data <namespace> (<code>): <message>, so a half-loaded card is never committed.
  • The registry namespaces are added to the data-file data or to github, after the WakaTime sources: github cannot be a registry namespace unless data-file is given, a data-file key equal to one (or to a data-json, data-feed or data-wakatime namespace) fails the run naming it before any request, and a namespace cannot also be a data-json, data-feed or data-wakatime namespace.
  • With cache-dir each found entry is kept for an hour, so a re-run sends no request for it; cache-dir then also goes with data-file.
  • A dry run with data-file and data-registry needs no GitHub token and no key.

Pushing

The push uses the credentials actions/checkout stores in the repository, so:

  • keep persist-credentials of the checkout on (its default) and give the job permissions: contents: write;
  • check the repository out into the workspace root (no path:), since git works there;
  • git must be on PATH (every GitHub-hosted runner has it). The Action starts it by the absolute path it finds there, skipping relative or empty PATH entries and any directory inside the workspace, and git, with the programs it starts (remote helpers, ssh, filters, gpg), gets a PATH without those entries, so a git, git.exe or other program committed to the repository never runs. The process is started from the directory it was found in, with the workspace given by -C;
  • check out the branch the Action pushes to (the default of actions/checkout on a schedule or workflow_dispatch run): the commit is pushed with the checkout's history, so the Action refuses to run, before writing anything, when the checkout is not the latest commit of that branch on origin (a detached HEAD, a pull request merge, another branch, a branch missing on origin or one that moved since the checkout). It never pushes commits nobody merged into the branch;
  • the push goes to origin; a branch that moves between that check and the push rejects the push and the run fails: nothing is rebased or forced. A concurrency group keeps two runs from racing;
  • a push made with GITHUB_TOKEN starts no other workflow, so the commit does not trigger your other workflows.

Windows trust boundary

The Action starts git outside the workspace, but git then works inside it, and so do the programs it starts. Windows searches the current directory of a program for a DLL after the program directory and the system directories, so a DLL committed to the repository is loaded only under a name neither of those holds. Git for Windows (checked with 2.51) loads every DLL it ships from its own directories and its optional system DLLs from System32 only. A git helper, filter or credential helper you install on a Windows runner is inside that boundary: install it with its DLLs beside it. Linux and macOS have no current-directory library search, but LD_LIBRARY_PATH, DYLD_LIBRARY_PATH and DYLD_FALLBACK_LIBRARY_PATH entries that are empty or relative resolve against it, so the Action removes those and the entries inside the workspace from them (splitting LD_LIBRARY_PATH on ; as well as :, as glibc does); other loader inputs (such as the rpath of a helper you install) are your trust boundary.

On Windows runners git may convert line ends; a .gitattributes line such as readmint/*.svg -text keeps the SVG bytes as written. The Action compares with the last commit after staging, so a line-end conversion alone never makes a commit.

Dry run

With dry-run: true the Action renders and writes the changed files into the workspace, prints dry run: would commit <n> file(s) and sets the outputs (commit empty), but runs no git command at all: nothing is staged, committed or pushed, and the workspace need not be a git repository. Use it to inspect the result, for example with actions/upload-artifact. With data-file it needs no token either, also with data-wakatime (whose key it still reads and masks) and data-registry (which needs none).

Errors

A failed run exits with 1 and prints one ::error:: line per problem, each naming what it is about:

  • inputs: a missing or invalid input (all at once; a manifest that is not valid or lists a design that does not exist, as manifest "<file>": error <code> at <pointer>: <message> lines, never a prop value; a variant other than empty or dark is refused without being echoed);
  • data: the fixture file's problem, cache directory "<path>" is not a folder, or GitHub data (<code>): <message> with the code of data-github.md, or JSON data <namespace> (<code>): <message> with a common code of data-sources.md, Feed data <namespace> (<code>): <message> with a common code of data-sources.md, WakaTime data <namespace> (<code>): <message> with a code of data-sources.md, Registry data <namespace> (<code>): <message> with a common code of data-sources.md, a WakaTime source whose environment variable is not set (naming the variable, never a key), or a namespace both the data-file and data-json, data-feed or data-wakatime give, or a data-file key equal to a data-json, data-feed or data-wakatime namespace;
  • render: the design's issues (a render of the variant input is named <design> (dark variant), a manifest render <design> (render "<name>"), with , dark variant inside the brackets for its variant), as readmint render prints them (an image bound to data that cannot be fetched as <design>: error image-<reason> at /assets/images/<id>/src: <message>), or a raster export over a limit (the measured value, the limit and a fix);
  • output: an output folder outside the workspace, an output that is not a regular file, a file that cannot be written;
  • git: the git command that failed and the last 20 lines of what it printed.

Every line is escaped: a design or data value cannot end the line, start a workflow command or colour the log. Anything unexpected prints only internal error (<name>).

Releases

The bundle (packages/action/dist/action.js, with the raster WebAssembly modules in packages/action/dist/wasm/) is not in the main branch. The Release Action workflow (workflow_dispatch with a version such as v0.1.0) builds it on main, commits it on a detached commit and pushes a new tag that points there. It never moves or overwrites a tag, so a tag always runs the same code. The Action is version 0: an added input or output is compatible, a renamed or removed one is a new major version.

Once the tag is pushed, the last step creates the GitHub Release for it with the notes GitHub generates (gh release create "$VERSION" --verify-tag --generate-notes --latest --title "$VERSION"); the workflow token with contents: write is enough, there is no further secret. A run for a tag that exists stops at the tag check before anything is committed, tagged or published, so neither a tag nor a Release is moved or overwritten. If the Release step fails after the tag was pushed, the run is red and the tag stays: create the Release by hand with the same command and --repo MrLarkyy/Readmint rather than running the workflow again.

After a release, bump defaultActionRef (packages/examples/src/defaultActionRef.ts) to the new tag, the literal in components ("by default") and the pin in packages/examples/test/buildExamples.test.ts, add any filter the release introduced to filterIntroducedIn (packages/examples/src/assertActionSupportsDesigns.ts; a layer property goes into propertyIntroducedIn, a key of the document into documentKeyIntroducedIn there, with the change that adds it to the format), check that every filterIntroducedIn, propertyIntroducedIn and documentKeyIntroducedIn version names a tag actually cut (maxLines, let, orbit and the root palette: v0.4.0; the filters exclude and sum: v0.5.0; correct it if that release got another number), run pnpm examples <clone> and push the clone: the generated example workflows pin that tag.

Clone this wiki locally