Repository navigation
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.
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.yamlUse 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.
| 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.
| 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
|
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.
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.jsonThe 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-3is written to<output-dir>/repo-3.<ext>for each format, and<output-dir>/repo-3.dark.<ext>for itsvariants: ["dark"]; a render withoutvariantstakes thevariantinput (refused with the CLI'sduplicate-keyline when the render'spropsset 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-filesection 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'spropsset wins over it, and the log names the section and the prop. -
designsandmanifestexclude each other: setting both isinputs designs and manifest are exclusive; set one; setting neither isinput designs lists no design file, as before.
- Reads and checks the inputs, then pairs the designs (or, with
manifest, the renders of the loaded manifest) with their outputs and loads theprops-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 itsletnames, components included, against the data namespaces of the run (githubunlessdata-fileis set, and everydata-json,data-feed,data-wakatimeanddata-registrynamespace): a clash fails the run as a data error, one line per clash naming theletentry, before any git check and before any request. A design that cannot be read is skipped here and reported by the render step. - 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. - Loads the data once: the
data-file, else the GitHub data oflogin, bound asgithub(see data-github.md). The load is given 5 minutes. Then eachdata-jsonsource in the listed order, added under its namespace (JSON data), then eachdata-feedfeed (Feed data), then eachdata-wakatimesource (WakaTime data), then eachdata-registrynamespace (Registry data). - Loads and renders every design and dark variant (a twin file, or the
variantinput's render of the design), in every listed format (a raster export samples an animated design atframe-rateand 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. - 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.
- Prints the snippet.
- 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 toorigin. A second run over unchanged designs and data makes no commit.
Renderer warnings are printed as ::warning:: lines and do not fail the run.
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.
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.
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-cacheWhat 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.
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: bindcounts.document.stars. Without<namespace>=the namespace isjson. - 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 ortext/plaincontent 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 withenv:, never inwith:). 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-filedata or togithub:githubcannot be a JSON namespace unlessdata-fileis given, and adata-filekey 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.
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: bindblog.entries[0].title. Without<namespace>=the namespace isfeed; 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 ortext/plaincontent 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 printswith: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-filedata or togithub, as the JSON ones:githubcannot be a feed namespace unlessdata-fileis given, adata-filekey equal to a feed namespace fails the run, naming it, and a namespace cannot be both adata-jsonand adata-feednamespace. - The feeds are loaded on every run (no cache).
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 iswakatime, 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
Authorizationvalue sent), so neither its log nor a later step's prints it. The key goes only tohttps://wakatime.com/api/v1/users/current/stats/<range>, in theAuthorizationheader; 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-filedata or togithub, after the feeds:githubcannot be a WakaTime namespace unlessdata-fileis given, adata-filekey equal to one (or to adata-jsonordata-feednamespace) fails the run naming it before any request, and a namespace cannot also be adata-jsonordata-feednamespace. - With
cache-direach 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-dirthen also goes withdata-file. - A dry run with
data-fileanddata-wakatimeneeds no GitHub token; it still needs the key.
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 isregistry. 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 }:npmentries{ name, version, weeklyDownloads },mavenentries{ group, artifact, latestVersion, updated }, in the order given. The libraries card template renders it (components.md). - Only
registry.npmjs.org,api.npmjs.organdrepo1.maven.orgare contacted, without credentials; no redirect is followed. - A package or artifact the registry does not know is
nullin 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 asRegistry data <namespace> (<code>): <message>, so a half-loaded card is never committed. - The registry namespaces are added to the
data-filedata or togithub, after the WakaTime sources:githubcannot be a registry namespace unlessdata-fileis given, adata-filekey equal to one (or to adata-json,data-feedordata-wakatimenamespace) fails the run naming it before any request, and a namespace cannot also be adata-json,data-feedordata-wakatimenamespace. - With
cache-direach found entry is kept for an hour, so a re-run sends no request for it;cache-dirthen also goes withdata-file. - A dry run with
data-fileanddata-registryneeds no GitHub token and no key.
The push uses the credentials actions/checkout stores in the repository, so:
- keep
persist-credentialsof the checkout on (its default) and give the jobpermissions: 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 emptyPATHentries and any directory inside the workspace, and git, with the programs it starts (remote helpers, ssh, filters, gpg), gets aPATHwithout those entries, so agit,git.exeor 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/checkouton a schedule orworkflow_dispatchrun): 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 onorigin(a detached HEAD, a pull request merge, another branch, a branch missing onoriginor 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. Aconcurrencygroup keeps two runs from racing; - a push made with
GITHUB_TOKENstarts no other workflow, so the commit does not trigger your other workflows.
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.
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).
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; avariantother than empty ordarkis refused without being echoed); - data: the fixture file's problem,
cache directory "<path>" is not a folder, orGitHub data (<code>): <message>with the code of data-github.md, orJSON 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 thedata-fileanddata-json,data-feedordata-wakatimegive, or adata-filekey equal to adata-json,data-feedordata-wakatimenamespace; - render: the design's issues (a render of the
variantinput is named<design> (dark variant), a manifest render<design> (render "<name>"), with, dark variantinside the brackets for its variant), asreadmint renderprints 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>).
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.
Home · Getting started · Examples · Source
See it first
Start here
Guides
Reference
Contributors