Release Workflow
task-plus automates the release process for Go projects. The workflow runs through five phases: Precheck, Gather, Ask, Check, and Execute. Every run ends with === Timings ===: the elapsed time of each phase and of each publishing step within Execute (Execute: goreleaser, Execute: deploy docs, …), then the total, so the slow steps are measured before they are tuned (#76).
Where the release stands
maturity.code picks the process: 0 trunk, 1 and up a batch
(ADR 0006).
tp release query reports the level, the one next step with who takes
it, and what the checkout is not ready for (#108). An agent takes the
steps that publish nothing; a human merges and runs anything that tags.
| Level | Checked out | State | Next step | Who |
|---|---|---|---|---|
| trunk | main |
commits since the last tag | tp release on main: commit, tag, push |
human |
| trunk | a story branch | — | merge it into main and tp release there, or tp release --merge |
human |
| batch | story/<n>-<slug> |
no changes/<n>.<kind>.md and no release commits |
add the fragment | agent |
| batch | story/<n>-<slug> |
no release on the remote; not pushed, or no pull request |
tp release on the branch: makes the release commits, pushes it, opens its pull request into main (a lone release, story 030) |
agent |
| batch | story/<n>-<slug> |
no release; another story's pull request into main open |
tp release on the branch: converts that lone release into a batch and joins it |
agent |
| batch | story/<n>-<slug> |
behind its remote copy (a conversion's revert) | tp release on the branch: fast-forwards it and walks on |
agent |
| batch | story/<n>-<slug> |
release on the remote; not pushed, or no pull request into release |
tp release on the branch: pushes it, opens the PR |
agent |
| batch | story/<n>-<slug> |
its PR open, into main or release |
review and merge it | human |
| batch | story/<n>-<slug> |
merged into release |
tp release on main |
human |
| batch | main |
no release; a lone release's pull request open |
merge it, then tp release on main |
human |
| batch | main |
no release; two or more story branches ready |
tp release batch, then tp release on each story branch |
agent |
| batch | main |
.tp-release.yml lists stories |
tp release: merges them into release, checking each, makes the release commit, opens the release PR |
human |
| batch | main |
stories in release, nothing listed |
tp release: the batch is those stories; makes the release commit, opens the release PR |
human |
| batch | main |
PRs into release open, nothing listed |
merge or close them, or list the batch in .tp-release.yml; then tp release |
human |
| batch | main |
release PR open | merge it, then tp release on main (a run still waiting tags it itself) |
human |
| batch | main |
merged into main, untagged |
tp release: tags and publishes |
human |
Not ready, as query lists it:
- any level: a dirty tree;
- trunk: a base branch the forge protects;
- batch: a hand-edited
[Unreleased], a.tp-release-comment, a story branch not namedstory/<n>-<slug>or without its fragment (or, for a lone release, its release commits), commits not pushed.
The sections below give each phase and process in detail.
Flowchart
flowchart TD
A[Start] --> B[Precheck]
B --> C
subgraph Gather [Phase 2: Gather]
C[Refuse a dirty tree; read tags, fork info]
end
C --> D
subgraph Ask [Phase 3: Ask]
F[Show the plan with every default]
F --> F2{Proceed? Y/n/e}
F2 -->|e| F3[Walk version, comment, push, goreleaser, install, deploy]
F3 --> F
F2 -->|n| F4[Nothing done]
end
F2 -->|Y| G
subgraph Check [Phase 4: Check]
G[Run fmt, vet, test]
end
G --> H
subgraph Execute [Phase 5: Execute]
H[Update CHANGELOG + commit] --> I[Git Tag]
I --> J{WASM?}
J -->|Yes| K[Build WASM + Re-tag]
J -->|No| L[Git Push]
K --> L
L --> M{Binary?}
M -->|Yes| N[Goreleaser]
M -->|No| O[Cleanup]
N --> O
O --> P{Fork?}
P -->|Yes| Q[Skip Install]
P -->|No| R[Install]
Q --> T{Deploy Docs?}
R --> T
T -->|Yes| U[Build + Deploy Docs]
T -->|No| V{post_release?}
U --> V
V -->|Yes| W[Run post_release commands]
V -->|No| S[Done]
W --> S
end
Phases
1. Precheck
Runs any configured precheck commands (e.g. task precheck). Fails fast before user interaction.
2. Gather
Read-only state probing:
- Working tree: any change stops the run with "working tree is dirty;
commit, stash or ignore before releasing" and the
git status, in every mode and in--dry-run(story 013, #35). The release commits only what its own steps change..tp-release-commentdoes not count. - Existing tags and retracted versions
- Suggested next version (latest tag bumped per
release_bump: major, minor or patch; minor by default; skips retracted versions) - Fork detection (go.mod vs git remote)
- Goreleaser config detection
- Forge detection (GitHub/GitLab/Forgejo) and release cleanup candidates
- Release process: trunk or batch, by
maturity.code(see below). In a batch the release remote's API token must be present (#70): a missing one is an error here, naming the env var(s) consulted, and a token the forge rejects fails on the pull request listing, both before anything is committed or pushed.tp checkreports amaturity.codeof 1 or more with notoken_envon the release remote (#69). - In a batch,
.tp-release.ymland.tp-release-inflightmust be gitignored (#133): the run writes them mid-release, and a commit on the base branch would otherwise carry them. A checkout that does not ignore them is an error naming the files, before anything is written; any ignore rule will do,.git/info/excludeincluded, so the fix can wait for the story that carries it. A base branch found diverged from the remote only by commits adding those files is named as such, with thegit reset --keeprecipe that drops the commits and keeps the files untracked, instead of the generic divergence error.
3. Ask
Plan first (story 020): every default is decided, the plan is printed, and
one question is asked — Proceed? [Y/n/e]. Y (or Enter) runs the plan
as shown; n ends the run with nothing done; e walks the questions one
by one, each defaulting to its value in the plan, then shows the plan and
asks again. --yes takes the plan without the prompt. The plan lists each
publishing step that applies to the project as yes or no; steps fixed
by config (install:, pypi: true) are shown and not asked.
The questions of the walk, and their defaults:
- Version (suggested or custom)
- Release comment. A batch takes it from the fragments. On trunk the default is
--comment, else the summary bullet already opening[Unreleased]in the CHANGELOG, else.tp-release-comment(saved bytp wtfrom the merged branch's last commit). That file's first line names the branch it was written for (branch: story/75-x); a run on another branch, or a file with no branch line, ignores it and says so (#82). The file is removed only once the Execute phase has completed on its branch; an abort, a failed check, a rollback or--dry-runleave it for the next run. The comment becomes the- summarybullet that opens the version's CHANGELOG section; a bullet already there is replaced, not joined (#30). - Roadmap items whose stories have all shipped: count each done (story 017)
- Push, goreleaser, PyPI, cleanup, install, deploy — each where it applies
4. Check
In a repo of more than one go.mod, first runs gofmt -l over the whole
tree (not vendor/, testdata/ or hidden directories) and stops on any
file it lists, since go fmt ./... does not reach a nested module (#107).
Then runs configured check commands (default: task check which runs fmt, vet,
test) on the tree that will be tagged, once per tree (#78): after the
release commit, with TP_VERSION set to the version, so a Taskfile can
build what it ships as VERSION: '{{.TP_VERSION}}'. On trunk that is
between the release commit and the tag; in a batch, on release after the
last story's merge and the release commit, before the push (earlier
stories are checked after each merge). The batch run records the tree in
.tp-release-inflight, and the tag run, after fast-forwarding the base
branch, skips the checks when the merged tree is that tree; a base that
moved is checked again. A failure rolls back to before the release
commit. Then it validates what the post-push steps will need, so a
release never fails after the tag is public:
- Deploy targets reachable and the docs build passing; the deploy reuses that build when the tree is unchanged, rather than building again.
- Goreleaser (story 010):
.goreleaser.yamlpublishes to the release remote — same forge,release.<forge>.owner/namematching the remote (or absent when the release remote isorigin, which goreleaser infers from),gitea_urls.api/downloadon the remote's host for a Forgejo — and the token goreleaser will read is set (GITHUB_TOKEN,GITLAB_TOKEN, orGITEA_TOKEN/the remote'stoken_envfor a Forgejo). Any finding aborts the run with the block to paste; the token message says whensecrets_provideris not configured.tp checkreports the same findings in its Goreleaser section.--dry-runruns the validation too.
5. Execute
All mutations in order:
- (Nothing to commit: the tree was clean in Gather; the checks of phase 4 run after step 4, before the tag)
- Tag existence check
- Version update task (if configured)
- Roadmap items done and stories shipping (story 017, below), then CHANGELOG update + auto-commit
- WASM build (if configured)
- Git tag
- Git push
- Mirror sync (if
mirrors:configured) - Goreleaser (binary projects)
- Release cleanup (old releases)
- Local install (
go install) - Documentation deployment (configured targets: GitHub Pages, statichost.eu, etc.)
- Post-release commands (
post_release:)
Process by code level
maturity.code in task-plus.yml alone picks the process
(ADR 0006, #109). The
levels follow the manual's
code-flow ladder: 0 trunk, 1
batch, 2 gated (batch plus CI, #51).
maturity.code |
Release remote | Base branch protected | tp release |
|---|---|---|---|
| 0 / unset | any | no | trunk: commit, tag and push on the base branch |
| 0 / unset | Forgejo, GitLab | yes | error: run tp maturity code 1, or unprotect the base; nothing written |
| 0 / unset | Forgejo, GitLab | forge unreachable | trunk, with a warning |
| 1 or more | Forgejo, GitLab | any | batch (below) |
| 1 or more | GitHub | — | error: needs a Forgejo or GitLab release remote |
On GitLab the pull request is a merge request, opened and read through
the GitLab API with the remote's token_env or, by default,
GITLAB_TOKEN (story 019). tp's output says "pull request" either way.
The base branch is the release remote's default branch (main unless
the remote's HEAD says otherwise).
Trunk off the base branch (#68)
A trunk release is made where the run stands. On a story branch that
would tag a commit the base branch cannot see, so the run stops before
anything is written and says so; --merge instead fast-forwards the base
branch to the story branch and releases from there. Only a fast-forward:
a base branch that has moved on is the human's to reconcile first.
| Checked-out branch | Base branch | --merge |
tp release |
|---|---|---|---|
| base | — | any | trunk release as above |
| story | — | no | stop in Gather: run tp maturity code 1, or re-run with --merge; nothing written |
| story | fast-forwardable to the story (remote and local) | yes | base checked out and fast-forwarded, then the trunk release from it; the story branch stays |
| story | has commits the story lacks | yes | error in Gather: reconcile first; nothing written |
--dry-run reports the row that applies.
Waiting for the merge (story 008)
merge_wait in task-plus.yml (default 10m; tp --init writes it)
keeps a batch run alive for the merge of the pull request it opened
(ADR 0003). The
run polls the forge every 15 s for up to merge_wait. On a terminal one
status line is rewritten in place each second with the time left
(PR #N open, 7m45s left (Ctrl-C stops waiting)) and cleared before the
outcome; piped or in CI the wait prints one line at the start and one at
the end.
| During the wait for the release PR | Then |
|---|---|
| merged | fetch; check out the base branch; fast-forward; run check again; tag and publish |
| still open at the deadline | says the release is still in flight (.tp-release-inflight) and that tp release on the base branch, once the PR is merged, tags it, closes the issues and deletes release (#133); exit 0 |
| closed without merging | says so, exit 0; nothing tagged |
| Ctrl-C | stops waiting; the PR stays open; exit 0 |
| the forge cannot be read | retried until the deadline; the error is reported with the timeout |
check fails on the merged base branch |
error naming the merged PR; fix, then tp release on the base branch tags it |
The wait for a story's pull request into release is the walk (story
023, below). With merge_wait: 0 the run does not wait. --dry-run
says it would wait and polls nothing.
What the release ships (story 017)
A batch run reads ROADMAP.md, the stories and the forge's issues
(tp roadmap):
| Found | In the release commit | After the tag |
|---|---|---|
| a roadmap item whose issue is closed | moved into the version's ### Roadmap list in CHANGELOG.md |
— |
an open roadmap item whose stories (**Roadmap:** #N) have all shipped or ship now |
asked "count it done?"; yes moves it as above | its issue closed with the release link |
a story with **Status:** ready and an issue |
status stamped shipped (vX.Y.Z) |
its issue closed with the release link |
a fragment changes/n.* |
gathered into the CHANGELOG | #n closed with the release link |
Issues are closed after the tag rather than by Closes #n in the pull
request, which would fire on merge, before the version exists.
What the tag run must finish is written to .tp-release-inflight
(gitignored, never committed): the version, the branch, the pull request,
the issues to close and the batch's story branches. The tag run (the same
run when merge_wait sees the merge, or the next tp release on the base
branch) closes each listed issue with a comment linking the release,
deletes the release branch and the story branches (below), and removes
the file. Until then tp check names what is in
flight. A file for another version is left alone with a warning; without
one the tag run says which issues it is not closing. --dry-run prints
the moves and writes nothing. A trunk release skips all of this.
Every run ends by comparing the working tree with what its outcome should
leave (#152), one Left behind: line per difference and no deletion; the
next run's Gather is the recovery. A run that walks on to another branch
leaves the report to the run on that branch; --dry-run has none.
| Run ended | HEAD on | .tp-release-inflight |
.tp-release.yml |
tag |
|---|---|---|---|---|
| released | base branch | not this version's | absent | present |
| in flight (release PR opened) | base branch | present, this version | — | absent |
| failed | the starting branch | — | — | — |
| with nothing to do | the starting branch | — | — | — |
| any | — | a present file git would commit is named (see tp check, #106) |
Release branch and fragments (story 021)
At code level 1 and up stories are released as a batch
(ADR 0005). Each story
carries its CHANGELOG line as a fragment, changes/<n>.<kind>.md for
issue #n, with kind one of added, changed, deprecated,
removed, fixed, security; roadmap is the kind of a retired
roadmap item (tp roadmap archive at this level, #142), rendered as
- #n text in the version's ### Roadmap list and counted as no
story and no change. Optional front matter orders it after other
stories:
---
after: [46]
---
Forget an environment from the perf form
A lone release, and the batch on request (story 030)
release exists only when a batch was asked for
(ADR 0007).
While the remote has none, a story is a lone release: tp release on
its branch makes the release commits there (the fragments gathered under
the version, the README markers, the fragment files deleted), runs the
checks, pushes the branch and opens its pull request straight into the
base, titled by the fragment, the version's CHANGELOG section as its
body. The merge is the release: tp release on the base tags it, and
with merge_wait the story run itself waits for the merge and walks on
to the base to tag. The cost follows the shape of the release:
| Stories in the release | Pull requests | Merges |
|---|---|---|
| 1 | 1 (story → main) |
1 |
| n ≥ 2 | n (story → release) + 1 (release → main) |
n + 1 |
A batch is asked for by tp release batch on the base branch, by a
second story's tp release while a lone release is in flight, or by a
.tp-release.yml naming another story. Opening one converts a lone
release in flight, each step skipped when its state already holds, so a
run that stopped resumes:
| Conversion step | State found | Step |
|---|---|---|
| cut | release absent on the remote |
pushed from the remote base |
| cut | release present |
skipped |
| revert | the story's CHANGELOG at the remote tip names an untagged version | one Unprepare vX.Y.Z: <branch> joins the batch commit reverting tp's release commits, pushed; the fragment is back (forward-only: no force push) |
| revert | the tip carries no release | skipped |
| retarget | pull request into the base | base set to release (Forgejo PATCH /pulls/{n}, GitLab PUT …/merge_requests/{n}) |
| retarget | pull request into release |
skipped |
| any | forge or push fails | error naming the step; the rerun resumes from this table |
| On a story branch | release on remote |
tp release |
|---|---|---|
| fragment, no pull request | no | the lone release above |
its own pull request into main open, more commits, no new fragment |
no | the release commits stand; checks; push; the pull request reused |
| its own pull request open, a new fragment | no | the release commits reverted (Unprepare) and remade for the new version; checks; push |
another story's pull request into main open |
any | the conversion, then as a story of the batch (below) |
.tp-release.yml names another story |
no | release cut, then as a story of the batch |
| behind its remote copy by a conversion's revert, no local commits | — | fast-forwarded, then as a story of the batch |
| behind and ahead of its remote copy | — | error: rebase onto the remote branch first |
its own pull request into main open, or its tip prepared |
yes | its own release commits reverted here, its pull request retargeted, then as a story of the batch |
tp release batch on main |
Does |
|---|---|
no release, no lone release |
cuts release from the remote base; names the next step |
no release, a lone release open |
the conversion, then as above |
release open |
says so and names the stories in it; nothing done |
maturity.code 0 |
error: a batch is code level 1 |
A batch of one story ships as a batch, two merges: the long path, not a gap.
Once release is on the remote, tp release on a story branch runs
the checks, pushes the story and opens its pull request into release,
titled by the fragment. No version, no CHANGELOG edit.
On the base branch (or release), tp release is the batch run. The
batch is listed in .tp-release.yml (gitignored): stories: [46, 47].
With no file, the batch is the stories already merged into release
(their fragments on release, not on the base), which is the usual case
once the forge merges the story PRs (#135).
The stories are merged into release in after: order, ties by issue
number, each --no-ff, followed by check, and release is pushed after
each one that passes. Each check, the docs pre-check and the deploy read
task-plus.yml from the tree they run on, not from the branch the run
started on (#152). Then the release commit on release: the fragments
gathered under the version, grouped by kind, (#n) on each line, and the
fragment files deleted. Then the pull request Release vX.Y.Z from
release to the base, and the wait or tag run as above.
.tp-release.yml |
State | tp release on the base |
|---|---|---|
| absent | stories in release, no open PR into it |
batch = those stories (#135): release commit; PR to base |
| absent | stories in release, PRs into it open |
error naming the PRs: merge or close them, or list the batch to have tp merge it (the tag run deletes release, which would orphan them) |
| absent | none in release, a lone release's pull request into the base open |
error naming it: merge it and tag, or tp release batch |
| absent | none in release |
error suggesting stories: from the open PRs into release |
| listed | none merged | merge each, check, push; release commit; PR to base |
| listed | some in release |
those skipped (already in release); the rest merged |
| listed | a story's branch gone, its fragment changes/n.* in release |
counted in release and skipped (merged on the forge, branch deleted) |
| listed | a story's branch carries an Update CHANGELOG for v… commit for an untagged version (#101) |
error before anything is merged: drop the commit, add a changes/<n>.<kind>.md fragment |
| listed | a story conflicts | stops naming it: merge release into the story, push, run again |
| listed | check fails after a merge |
stops naming it; the merge is undone; release not pushed past it |
| listed | after: cycle, a dependency neither listed nor with its fragment in the base or release, or no story/<n>-* branch and no fragment in release |
error before anything is merged |
| any | release PR open | reported, nothing else |
| any | release PR merged, untagged | tag run; release and the stories' branches deleted; .tp-release.yml removed |
| any | release/* branches on the remote |
error naming them |
The version follows from the fragments: any kind but fixed or
security bumps minor, else patch; release_bump is not consulted. The
comment is the fragments' first lines joined with ; , or with a space
after a line that ends in ., ! or ? (#165).
.tp-release-comment is not read.
The tag run deletes each story's branch on the remote and locally (#148),
one line per branch, since tp's own merge into release is no forge
merge and fires no delete-on-merge. The branch's tip being in the tagged
base is the proof it is done: a branch with commits the release lacks is
kept, and the line says so. A branch the forge already deleted on merge
is passed over on the remote, and its local copy goes the same way
(#162). Nothing here fails the release.
The tag run closes, with the release link, the issues of:
| Source | Closed |
|---|---|
fragment changes/n.* |
#n |
story **Status:** ready with an issue |
its issue |
| roadmap item counted done | its issue |
| the same number from several sources | once |
Walking the stories (story 023)
With merge_wait (10m by default), tp release on a story branch waits
for you to merge its pull request into release, then walks on. You
review and merge each story on the forge, and the run takes you from one
to the next. .tp-release.yml tells the walk how many stories the batch
has, so when the last one lands the walk checks out the base branch and
runs the batch there (#139): the plan is shown and Proceed? asked
before the release commit, the push of release and the release pull
request, and the run then waits for that pull request as any batch run
does. Nothing is published without that answer (#113): Proceed? is No
when stdin is closed, so an agent's non-interactive run on a story branch
stops at the plan with nothing done. Merging one story at a time, you
answer n until you are ready; merging them all in one sitting, the one
run carries through to the tag. When you merge on the forge, the batch's
own merge sees the story as already in release and skips it.
| Story PR during the wait | Stories in .tp-release.yml not yet in release |
Then |
|---|---|---|
| merged | some | checks out the first in after: order; pushes it, opens or reuses its PR, waits |
| merged | none | checks out the base branch and runs the batch: the plan, Proceed?, the release PR, the wait for its merge, the tag |
| merged | none, Proceed? answered n |
nothing done; the checkout is left on the base branch, the stories stay in release |
| merged | no .tp-release.yml |
stops: Next step (human): tp release on main (#135: the walk cannot know the batch) |
| open at the deadline, or Ctrl-C | any | stops on that story's branch; tp release there walks on |
| closed unmerged | any | stops; nothing tagged |
— (already in release, no open PR) |
any | skipped: on to the next |
With merge_wait: 0, a story run opens its PR and stops, as before.
A rerun on the story branch picks the wait back up (#75). The commit that
last passed check in this checkout is recorded in git config
(branch.<name>.tp-checked), so only that exact commit skips the checks;
a branch pushed by hand, or with commits since, is checked again.
Story PR into release |
Tip vs last checked | Rerun |
|---|---|---|
| open | same | no checks; push; wait |
| open | moved, or never checked here | checks; push; wait |
| closed without merging | any | error naming the PR: reopen it on the forge to go on; no new PR |
| merged | any | walks on, as above |
| merged, and the story is in the base branch | any | stops before the checks: names the release it went out in and the tidy-up, git switch main && git branch -d story/<n>-<slug>; untagged, points at the tag run on the base (#166) |
The release in progress: tp release query (story 024)
tp release query reports what the next tp release would ship and
changes nothing. It fetches the release remote first (-offline skips
the fetch and the forge) and always exits 0.
| Section | Holds |
|---|---|
| Mode | trunk or batch, with the maturity.code that decided it (#108) |
| Last release | the newest version tag, and the commits on the base branch since it |
| In the release | batch: the fragments on release that are not yet on the base, one line per story; trunk: story branches merged into the base since the last tag |
| Listed, still to merge | .tp-release.yml stories not yet in release, in after: order |
| Open, not in the release | remote story/<n>-* branches in neither of the above and not yet in the base |
| Lone release | a story branch whose tip names an untagged version, with its pull request into the base open and no release on the remote: vX.Y.Z on story/<n>-<slug>, pull request #N into main (story 030) |
| Carrying an untagged release commit | other remote story/* branches whose tip names a version with no tag (#101): a leftover, not a lone release |
| In flight | .tp-release-inflight's version and PR, with the PR's state from the forge (unknown (…) when the forge can't say) |
| Next version | worked out as tp release would: from the fragments, else release_bump |
| Next step | the one action that moves the release on, and who takes it: agent or human (#108; the table at the top) |
| Not ready | what the checkout lacks, or carries that the level refuses (#108) |
On trunk, a story whose branch the forge deleted after merging is counted only in the commits since the tag: nothing else records it.
Post-release commands
post_release: in task-plus.yml lists commands to run once the release is
published (pushed, goreleaser assets uploaded, docs deployed), each in the
project directory with TP_VERSION set to the new tag. Keep the work in a
Taskfile task and name it here:
post_release:
- task deploy:fetch
They run whenever the release is pushed, without a prompt. The tag is already public by then, so a failing command prints a warning and the rest still run; the release is not rolled back.
Staged deployment
tp release cuts and publishes one version; it has no release-candidate
or staging step. A project that wants to deploy to a staging site first,
canary, or promote on approval owns that in its deploy tooling and runs it
from post_release (or a Taskfile task), where TP_VERSION names the
release.
Pull mirrors
A Forgejo pull mirror refuses pushes, so it can't be one of remotes:.
List it under mirrors: instead and, after the push, tp asks it to fetch
now (POST /api/v1/repos/{owner}/{repo}/mirror-sync) rather than on its
own interval:
mirrors:
- {} # host and token_env from task-plus.local.yaml
- repo: hum3/other # owner/repo on the mirror host, if not the release remote's
host (Forgejo base URL; https:// if no scheme) and token_env default
to mirror_host and mirror_token_env in task-plus.local.yaml, so the
private host stays out of the repo. The token is fetched like any other
(list it under secrets: if the project has that list).
The step never fails the release; each mirror prints one line:
| Condition | Prints |
|---|---|
no host, or repo not owner/repo |
Skipping mirror sync: <reason> |
no token_env, or it is unset |
Skipping mirror sync <mirror>: <reason> |
| host unreachable, or API error | Skipping mirror sync <mirror>: <error> |
--dry-run |
Would sync mirror <mirror> |
| synced | Synced mirror <mirror> |
tp check verifies each mirror (fetching its token like tp issue):
| Mirror host says | Finding |
|---|---|
| unreachable | warning |
| API error, including repo not found | error |
| repo is not a pull mirror | error |
mirrors a URL other than the release remote (host/owner/repo, ignoring scheme, case and .git) |
error |
| pull mirror of the release remote | ok |