Compare commits

...

45 commits
v0.13.8 ... dev

Author SHA1 Message Date
Renovate Bot
0f78fc731a chore(deps): update rust crate notify to v7
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 6m52s
Rust / build (map[name:stable]) (push) Successful in 13m12s
Release Please / Release-plz (push) Successful in 2m18s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-10-25 18:12:13 +01:00
Renovate Bot
1784f3f28b chore(deps): update rust crate tui-scrollview to 0.5
All checks were successful
Rust / build (map[name:stable]) (push) Successful in 7m41s
Rust / build (map[name:nightly]) (push) Successful in 12m45s
Release Please / Release-plz (push) Successful in 2m6s
revert: reneable ScrollView
2024-10-25 18:10:44 +01:00
Renovate Bot
b794a21dd9 chore(deps): update rust crate gix to 0.67
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 6m58s
Rust / build (map[name:stable]) (push) Successful in 13m56s
Release Please / Release-plz (push) Successful in 1m6s
2024-10-22 19:16:59 +00:00
Renovate Bot
d989da659c chore(deps): update rust crate ratatui to 0.29
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 6m57s
Rust / build (map[name:stable]) (push) Successful in 14m18s
Release Please / Release-plz (push) Successful in 1m50s
2024-10-22 07:32:53 +01:00
23de987444 fix: disable ScrollView
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 12m38s
Rust / build (map[name:stable]) (push) Successful in 13m3s
Release Please / Release-plz (push) Successful in 1m57s
Current version is incompatible with latest Ratatui. Backout this change
when compatibility is restore.
2024-10-22 07:31:08 +01:00
Renovate Bot
9d6271a176 chore(deps): update docker.io/rust docker tag to v1.82.0
All checks were successful
Rust / build (map[name:stable]) (push) Successful in 9m16s
Rust / build (map[name:nightly]) (push) Successful in 13m39s
Release Please / Release-plz (push) Successful in 2m16s
2024-10-21 19:37:22 +01:00
Renovate Bot
ddc22867b3 chore(deps): update docker.io/woodpeckerci/plugin-docker-buildx docker tag to v5
All checks were successful
Rust / build (map[name:stable]) (push) Successful in 8m44s
Rust / build (map[name:nightly]) (push) Successful in 13m23s
Release Please / Release-plz (push) Successful in 2m37s
2024-10-21 19:37:13 +01:00
Renovate Bot
5e4e287562 chore(deps): update rust crate rstest to 0.23
All checks were successful
Rust / build (map[name:stable]) (push) Successful in 7m52s
Rust / build (map[name:nightly]) (push) Successful in 12m53s
Release Please / Release-plz (push) Successful in 1m27s
2024-10-21 19:18:04 +01:00
Renovate Bot
6a0e0580dc chore(deps): update rust crate secrecy to 0.10
All checks were successful
Rust / build (map[name:stable]) (push) Successful in 7m51s
Rust / build (map[name:nightly]) (push) Successful in 12m40s
Release Please / Release-plz (push) Successful in 1m41s
2024-10-21 19:14:10 +01:00
Renovate Bot
7bd6347dd8 chore(deps): update kemitix/rust action to v2.3.0
All checks were successful
ci/woodpecker/pr/cron-docker-builder Pipeline was successful
ci/woodpecker/pr/push-next Pipeline was successful
ci/woodpecker/pr/tag-created Pipeline was successful
ci/woodpecker/pull_request_closed/cron-docker-builder Pipeline was successful
ci/woodpecker/pull_request_closed/push-next Pipeline was successful
ci/woodpecker/pull_request_closed/tag-created Pipeline was successful
Rust / build (map[name:nightly]) (push) Successful in 7m44s
Rust / build (map[name:stable]) (push) Successful in 13m59s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m43s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-30 21:46:31 +00:00
Renovate Bot
360b7f2cf7 chore(deps): update kemitix/rust action to v2.2.0
All checks were successful
Release Please / Release-plz (push) Successful in 51s
Rust / build (map[name:stable]) (push) Successful in 6m31s
Rust / build (map[name:nightly]) (push) Successful in 11m11s
Rust / build (map[name:nightly]) (pull_request) Successful in 6m39s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/pr/cron-docker-builder Pipeline was successful
ci/woodpecker/pr/push-next Pipeline was successful
ci/woodpecker/pr/tag-created Pipeline was successful
ci/woodpecker/pull_request_closed/cron-docker-builder Pipeline was successful
ci/woodpecker/pull_request_closed/push-next Pipeline was successful
ci/woodpecker/pull_request_closed/tag-created Pipeline was successful
Rust / build (map[name:stable]) (pull_request) Successful in 6m15s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-25 08:31:43 +00:00
f3a5b9cb4c build: switch to forgejo-todo-checker
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 6m21s
Rust / build (map[name:stable]) (push) Successful in 8m29s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m27s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
Remove woodpecker's TODO checker
2024-09-22 15:22:53 +01:00
18a537b18e build: add cargo machette to push-next workflow
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 6m49s
Rust / build (map[name:stable]) (push) Successful in 7m42s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 54s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-17 15:20:34 +01:00
ef6474ef9f test: also run CI tests against Rust nightly
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 6m23s
Rust / build (map[name:stable]) (push) Successful in 7m58s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 47s
2024-09-17 11:44:52 +01:00
dbf1a0db27 docs: add demo gif of tui
All checks were successful
Rust / build (push) Successful in 6m10s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m31s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-16 13:54:33 +01:00
ForgeJo Action. See: https://git.kemitix.net/kemitix/rust
91c5973e31 chore: release
All checks were successful
ci/woodpecker/pr/cron-docker-builder Pipeline was successful
ci/woodpecker/pr/push-next Pipeline was successful
ci/woodpecker/pr/tag-created Pipeline was successful
ci/woodpecker/pull_request_closed/cron-docker-builder Pipeline was successful
ci/woodpecker/pull_request_closed/push-next Pipeline was successful
ci/woodpecker/pull_request_closed/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 3m44s
ci/woodpecker/tag/cron-docker-builder Pipeline was successful
ci/woodpecker/tag/push-next Pipeline was successful
ci/woodpecker/tag/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
Rust / build (push) Successful in 6m16s
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
Signed-off-by: ForgeJo Action. See: https://git.kemitix.net/kemitix/rust <action@git.kemitix.net>
2024-09-14 14:24:04 +00:00
978205b823 feat(tui): add time and version in border
All checks were successful
Rust / build (push) Successful in 6m12s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m6s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-14 15:13:45 +01:00
8359d0d7ca refactor: Update TUI sooner when receiving CI status
All checks were successful
Rust / build (push) Successful in 6m6s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m49s
Looking to avoid getting stuck on 'Checking CI status', but this doesn't
appear to be where the problem is coming from.
2024-09-14 12:40:30 +01:00
93cf6f83df chore: add run and run-ui recipes to justfile
All checks were successful
Rust / build (push) Successful in 6m9s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m34s
2024-09-14 12:26:06 +01:00
681d85aac1 chore: remove manual crates.io publish recipe from justfile
All checks were successful
Rust / build (push) Successful in 6m9s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m50s
2024-09-14 12:22:29 +01:00
d4f16e6f5e feat: should fetch repo on startup when not cloning
All checks were successful
Rust / build (push) Successful in 6m8s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m5s
We already have a copy of the repo, so we don't clone, but we should
perform a `git fetch` to make sure it is up-to-date.
2024-09-14 12:19:24 +01:00
048111202a feat: Remove branches when fetching from remote
All checks were successful
Rust / build (push) Successful in 6m9s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m30s
2024-09-14 07:42:24 +01:00
3ea7f36c98 build(docker): Don't break when debian drops old packge versions
Some checks failed
Rust / build (push) Successful in 6m8s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
Release Please / Release-plz (push) Failing after 1h0m59s
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Debian routinly drop older versions of packages from the repositories as
new versions replace them. Pinning the version causes the build to break
at seamingly random times when the pinned version gets dropped.
2024-09-14 07:38:13 +01:00
6c60e3fb7a refactor: reimplement git fetch using git
All checks were successful
Rust / build (push) Successful in 6m11s
Release Please / Release-plz (push) Successful in 1m38s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-13 18:55:21 +01:00
313d6d79c5 docs: mark tui as complete on roadmap
All checks were successful
Rust / build (push) Successful in 6m18s
Release Please / Release-plz (push) Successful in 1m6s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-09-13 09:48:38 +01:00
189d579d33 docs: Add missing port mapping parameter for running in docker
All checks were successful
Rust / build (push) Successful in 6m11s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 56s
2024-09-13 08:59:38 +01:00
a77c6335a6 chore: ignore .local/ directory
All checks were successful
Rust / build (push) Successful in 6m23s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m27s
This directory is created when running git-next via docker with the --ui
option.
2024-09-13 08:53:04 +01:00
ForgeJo Action. See: https://git.kemitix.net/kemitix/rust
82241de0dd chore: release
All checks were successful
ci/woodpecker/pr/cron-docker-builder Pipeline was successful
ci/woodpecker/pr/push-next Pipeline was successful
ci/woodpecker/pr/tag-created Pipeline was successful
ci/woodpecker/pull_request_closed/cron-docker-builder Pipeline was successful
ci/woodpecker/pull_request_closed/push-next Pipeline was successful
ci/woodpecker/pull_request_closed/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 3m53s
ci/woodpecker/tag/cron-docker-builder Pipeline was successful
ci/woodpecker/tag/push-next Pipeline was successful
ci/woodpecker/tag/tag-created Pipeline was successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
Rust / build (push) Successful in 6m11s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Signed-off-by: ForgeJo Action. See: https://git.kemitix.net/kemitix/rust <action@git.kemitix.net>
2024-09-12 19:46:24 +00:00
664e424d1a fix(tui): make tui work from docker image
All checks were successful
Rust / build (push) Successful in 6m13s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 2m26s
Add missing environment variable in Dockerfile and gave example command
to run via docker.

Closes kemitix/git-next#154
2024-09-12 19:50:29 +01:00
df6b96fbfd fix(tui): alerts, such as WIP aren't being reset
All checks were successful
Rust / build (push) Successful in 6m11s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m20s
2024-09-12 10:37:53 +01:00
566125f5c0 fix(test): tests requiring .git pass when not present
All checks were successful
Rust / build (push) Successful in 6m12s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m32s
These are tests that assume they are running in a locally checked out
git repository. If that isn't the case, e.g. when using jujutsu, then
the tests should not fail. They will continue to run as normal under
CI conditions as those do use a locally checked out git repository.
2024-09-12 10:37:46 +01:00
80af909ab0 build(push-next): use rust image v1.81.0
All checks were successful
Release Please / Release-plz (push) Successful in 1m18s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Rust / build (push) Successful in 10m7s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-08 12:07:18 +01:00
ecd460cdfb fix(tui): update ui when push next or main finishes
All checks were successful
Rust / build (push) Successful in 8m54s
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m34s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
Removes the artificial pause while we wait for any CI to start before
checking the CI status.

Closes kemitix/git-next#160
2024-09-06 18:28:02 +01:00
Renovate Bot
35c2057f05 chore(deps): update docker.io/rust docker tag to v1.81.0
All checks were successful
ci/woodpecker/pr/cron-docker-builder Pipeline was successful
ci/woodpecker/pr/push-next Pipeline was successful
ci/woodpecker/pr/tag-created Pipeline was successful
ci/woodpecker/pull_request_closed/cron-docker-builder Pipeline was successful
ci/woodpecker/pull_request_closed/push-next Pipeline was successful
ci/woodpecker/pull_request_closed/tag-created Pipeline was successful
Rust / build (push) Successful in 7m38s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m0s
2024-09-06 07:31:55 +00:00
d2e2d00fe1 fix(tui): don't set background for normal repo alias
All checks were successful
Rust / build (push) Successful in 6m24s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 56s
This didn't look good when using a light coloured terminal.
2024-09-06 08:19:43 +01:00
e759e495fd feat: optionally specify max commits between dev and main
All checks were successful
Rust / build (push) Successful in 6m21s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 2m9s
The default is 25.

Closes kemitix/git-next#121
2024-09-06 08:10:10 +01:00
ForgeJo Action. See: https://git.kemitix.net/kemitix/rust
3672fd5d45 chore: release
All checks were successful
ci/woodpecker/pr/cron-docker-builder Pipeline was successful
ci/woodpecker/pr/push-next Pipeline was successful
ci/woodpecker/pr/tag-created Pipeline was successful
ci/woodpecker/pull_request_closed/cron-docker-builder Pipeline was successful
ci/woodpecker/pull_request_closed/push-next Pipeline was successful
ci/woodpecker/pull_request_closed/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
Release Please / Release-plz (push) Successful in 6m17s
ci/woodpecker/tag/push-next Pipeline was successful
ci/woodpecker/tag/cron-docker-builder Pipeline was successful
ci/woodpecker/tag/tag-created Pipeline was successful
Rust / build (push) Successful in 9m2s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
Signed-off-by: ForgeJo Action. See: https://git.kemitix.net/kemitix/rust <action@git.kemitix.net>
2024-09-04 05:45:15 +00:00
1f0b5e867c fix(tui): alerts are cleared on next repo update
All checks were successful
Rust / build (push) Successful in 8m54s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m21s
Closes kemitix/git-next#151
2024-09-04 06:35:41 +01:00
8ca7aad3c3 docs: Expand docker docmentation
All checks were successful
Rust / build (push) Successful in 7m44s
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m50s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-03 20:17:59 +01:00
d923e831f0 build(docker): enable passing arguments when running via docker
All checks were successful
Rust / build (push) Successful in 6m19s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 2m0s
2024-09-03 20:08:40 +01:00
5e0cf270dd fix: shutdown properly on error
All checks were successful
Rust / build (push) Successful in 9m7s
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 2m2s
2024-09-03 20:08:12 +01:00
b4a4631a1d fix: shutdown properly on file parse error
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m24s
ci/woodpecker/push/push-next Pipeline was successful
Rust / build (push) Successful in 10m0s
Closes kemitix/git-next#152
2024-09-03 06:53:12 +01:00
181ec8eb0f build(woodpecker): build docker image on push to next
All checks were successful
Rust / build (push) Successful in 8m28s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 56s
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
2024-09-01 13:53:03 +01:00
47cbbad8e7 build(docker): update debian libssl3 dependency
All checks were successful
Rust / build (push) Successful in 6m2s
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m55s
2024-09-01 13:52:47 +01:00
e793c18215 docs(release): add links
All checks were successful
Rust / build (push) Successful in 7m31s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m30s
2024-09-01 13:26:49 +01:00
66 changed files with 1622 additions and 499 deletions

View file

@ -22,13 +22,13 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Run release-plz release-pr - name: Run release-plz release-pr
uses: https://git.kemitix.net/kemitix/rust@v1.80.0-2 uses: https://git.kemitix.net/kemitix/rust@v2.3.0
with: with:
args: release-plz release-pr --backend gitea --git-token ${{ secrets.FORGEJO_TOKEN }} args: release-plz release-pr --backend gitea --git-token ${{ secrets.FORGEJO_TOKEN }}
env: env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
- name: Run release-plz release - name: Run release-plz release
uses: https://git.kemitix.net/kemitix/rust@v1.80.0-2 uses: https://git.kemitix.net/kemitix/rust@v2.3.0
with: with:
args: release-plz release --backend gitea --git-token ${{ secrets.FORGEJO_TOKEN }} args: release-plz release --backend gitea --git-token ${{ secrets.FORGEJO_TOKEN }}
env: env:

View file

@ -13,26 +13,40 @@ jobs:
build: build:
runs-on: docker runs-on: docker
strategy:
matrix:
toolchain:
- name: stable
- name: nightly
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Format - name: Check TODOs
uses: https://git.kemitix.net/kemitix/rust@v1.80.1-1 uses: kemitix/todo-checker@v1.1.0
- name: Machete
uses: https://git.kemitix.net/kemitix/rust@v2.3.0
with: with:
args: cargo fmt --all -- --check args: ${{ matrix.toolchain.name }} cargo machete
- name: Format
uses: https://git.kemitix.net/kemitix/rust@v2.3.0
with:
args: ${{ matrix.toolchain.name }} cargo fmt --all -- --check
- name: Clippy - name: Clippy
uses: https://git.kemitix.net/kemitix/rust@v1.80.1-1 uses: https://git.kemitix.net/kemitix/rust@v2.3.0
with: with:
args: cargo hack --feature-powerset clippy args: ${{ matrix.toolchain.name }} cargo hack --feature-powerset clippy
- name: Build - name: Build
uses: https://git.kemitix.net/kemitix/rust@v1.80.1-1 uses: https://git.kemitix.net/kemitix/rust@v2.3.0
with: with:
args: cargo hack --feature-powerset build args: ${{ matrix.toolchain.name }} cargo hack --feature-powerset build
- name: Test - name: Test
uses: https://git.kemitix.net/kemitix/rust@v1.80.1-1 uses: https://git.kemitix.net/kemitix/rust@v2.3.0
with: with:
args: cargo hack --feature-powerset test args: ${{ matrix.toolchain.name }} cargo hack --feature-powerset test

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
# git-next ui logs
.local/
# ---> Rust # ---> Rust
# Generated by Cargo # Generated by Cargo
# will have compiled files and executables # will have compiled files and executables

View file

@ -1,13 +1,13 @@
steps: steps:
todo_check: docker-build:
# INFO: This doesn't have an equivalent yet for Forgejo Actions
# INFO: https://woodpecker-ci.org/plugins/TODO-Checker
image: codeberg.org/epsilon_02/todo-checker:1.1
when: when:
- event: push - event: push
branch: next branch: next
# INFO: https://woodpecker-ci.org/plugins/Docker%20Buildx
image: docker.io/woodpeckerci/plugin-docker-buildx:5.0.0
settings: settings:
# git-next-woodpecker-todo-checker - read:issue username: kemitix
repository_token: "776a3b928b852472c2af727a360c85c00af64b9f" repo: git.kemitix.net/kemitix/git-next
prefix_regex: "(#|//) (TODO|FIXME): " dockerfile: Dockerfile
debug: false auto_tag: false
dry-run: true # don't push to remote repo

View file

@ -4,7 +4,7 @@ steps:
- event: tag - event: tag
ref: refs/tags/v* ref: refs/tags/v*
# INFO: https://woodpecker-ci.org/plugins/Docker%20Buildx # INFO: https://woodpecker-ci.org/plugins/Docker%20Buildx
image: docker.io/woodpeckerci/plugin-docker-buildx:4.2.0 image: docker.io/woodpeckerci/plugin-docker-buildx:5.0.0
settings: settings:
username: kemitix username: kemitix
repo: git.kemitix.net/kemitix/git-next repo: git.kemitix.net/kemitix/git-next

View file

@ -2,6 +2,65 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## `git-next-core` - [0.13.11](https://git.kemitix.net/kemitix/git-next/compare/git-next-core-v0.13.10...git-next-core-v0.13.11) - 2024-09-14
### Added
- should fetch repo on startup when not cloning
- Remove branches when fetching from remote
### Other
- reimplement git fetch using git
## `git-next` - [0.13.11](https://git.kemitix.net/kemitix/git-next/compare/v0.13.10...v0.13.11) - 2024-09-14
### Added
- *(tui)* add time and version in border
- should fetch repo on startup when not cloning
- Remove branches when fetching from remote
### Other
- Update TUI sooner when receiving CI status
- reimplement git fetch using git
- mark tui as complete on roadmap
- Add missing port mapping parameter for running in docker
## `git-next-forge-github` - [0.13.10](https://git.kemitix.net/kemitix/git-next/compare/git-next-forge-github-v0.13.9...git-next-forge-github-v0.13.10) - 2024-09-12
### Added
- optionally specify max commits between dev and main
## `git-next-forge-forgejo` - [0.13.10](https://git.kemitix.net/kemitix/git-next/compare/git-next-forge-forgejo-v0.13.9...git-next-forge-forgejo-v0.13.10) - 2024-09-12
### Added
- optionally specify max commits between dev and main
## `git-next-core` - [0.13.10](https://git.kemitix.net/kemitix/git-next/compare/git-next-core-v0.13.9...git-next-core-v0.13.10) - 2024-09-12
### Added
- optionally specify max commits between dev and main
## `git-next` - [0.13.10](https://git.kemitix.net/kemitix/git-next/compare/v0.13.9...v0.13.10) - 2024-09-12
### Added
- optionally specify max commits between dev and main
### Fixed
- *(tui)* make tui work from docker image
- *(tui)* alerts, such as WIP aren't being reset
- *(test)* tests requiring .git pass when not present
- *(tui)* update ui when push next or main finishes
- *(tui)* don't set background for normal repo alias
## `git-next` - [0.13.9](https://git.kemitix.net/kemitix/git-next/compare/v0.13.8...v0.13.9) - 2024-09-04
### Fixed
- *(tui)* alerts are cleared on next repo update
- shutdown properly on error
- shutdown properly on file parse error
### Other
- Expand docker docmentation
## `git-next-forge-forgejo` - [0.13.8](https://git.kemitix.net/kemitix/git-next/compare/git-next-forge-forgejo-v0.13.7...git-next-forge-forgejo-v0.13.8) - 2024-09-01 ## `git-next-forge-forgejo` - [0.13.8](https://git.kemitix.net/kemitix/git-next/compare/git-next-forge-forgejo-v0.13.7...git-next-forge-forgejo-v0.13.8) - 2024-09-01
### Other ### Other

462
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.13.8" version = "0.13.11"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
@ -27,12 +27,13 @@ git-next-forge-forgejo = { path = "crates/forge-forgejo", version = "0.13" }
git-next-forge-github = { path = "crates/forge-github", version = "0.13" } git-next-forge-github = { path = "crates/forge-github", version = "0.13" }
# TUI # TUI
ratatui = "0.28" ratatui = "0.29"
directories = "5.0" directories = "5.0"
lazy_static = "1.5" lazy_static = "1.5"
color-eyre = "0.6" color-eyre = "0.6"
tui-scrollview = "0.4" tui-scrollview = "0.5"
regex = "1.10" regex = "1.10"
chrono = "0.4"
# CLI parsing # CLI parsing
clap = { version = "4.5", features = ["cargo", "derive"] } clap = { version = "4.5", features = ["cargo", "derive"] }
@ -51,7 +52,7 @@ sha2 = "0.10"
hex = "0.4" hex = "0.4"
# git # git
gix = { version = "0.66", features = [ gix = { version = "0.67", features = [
"dirwalk", "dirwalk",
"blocking-http-transport-reqwest-rust-tls", "blocking-http-transport-reqwest-rust-tls",
] } ] }
@ -67,7 +68,7 @@ serde_json = "1.0"
toml = "0.8" toml = "0.8"
# Secrets and Password # Secrets and Password
secrecy = "0.8" secrecy = "0.10"
# Conventional Commit check # Conventional Commit check
git-conventional = "0.12" git-conventional = "0.12"
@ -97,7 +98,7 @@ pike = "0.1"
take-until = "0.2" take-until = "0.2"
# file watcher # file watcher
notify = "6.1" notify = "7.0"
# Actors # Actors
actix = "0.13" actix = "0.13"
@ -117,4 +118,4 @@ pretty_assertions = "1.4"
rand = "0.8" rand = "0.8"
mockall = "0.13" mockall = "0.13"
test-log = "0.2" test-log = "0.2"
rstest = { version = "0.22", features = ["async-timeout"] } rstest = { version = "0.23", features = ["async-timeout"] }

View file

@ -15,17 +15,16 @@ COPY . .
RUN cargo build --release --bin git-next --all-features && \ RUN cargo build --release --bin git-next --all-features && \
strip target/release/git-next strip target/release/git-next
FROM docker.io/debian:stable-20240722-slim AS runtime FROM docker.io/debian:stable-20240904-slim AS runtime
WORKDIR /app WORKDIR /app
RUN apt-get update && \ RUN apt-get update && \
apt-get install --no-install-recommends -y \ apt-get satisfy -y "git (>=2.39), libssl3 (>=3.0.14), libdbus-1-dev (>=1.14.10), ca-certificates (>=20230311)" \
git=1:2.39.2-1.1 \
libssl3=3.0.13-1~deb12u1 \
libdbus-1-dev=1.14.10-1~deb12u1 \
ca-certificates=20230311 \
&& \ && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
USER 1000 USER 1000
COPY --from=builder /app/target/release/git-next /usr/local/bin COPY --from=builder /app/target/release/git-next /usr/local/bin
ENTRYPOINT [ "/usr/local/bin/git-next", "server", "start" ] ENV HOME=/app
ENTRYPOINT [ "/usr/local/bin/git-next" ]
CMD [ "server", "start" ]

View file

@ -1,4 +1,4 @@
FROM docker.io/rust:1.80.1-bookworm FROM docker.io/rust:1.82.0-bookworm
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y libdbus-1-dev && \ apt-get install -y libdbus-1-dev && \

View file

@ -6,4 +6,7 @@
development workflows where each commit must pass CI before being included in development workflows where each commit must pass CI before being included in
the main branch. the main branch.
![Demo](./demo.gif)
See [README.md](https://git.kemitix.net/kemitix/git-next/src/branch/main/crates/cli/README.md) for more information. See [README.md](https://git.kemitix.net/kemitix/git-next/src/branch/main/crates/cli/README.md) for more information.

View file

@ -3,8 +3,8 @@
## TLDR ## TLDR
1. Merge PR `chore: release` 1. Merge PR `chore: release`
2. Wait for `push-main` workflow to complete 2. Wait for [`push-main` workflow](https://git.kemitix.net/kemitix/git-next/actions?workflow=push-main.yml) to complete
3. Replace Release Notes body with details from CHANGELOG (remove crates and duplicates) 3. Replace [Release Notes](https://git.kemitix.net/kemitix/git-next/releases) body with details from [CHANGELOG](https://git.kemitix.net/kemitix/git-next/src/branch/main/CHANGELOG.md) (remove crates and duplicates)
4. Update thread: <https://mitra.kemitix.net/post/01907ef5-5bd9-b0b6-2b8a-e29762541d78> 4. Update thread: <https://mitra.kemitix.net/post/01907ef5-5bd9-b0b6-2b8a-e29762541d78>
## Detail ## Detail

View file

@ -16,7 +16,7 @@ categories = { workspace = true }
default = ["forgejo", "github", "tui"] default = ["forgejo", "github", "tui"]
forgejo = ["git-next-forge-forgejo"] forgejo = ["git-next-forge-forgejo"]
github = ["git-next-forge-github"] github = ["git-next-forge-github"]
tui = ["ratatui", "directories", "lazy_static", "tui-scrollview", "regex"] tui = ["ratatui", "directories", "lazy_static", "tui-scrollview", "regex", "chrono"]
[dependencies] [dependencies]
git-next-core = { workspace = true } git-next-core = { workspace = true }
@ -30,6 +30,7 @@ lazy_static = { workspace = true, optional = true }
color-eyre = { workspace = true } color-eyre = { workspace = true }
tui-scrollview = { workspace = true, optional = true } tui-scrollview = { workspace = true, optional = true }
regex = { workspace = true, optional = true } regex = { workspace = true, optional = true }
chrono = { workspace = true, optional = true }
# CLI parsing # CLI parsing
clap = { workspace = true } clap = { workspace = true }
@ -51,6 +52,7 @@ toml = { workspace = true }
# Actors # Actors
actix = { workspace = true } actix = { workspace = true }
actix-rt = { workspace = true } actix-rt = { workspace = true }
tokio = { workspace = true }
# boilerplate # boilerplate
bon = { workspace = true } bon = { workspace = true }

View file

@ -59,7 +59,7 @@ cargo install --path crates/cli
- [x] cli - [x] cli
- [x] server - [x] server
- [x] notifications - notify user when intervention required (e.g. to rebase) - [x] notifications - notify user when intervention required (e.g. to rebase)
- [ ] tui overview - [x] tui overview
- [ ] webui overview - [ ] webui overview
## Branch Names ## Branch Names
@ -198,13 +198,14 @@ forge_type = "GitHub"
hostname = "github.com" hostname = "github.com"
user = "username" user = "username"
token = "api-key" token = "api-key"
max_dev_commits = 25
``` ```
- **forge_type** - one of: `ForgeJo` or `GitHub` - **forge_type** - one of: `ForgeJo` or `GitHub`
- **hostname** - the hostname for the forge. - **hostname** - the hostname for the forge.
- **user** - the user to authenticate as - **user** - the user to authenticate as
- **token** - application token for the user. See below for the permissions - **token** - application token for the user. See [Forges](#forges) below for the permissions required for each forge.
required for on each forge. - **max_dev_commits** - [optional] the maximum number of commits allowed between `dev` and `main`. Defaults to 25.
Generally, the `user` will need to be able to push to `main` and to _force-push_ Generally, the `user` will need to be able to push to `main` and to _force-push_
to `next`. to `next`.
@ -576,6 +577,58 @@ world = { repo = "user/world", branch = "master", main = "master", next = "upcom
The token is created [here](https://github.com/settings/tokens/new) and requires the `repo` and `admin:repo_hook` permissions. The token is created [here](https://github.com/settings/tokens/new) and requires the `repo` and `admin:repo_hook` permissions.
## Docker
`git-next` is available as a [Docker image](https://git.kemitix.net/kemitix/-/packages/container/git-next/).
```shell
docker pull docker pull git.kemitix.net/kemitix/git-next:latest
```
### Docker Compose
Here is an example `docker-compose.yml`:
```yaml
services:
server:
image: git.kemitix.net/kemitix/git-next:latest
container_name: git-next-server
restart: unless-stopped
environment:
RUST_LOG: "hyper=warn,info"
ports:
- 8080:8092
volumes:
- ./:/app/
```
Note: this assumes the `git-next-server.toml` has a `listen.http.port` of
`8092` and that you are using a reverse proxy to route traffic arriving at
`listen.url` to port `8080`.
### Docker Run
This will run with the `server start` options:
```shell
docker run -it -p "8080:8092" -v .:/app/ git.kemitix.net/kemitix/git-next:latest
```
To perform `server init`:
```shell
docker run -it -v .:/app/ git.kemitix.net/kemitix/git-next:latest server init
```
To perform repo `init`:
```shell
docker run -it -v .:/app/ git.kemitix.net/kemitix/git-next:latest init
```
TUI support is not available in the docker container. See [kemitix/git-next#154](https://git.kemitix.net/kemitix/git-next/issues/154).
## Contributing ## Contributing
Contributions to `git-next` are welcome! If you find a bug or have a feature Contributions to `git-next` are welcome! If you find a bug or have a feature

View file

@ -1,8 +1,5 @@
// //
use git_next_core::{ use git_next_core::git::{ForgeLike, RepoDetails};
git::{ForgeLike, RepoDetails},
ForgeType,
};
#[cfg(feature = "forgejo")] #[cfg(feature = "forgejo")]
use git_next_forge_forgejo::ForgeJo; use git_next_forge_forgejo::ForgeJo;
@ -19,10 +16,14 @@ impl Forge {
pub fn create(repo_details: RepoDetails, net: Network) -> Box<dyn ForgeLike> { pub fn create(repo_details: RepoDetails, net: Network) -> Box<dyn ForgeLike> {
match repo_details.forge.forge_type() { match repo_details.forge.forge_type() {
#[cfg(feature = "forgejo")] #[cfg(feature = "forgejo")]
ForgeType::ForgeJo => Box::new(ForgeJo::new(repo_details, net)), git_next_core::ForgeType::ForgeJo => Box::new(ForgeJo::new(repo_details, net)),
#[cfg(feature = "github")] #[cfg(feature = "github")]
ForgeType::GitHub => Box::new(Github::new(repo_details, net)), git_next_core::ForgeType::GitHub => Box::new(Github::new(repo_details, net)),
_ => unreachable!(), _ => {
drop(repo_details);
drop(net);
unreachable!();
}
} }
} }
} }

View file

@ -1,4 +1,5 @@
// //
#[cfg(any(feature = "forgejo", feature = "github"))]
use super::*; use super::*;
use git_next_core::{ use git_next_core::{
@ -11,7 +12,7 @@ use git_next_core::{
#[test] #[test]
fn test_forgejo_name() { fn test_forgejo_name() {
let net = Network::new_mock(); let net = Network::new_mock();
let repo_details = given_repo_details(ForgeType::ForgeJo); let repo_details = given_repo_details(git_next_core::ForgeType::ForgeJo);
let forge = Forge::create(repo_details, net); let forge = Forge::create(repo_details, net);
assert_eq!(forge.name(), "forgejo"); assert_eq!(forge.name(), "forgejo");
} }
@ -20,20 +21,17 @@ fn test_forgejo_name() {
#[test] #[test]
fn test_github_name() { fn test_github_name() {
let net = Network::new_mock(); let net = Network::new_mock();
let repo_details = given_repo_details(ForgeType::GitHub); let repo_details = given_repo_details(git_next_core::ForgeType::GitHub);
let forge = Forge::create(repo_details, net); let forge = Forge::create(repo_details, net);
assert_eq!(forge.name(), "github"); assert_eq!(forge.name(), "github");
} }
fn given_fs() -> kxio::fs::FileSystem { #[allow(dead_code)]
kxio::fs::temp().unwrap_or_else(|e| { fn given_repo_details(forge_type: git_next_core::ForgeType) -> RepoDetails {
let fs = kxio::fs::temp().unwrap_or_else(|e| {
println!("{e}"); println!("{e}");
panic!("fs") panic!("fs")
}) });
}
fn given_repo_details(forge_type: ForgeType) -> RepoDetails {
let fs = given_fs();
git::repo_details( git::repo_details(
1, 1,
git::Generation::default(), git::Generation::default(),

View file

@ -19,7 +19,7 @@ use tracing::{info, instrument, warn};
pub fn advance_next( pub fn advance_next(
commit: Option<Commit>, commit: Option<Commit>,
force: git_next_core::git::push::Force, force: git_next_core::git::push::Force,
repo_details: RepoDetails, repo_details: &RepoDetails,
repo_config: RepoConfig, repo_config: RepoConfig,
open_repository: &dyn OpenRepositoryLike, open_repository: &dyn OpenRepositoryLike,
message_token: MessageToken, message_token: MessageToken,
@ -29,7 +29,7 @@ pub fn advance_next(
info!("Advancing next to commit '{}'", commit); info!("Advancing next to commit '{}'", commit);
reset( reset(
open_repository, open_repository,
&repo_details, repo_details,
&repo_config.branches().next(), &repo_config.branches().next(),
&commit.into(), &commit.into(),
&force, &force,

View file

@ -1,7 +1,7 @@
// //
use actix::prelude::*; use actix::prelude::*;
use git_next_core::RepoConfigSource; use git_next_core::{git, RepoConfigSource};
use tracing::warn; use tracing::warn;
@ -35,18 +35,25 @@ impl Handler<AdvanceMain> for RepoActor {
commit: commit.clone(), commit: commit.clone(),
}); });
match advance_main(commit, &repo_details, &repo_config, &**open_repository) { if let Err(err) = advance_main(commit, &repo_details, &repo_config, &**open_repository) {
Err(err) => { warn!("advance main: {err}");
warn!("advance main: {err}"); self.alert_tui(format!("advance main: {err}"));
} else {
self.update_tui(RepoUpdate::MainUpdated);
if let Some(open_repository) = &self.open_repository {
match open_repository.fetch() {
Ok(()) => self.update_tui_log(git::graph::log(&self.repo_details)),
Err(err) => self.alert_tui(format!("fetching: {err}")),
}
} }
Ok(()) => match repo_config.source() { match repo_config.source() {
RepoConfigSource::Repo => { RepoConfigSource::Repo => {
do_send(&addr, LoadConfigFromRepo, self.log.as_ref()); do_send(&addr, LoadConfigFromRepo, self.log.as_ref());
} }
RepoConfigSource::Server => { RepoConfigSource::Server => {
do_send(&addr, ValidateRepo::new(message_token), self.log.as_ref()); do_send(&addr, ValidateRepo::new(message_token), self.log.as_ref());
} }
}, }
} }
} }
} }

View file

@ -1,6 +1,7 @@
// //
use actix::prelude::*; use actix::prelude::*;
use git_next_core::git;
use tracing::{warn, Instrument}; use tracing::{warn, Instrument};
use crate::{ use crate::{
@ -43,13 +44,18 @@ impl Handler<AdvanceNext> for RepoActor {
match advance_next( match advance_next(
commit, commit,
force, force,
repo_details, &repo_details,
repo_config, repo_config,
&**open_repository, &**open_repository,
self.message_token, self.message_token,
) { ) {
Ok(message_token) => { Ok(message_token) => {
// pause to allow any CI checks to be started self.update_tui(RepoUpdate::NextUpdated);
match open_repository.fetch() {
Ok(()) => self.update_tui_log(git::graph::log(&self.repo_details)),
Err(err) => self.alert_tui(format!("fetching: {err}")),
}
// INFO: pause to allow any CI checks to be started
let sleep_duration = self.sleep_duration; let sleep_duration = self.sleep_duration;
let log = self.log.clone(); let log = self.log.clone();
async move { async move {

View file

@ -17,21 +17,20 @@ impl Handler<ReceiveCIStatus> for RepoActor {
type Result = (); type Result = ();
fn handle(&mut self, msg: ReceiveCIStatus, ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: ReceiveCIStatus, ctx: &mut Self::Context) -> Self::Result {
let log = self.log.clone(); logger(self.log.as_ref(), "start: ReceiveCIStatus");
logger(log.as_ref(), "start: ReceiveCIStatus");
let addr = ctx.address();
let (next, status) = msg.peel(); let (next, status) = msg.peel();
self.update_tui(RepoUpdate::ReceiveCIStatus {
status: status.clone(),
});
debug!(?status, "");
let graph_log = graph::log(&self.repo_details);
self.update_tui_log(graph_log.clone());
let addr = ctx.address();
let forge_alias = self.repo_details.forge.forge_alias().clone(); let forge_alias = self.repo_details.forge.forge_alias().clone();
let repo_alias = self.repo_details.repo_alias.clone(); let repo_alias = self.repo_details.repo_alias.clone();
let message_token = self.message_token; let message_token = self.message_token;
let sleep_duration = self.sleep_duration; let sleep_duration = self.sleep_duration;
let graph_log = graph::log(&self.repo_details);
self.update_tui_log(graph_log.clone());
self.update_tui(RepoUpdate::ReceiveCIStatus {
status: status.clone(),
});
debug!(?status, "");
match status { match status {
Status::Pass => { Status::Pass => {
do_send(&addr, AdvanceMain::new(next), self.log.as_ref()); do_send(&addr, AdvanceMain::new(next), self.log.as_ref());
@ -57,8 +56,9 @@ impl Handler<ReceiveCIStatus> for RepoActor {
commit: next, commit: next,
log: graph_log, log: graph_log,
}, },
log.as_ref(), self.log.as_ref(),
); );
let log = self.log.clone();
async move { async move {
debug!("sleeping before retrying..."); debug!("sleeping before retrying...");
logger(log.as_ref(), "before sleep"); logger(log.as_ref(), "before sleep");

View file

@ -79,7 +79,6 @@ impl Handler<ValidateRepo> for RepoActor {
self.update_tui_log(git_log); self.update_tui_log(git_log);
if next_is_valid && next != main { if next_is_valid && next != main {
info!("Checking CI"); info!("Checking CI");
self.update_tui(RepoUpdate::CheckingCI);
do_send(&ctx.address(), CheckCIStatus::new(next), self.log.as_ref()); do_send(&ctx.address(), CheckCIStatus::new(next), self.log.as_ref());
} else if next != dev { } else if next != dev {
info!("Advance next"); info!("Advance next");

View file

@ -99,8 +99,7 @@ impl RepoActor {
} }
fn update_tui_branches(&self) { fn update_tui_branches(&self) {
#[cfg(feature = "tui")] if cfg!(feature = "tui") {
{
use crate::server::actor::messages::RepoUpdate; use crate::server::actor::messages::RepoUpdate;
let Some(repo_config) = &self.repo_details.repo_config else { let Some(repo_config) = &self.repo_details.repo_config else {
return; return;
@ -112,16 +111,14 @@ impl RepoActor {
#[allow(unused_variables)] #[allow(unused_variables)]
fn update_tui_log(&self, log: git::graph::Log) { fn update_tui_log(&self, log: git::graph::Log) {
#[cfg(feature = "tui")] if cfg!(feature = "tui") {
{
self.update_tui(RepoUpdate::Log { log }); self.update_tui(RepoUpdate::Log { log });
} }
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn alert_tui(&self, alert: impl Into<String>) { fn alert_tui(&self, alert: impl Into<String>) {
#[cfg(feature = "tui")] if cfg!(feature = "tui") {
{
self.update_tui(RepoUpdate::Alert { self.update_tui(RepoUpdate::Alert {
alert: alert.into(), alert: alert.into(),
}); });
@ -130,8 +127,7 @@ impl RepoActor {
#[allow(unused_variables)] #[allow(unused_variables)]
fn update_tui(&self, repo_update: RepoUpdate) { fn update_tui(&self, repo_update: RepoUpdate) {
#[cfg(feature = "tui")] if cfg!(feature = "tui") {
{
let Some(server_addr) = &self.server_addr else { let Some(server_addr) = &self.server_addr else {
return; return;
}; };

View file

@ -7,7 +7,7 @@ fn advance_next_sut(
next: &Commit, next: &Commit,
main: &Commit, main: &Commit,
dev_commit_history: &[Commit], dev_commit_history: &[Commit],
repo_details: RepoDetails, repo_details: &RepoDetails,
repo_config: RepoConfig, repo_config: RepoConfig,
open_repository: &dyn OpenRepositoryLike, open_repository: &dyn OpenRepositoryLike,
message_token: MessageToken, message_token: MessageToken,
@ -42,7 +42,7 @@ mod when_at_dev {
&next, &next,
main, main,
dev_commit_history, dev_commit_history,
repo_details, &repo_details,
repo_config, repo_config,
&open_repository, &open_repository,
message_token, message_token,
@ -77,7 +77,7 @@ mod can_advance {
&next, &next,
main, main,
dev_commit_history, dev_commit_history,
repo_details, &repo_details,
repo_config, repo_config,
&open_repository, &open_repository,
message_token, message_token,
@ -108,7 +108,7 @@ mod can_advance {
&next, &next,
main, main,
dev_commit_history, dev_commit_history,
repo_details, &repo_details,
repo_config, repo_config,
&open_repository, &open_repository,
message_token, message_token,
@ -148,7 +148,7 @@ mod can_advance {
&next, &next,
main, main,
dev_commit_history, dev_commit_history,
repo_details, &repo_details,
repo_config, repo_config,
&open_repository, &open_repository,
message_token, message_token,
@ -180,7 +180,7 @@ mod can_advance {
&next, &next,
main, main,
dev_commit_history, dev_commit_history,
repo_details, &repo_details,
repo_config, repo_config,
&open_repository, &open_repository,
message_token, message_token,

View file

@ -69,6 +69,22 @@ pub fn a_name() -> String {
generate(5) generate(5)
} }
pub fn maybe_a_number() -> Option<u32> {
use rand::Rng;
let mut rng = rand::thread_rng();
if Rng::gen_ratio(&mut rng, 1, 2) {
Some(a_number())
} else {
None
}
}
pub fn a_number() -> u32 {
use rand::Rng;
let mut rng = rand::thread_rng();
rng.gen_range(0..100)
}
pub fn a_webhook_id() -> WebhookId { pub fn a_webhook_id() -> WebhookId {
WebhookId::new(a_name()) WebhookId::new(a_name())
} }
@ -89,6 +105,7 @@ pub fn a_forge_config() -> ForgeConfig {
a_name(), a_name(),
a_name(), a_name(),
a_name(), a_name(),
maybe_a_number(),
BTreeMap::default(), // no repos BTreeMap::default(), // no repos
) )
} }

View file

@ -25,6 +25,11 @@ async fn when_repo_config_should_fetch_then_push_then_revalidate() -> TestResult
.times(1) .times(1)
.in_sequence(&mut seq) .in_sequence(&mut seq)
.return_once(|_, _, _, _| Ok(())); .return_once(|_, _, _, _| Ok(()));
open_repository
.expect_fetch()
.times(1)
.in_sequence(&mut seq)
.return_once(|| Ok(()));
//when //when
let (addr, log) = when::start_actor_with_open_repository( let (addr, log) = when::start_actor_with_open_repository(
@ -70,6 +75,11 @@ async fn when_app_config_should_fetch_then_push_then_revalidate() -> TestResult
.times(1) .times(1)
.in_sequence(&mut seq) .in_sequence(&mut seq)
.return_once(|_, _, _, _| Ok(())); .return_once(|_, _, _, _| Ok(()));
open_repository
.expect_fetch()
.times(1)
.in_sequence(&mut seq)
.return_once(|| Ok(()));
//when //when
let (addr, log) = when::start_actor_with_open_repository( let (addr, log) = when::start_actor_with_open_repository(
@ -83,10 +93,6 @@ async fn when_app_config_should_fetch_then_push_then_revalidate() -> TestResult
//then //then
tracing::debug!(?log, ""); tracing::debug!(?log, "");
log.read().map_err(|e| e.to_string()).map(|l| { log.require_message_containing("send: ValidateRepo")?;
assert!(l
.iter()
.any(|message| message.contains("send: ValidateRepo")));
})?;
Ok(()) Ok(())
} }

View file

@ -28,6 +28,11 @@ async fn should_fetch_then_push_then_revalidate() -> TestResult {
.times(1) .times(1)
.in_sequence(&mut seq) .in_sequence(&mut seq)
.return_once(|_, _, _, _| Ok(())); .return_once(|_, _, _, _| Ok(()));
open_repository
.expect_fetch()
.times(1)
.in_sequence(&mut seq)
.return_once(|| Ok(()));
//when //when
let (addr, log) = when::start_actor_with_open_repository( let (addr, log) = when::start_actor_with_open_repository(

View file

@ -43,6 +43,10 @@ async fn should_open() -> TestResult {
//given //given
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let (mut open_repository, repo_details) = given::an_open_repository(&fs); let (mut open_repository, repo_details) = given::an_open_repository(&fs);
open_repository
.expect_fetch()
.times(1)
.return_once(|| Ok(()));
given::has_all_valid_remote_defaults(&mut open_repository, &repo_details); given::has_all_valid_remote_defaults(&mut open_repository, &repo_details);
// factory opens a repository // factory opens a repository
let mut repository_factory = MockRepositoryFactory::new(); let mut repository_factory = MockRepositoryFactory::new();
@ -79,6 +83,10 @@ async fn when_server_has_no_repo_config_should_send_load_from_repo() -> TestResu
//given //given
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let (mut open_repository, mut repo_details) = given::an_open_repository(&fs); let (mut open_repository, mut repo_details) = given::an_open_repository(&fs);
open_repository
.expect_fetch()
.times(1)
.return_once(|| Ok(()));
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
let _repo_config = repo_details.repo_config.take().unwrap(); let _repo_config = repo_details.repo_config.take().unwrap();
@ -106,6 +114,10 @@ async fn when_server_has_repo_config_should_send_register_webhook() -> TestResul
//given //given
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let (mut open_repository, repo_details) = given::an_open_repository(&fs); let (mut open_repository, repo_details) = given::an_open_repository(&fs);
open_repository
.expect_fetch()
.times(1)
.return_once(|| Ok(()));
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
given::has_all_valid_remote_defaults(&mut open_repository, &repo_details); given::has_all_valid_remote_defaults(&mut open_repository, &repo_details);
@ -129,6 +141,10 @@ async fn opened_repo_with_no_default_push_should_not_proceed() -> TestResult {
//given //given
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let (mut open_repository, repo_details) = given::an_open_repository(&fs); let (mut open_repository, repo_details) = given::an_open_repository(&fs);
open_repository
.expect_fetch()
.times(1)
.return_once(|| Ok(()));
given::has_remote_defaults( given::has_remote_defaults(
&mut open_repository, &mut open_repository,
@ -158,15 +174,10 @@ async fn opened_repo_with_no_default_fetch_should_not_proceed() -> TestResult {
//given //given
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let (mut open_repository, repo_details) = given::an_open_repository(&fs); let (mut open_repository, repo_details) = given::an_open_repository(&fs);
open_repository
given::has_remote_defaults( .expect_fetch()
&mut open_repository, .times(1)
HashMap::from([ .return_once(|| Err(git::fetch::Error::NoFetchRemoteFound));
(Direction::Push, repo_details.remote_url()),
(Direction::Fetch, None),
]),
);
let mut repository_factory = MockRepositoryFactory::new(); let mut repository_factory = MockRepositoryFactory::new();
expect::open_repository(&mut repository_factory, open_repository); expect::open_repository(&mut repository_factory, open_repository);
fs.dir_create(&repo_details.gitdir)?; fs.dir_create(&repo_details.gitdir)?;

View file

@ -3,4 +3,5 @@ mod receive_app_config;
mod receive_valid_app_config; mod receive_valid_app_config;
mod server_update; mod server_update;
mod shutdown; mod shutdown;
mod shutdown_trigger;
mod subscribe_updates; mod subscribe_updates;

View file

@ -0,0 +1,12 @@
//
use actix::Handler;
use crate::server::{actor::messages::ShutdownTrigger, ServerActor};
impl Handler<ShutdownTrigger> for ServerActor {
type Result = ();
fn handle(&mut self, msg: ShutdownTrigger, _ctx: &mut Self::Context) -> Self::Result {
self.shutdown_trigger.replace(msg.peel());
}
}

View file

@ -92,6 +92,8 @@ pub enum RepoUpdate {
}, },
RegisteredWebhook, RegisteredWebhook,
Opened, Opened,
NextUpdated,
MainUpdated,
} }
message!( message!(
@ -99,3 +101,13 @@ message!(
Recipient<ServerUpdate>, Recipient<ServerUpdate>,
"Subscribe to receive updates from the server" "Subscribe to receive updates from the server"
); );
/// Sends a channel to be used to shutdown the server
#[derive(Message, Constructor)]
#[rtype(result = "()")]
pub struct ShutdownTrigger(std::sync::mpsc::Sender<String>);
impl ShutdownTrigger {
pub fn peel(self) -> std::sync::mpsc::Sender<String> {
self.0
}
}

View file

@ -1,6 +1,6 @@
// //
use actix::prelude::*; use actix::prelude::*;
use messages::{ReceiveAppConfig, ServerUpdate}; use messages::{ReceiveAppConfig, ServerUpdate, Shutdown};
use tracing::error; use tracing::error;
#[cfg(test)] #[cfg(test)]
@ -58,6 +58,7 @@ pub struct ServerActor {
sleep_duration: std::time::Duration, sleep_duration: std::time::Duration,
repo_actors: BTreeMap<(ForgeAlias, RepoAlias), Addr<RepoActor>>, repo_actors: BTreeMap<(ForgeAlias, RepoAlias), Addr<RepoActor>>,
shutdown_trigger: Option<std::sync::mpsc::Sender<String>>,
subscribers: Vec<Recipient<ServerUpdate>>, subscribers: Vec<Recipient<ServerUpdate>>,
// testing // testing
@ -84,6 +85,7 @@ impl ServerActor {
net, net,
alerts, alerts,
repository_factory: repo, repository_factory: repo,
shutdown_trigger: None,
subscribers: Vec::default(), subscribers: Vec::default(),
sleep_duration, sleep_duration,
repo_actors: BTreeMap::new(), repo_actors: BTreeMap::new(),
@ -228,10 +230,15 @@ impl ServerActor {
} }
/// Attempts to gracefully shutdown the server before stopping the system. /// Attempts to gracefully shutdown the server before stopping the system.
fn abort(&self, ctx: &<Self as actix::Actor>::Context, message: impl Into<String>) { fn abort(&mut self, ctx: &<Self as actix::Actor>::Context, message: impl Into<String>) {
tracing::error!("Aborting: {}", message.into());
self.do_send(crate::server::actor::messages::Shutdown, ctx); self.do_send(crate::server::actor::messages::Shutdown, ctx);
System::current().stop_with_code(1); if let Some(t) = self.shutdown_trigger.take() {
let _ = t.send(message.into());
} else {
error!("{}", message.into());
self.do_send(Shutdown, ctx);
// System::current().stop_with_code(1);
}
} }
fn do_send<M>(&self, msg: M, ctx: &<Self as actix::Actor>::Context) fn do_send<M>(&self, msg: M, ctx: &<Self as actix::Actor>::Context)

View file

@ -6,6 +6,7 @@ mod tests;
use actix::prelude::*; use actix::prelude::*;
use actix_rt::signal; use actix_rt::signal;
use actor::messages::ShutdownTrigger;
use crate::{ use crate::{
alerts::{AlertsActor, History}, alerts::{AlertsActor, History},
@ -19,11 +20,11 @@ use git_next_core::git::RepositoryFactory;
use color_eyre::{eyre::Context, Result}; use color_eyre::{eyre::Context, Result};
use kxio::{fs::FileSystem, network::Network}; use kxio::{fs::FileSystem, network::Network};
use tracing::{error, info}; use tracing::info;
use std::{ use std::{
path::PathBuf, path::PathBuf,
sync::{atomic::Ordering, Arc, RwLock}, sync::{atomic::Ordering, mpsc::channel, Arc, RwLock},
time::Duration, time::Duration,
}; };
@ -45,6 +46,7 @@ pub fn init(fs: &FileSystem) -> Result<()> {
Ok(()) Ok(())
} }
#[allow(clippy::too_many_lines)]
pub fn start( pub fn start(
ui: bool, ui: bool,
fs: FileSystem, fs: FileSystem,
@ -61,8 +63,10 @@ pub fn start(
init_logging(); init_logging();
} }
let file_watcher_err_channel: Arc<RwLock<Option<anyhow::Error>>> = Arc::new(RwLock::new(None)); let shutdown_message_holder: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
let file_watcher_err_channel_exec = file_watcher_err_channel.clone(); let shutdown_message_holder_exec = shutdown_message_holder.clone();
let file_watcher_err_holder: Arc<RwLock<Option<anyhow::Error>>> = Arc::new(RwLock::new(None));
let file_watcher_err_holder_exec = file_watcher_err_holder.clone();
let execution = async move { let execution = async move {
info!("Starting Alert Dispatcher..."); info!("Starting Alert Dispatcher...");
let alerts_addr = AlertsActor::new(None, History::new(A_DAY), net.clone()).start(); let alerts_addr = AlertsActor::new(None, History::new(A_DAY), net.clone()).start();
@ -80,37 +84,58 @@ pub fn start(
server.do_send(crate::server::actor::messages::Shutdown); server.do_send(crate::server::actor::messages::Shutdown);
actix_rt::time::sleep(std::time::Duration::from_millis(10)).await; actix_rt::time::sleep(std::time::Duration::from_millis(10)).await;
System::current().stop(); System::current().stop();
let _ = file_watcher_err_channel_exec let _ = file_watcher_err_holder_exec
.write() .write()
.map(|mut o| o.replace(err)); .map(|mut o| o.replace(err));
return; return;
} }
}; };
let (tx_shutdown, rx_shutdown) = channel::<String>();
if ui { if ui {
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
{ {
use crate::server::actor::messages::SubscribeToUpdates; use crate::server::actor::messages::SubscribeToUpdates;
use crate::tui; use crate::tui;
use std::sync::mpsc::channel;
let (tx_shutdown, rx_shutdown) = channel::<()>(); let tui_addr = tui::Tui::new(tx_shutdown.clone()).start();
let tui_addr = tui::Tui::new(tx_shutdown).start();
server.do_send(SubscribeToUpdates::new(tui_addr.clone().recipient())); server.do_send(SubscribeToUpdates::new(tui_addr.clone().recipient()));
server.do_send(ShutdownTrigger::new(tx_shutdown));
server.do_send(FileUpdated); // update file after ui subscription in place server.do_send(FileUpdated); // update file after ui subscription in place
loop { loop {
let _ = tui_addr.send(tui::Tick).await; let _ = tui_addr.send(tui::Tick).await;
if rx_shutdown.try_recv().is_ok() { if let Ok(message) = rx_shutdown.try_recv() {
let _ = shutdown_message_holder_exec
.write()
.map(|mut o| o.replace(message));
break; break;
} }
actix_rt::time::sleep(Duration::from_millis(16)).await; actix_rt::time::sleep(Duration::from_millis(16)).await;
} }
} }
} else { } else {
server.do_send(ShutdownTrigger::new(tx_shutdown.clone()));
server.do_send(FileUpdated); server.do_send(FileUpdated);
info!("Server running - Press Ctrl-C to stop..."); info!("Server running - Press Ctrl-C to stop...");
let _ = signal::ctrl_c().await; tokio::select! {
info!("Ctrl-C received, shutting down..."); _r = signal::ctrl_c() => {
info!("Ctrl-C received, shutting down...");
}
_x = async move {
loop{
if let Ok(message) = rx_shutdown.try_recv() {
let _ = shutdown_message_holder_exec
.write()
.map(|mut o| o.replace(message));
break;
}
actix_rt::task::yield_now().await;
}
} => {
info!("signaled shutdown");
}
};
} }
// shutdown // shutdown
@ -124,13 +149,25 @@ pub fn start(
Arbiter::current().spawn(execution); Arbiter::current().spawn(execution);
system.run()?; system.run()?;
// check for error from server thread
#[allow(clippy::unwrap_used)]
if let Some(err) = &*shutdown_message_holder.read().unwrap() {
#[cfg(feature = "tui")]
if ui {
ratatui::restore();
}
if !err.is_empty() {
return Err(color_eyre::eyre::eyre!(format!("{err}")));
}
}
// check for error from file watcher thread // check for error from file watcher thread
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
if let Some(err) = &*file_watcher_err_channel.read().unwrap() { if let Some(err) = &*file_watcher_err_holder.read().unwrap() {
#[cfg(feature = "tui")]
if ui { if ui {
eprintln!("File Watcher: {err:?}"); ratatui::restore();
} }
error!(?err, "file watcher");
return Err(color_eyre::eyre::eyre!(format!("{err}"))); return Err(color_eyre::eyre::eyre!(format!("{err}")));
} }

View file

@ -6,7 +6,7 @@ use git_next_core::{
ApiToken, ForgeType, GitDir, Hostname, RepoBranches, RepoConfig, RepoConfigSource, RepoPath, ApiToken, ForgeType, GitDir, Hostname, RepoBranches, RepoConfig, RepoConfigSource, RepoPath,
StoragePathType, User, StoragePathType, User,
}; };
use secrecy::Secret; use secrecy::SecretString;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>; type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
@ -59,10 +59,13 @@ fn repo_details_find_default_push_remote_finds_correct_remote() -> Result<()> {
repo_details.forge = repo_details repo_details.forge = repo_details
.forge .forge
.with_user(User::new("git".to_string())) .with_user(User::new("git".to_string()))
.with_token(ApiToken::new(Secret::new(String::new()))) .with_token(ApiToken::new(SecretString::from(String::new())))
.with_hostname(Hostname::new("git.kemitix.net")); .with_hostname(Hostname::new("git.kemitix.net"));
repo_details.repo_path = RepoPath::new("kemitix/git-next".to_string()); repo_details.repo_path = RepoPath::new("kemitix/git-next".to_string());
let open_repository = git::repository::factory::real().open(&repo_details)?; let Ok(open_repository) = git::repository::factory::real().open(&repo_details) else {
// .git directory may not be present on dev environment
return Ok(());
};
let_assert!( let_assert!(
Some(found_git_remote) = open_repository.find_default_remote(Direction::Push), Some(found_git_remote) = open_repository.find_default_remote(Direction::Push),
"Default Push Remote not found" "Default Push Remote not found"
@ -92,13 +95,13 @@ fn gitdir_validate_should_pass_a_valid_git_repo() -> Result<()> {
repo_details.forge = repo_details repo_details.forge = repo_details
.forge .forge
.with_user(User::new("git".to_string())) .with_user(User::new("git".to_string()))
.with_token(ApiToken::new(Secret::new(String::new()))) .with_token(ApiToken::new(SecretString::from(String::new())))
.with_hostname(Hostname::new("git.kemitix.net")); .with_hostname(Hostname::new("git.kemitix.net"));
tracing::debug!("opening..."); tracing::debug!("opening...");
let_assert!( let Ok(repository) = git::repository::factory::real().open(&repo_details) else {
Ok(repository) = git::repository::factory::real().open(&repo_details), // .git directory may not be present on dev environment
"open repository" return Ok(());
); };
tracing::debug!("open okay"); tracing::debug!("open okay");
tracing::info!(?repository, "FOO"); tracing::info!(?repository, "FOO");
tracing::info!(?repo_details, "BAR"); tracing::info!(?repo_details, "BAR");
@ -108,11 +111,13 @@ fn gitdir_validate_should_pass_a_valid_git_repo() -> Result<()> {
} }
#[test] #[test]
fn gitdir_validate_should_fail_a_git_repo_with_wrong_remote() -> Result<()> { fn gitdir_validate_should_fail_a_git_repo_with_wrong_remote() {
let_assert!( let_assert!(
Ok(cli_crate_dir) = std::env::current_dir().map_err(git::validation::remotes::Error::Io) Ok(cli_crate_dir) = std::env::current_dir().map_err(git::validation::remotes::Error::Io)
); );
eprintln!("cli_crate_dir: {cli_crate_dir:?}");
let_assert!(Some(Some(root)) = cli_crate_dir.parent().map(|p| p.parent())); let_assert!(Some(Some(root)) = cli_crate_dir.parent().map(|p| p.parent()));
eprintln!("root: {root:?}");
let mut repo_details = git::repo_details( let mut repo_details = git::repo_details(
1, 1,
git::Generation::default(), git::Generation::default(),
@ -124,16 +129,17 @@ fn gitdir_validate_should_fail_a_git_repo_with_wrong_remote() -> Result<()> {
repo_details.forge = repo_details repo_details.forge = repo_details
.forge .forge
.with_user(User::new("git".to_string())) .with_user(User::new("git".to_string()))
.with_token(ApiToken::new(Secret::new(String::new()))) .with_token(ApiToken::new(SecretString::from(String::new())))
.with_hostname(Hostname::new("git.kemitix.net")); .with_hostname(Hostname::new("git.kemitix.net"));
let repository = git::repository::factory::real().open(&repo_details)?; let Ok(repository) = git::repository::factory::real().open(&repo_details) else {
// .git directory may not be present on dev environment
return;
};
let mut repo_details = repo_details.clone(); let mut repo_details = repo_details.clone();
repo_details.forge = repo_details repo_details.forge = repo_details
.forge .forge
.with_hostname(Hostname::new("code.kemitix.net")); .with_hostname(Hostname::new("code.kemitix.net"));
let_assert!(Err(_) = validate_default_remotes(&*repository, &repo_details)); let_assert!(Err(_) = validate_default_remotes(&*repository, &repo_details));
Ok(())
} }
#[test] #[test]

View file

@ -8,6 +8,19 @@ The build `git-next` with the Terminal UI use: `cargo install git-next --feature
To run `git-next` with the Terminal UI use: `git-next server start --ui` To run `git-next` with the Terminal UI use: `git-next server start --ui`
### Docker
If using the docker image you will need to create a directory to mount that contains the
`git-next-server.toml` file. Mount this directory as `/app`. In the example below we use
the current directory for this.
If you want to persist the clones of your monitored repos then point `storage.path` in
`git-next-server.toml` to the the directory `/app`, (e.g. `path = "/app/data"`).
Map the port your webhook notifications are arriving on to the port specified in `listen.http.port`.
`docker run -it -p "8080:8092" -v .:/app/ git.kemitix.net/kemitix/git-next:latest server start --ui`
## logs ## logs
When the Terminal UI is enabled via the `--ui` parameter, logs are written to the file: When the Terminal UI is enabled via the `--ui` parameter, logs are written to the file:

View file

@ -34,6 +34,7 @@ impl Handler<ServerUpdate> for Tui {
let Some(repo_state) = forge_state.repos.get_mut(&repo_alias) else { let Some(repo_state) = forge_state.repos.get_mut(&repo_alias) else {
return; return;
}; };
repo_state.clear_alert();
match repo_update { match repo_update {
RepoUpdate::Branches { branches } => { RepoUpdate::Branches { branches } => {
repo_state.update_branches(branches); repo_state.update_branches(branches);
@ -57,10 +58,16 @@ impl Handler<ServerUpdate> for Tui {
repo_state repo_state
.update_message(format!("advancing next to {commit}"), ACTING); .update_message(format!("advancing next to {commit}"), ACTING);
} }
RepoUpdate::NextUpdated => {
repo_state.update_message("next updated - pause while CI starts", OKAY);
}
RepoUpdate::AdvancingMain { commit } => { RepoUpdate::AdvancingMain { commit } => {
repo_state repo_state
.update_message(format!("advancing main to {commit}"), ACTING); .update_message(format!("advancing main to {commit}"), ACTING);
} }
RepoUpdate::MainUpdated => {
repo_state.update_message("main updated", OKAY);
}
RepoUpdate::Opening => { RepoUpdate::Opening => {
repo_state.update_message("opening...", PREP); repo_state.update_message("opening...", PREP);
} }

View file

@ -3,6 +3,9 @@ mod handlers;
pub mod messages; pub mod messages;
mod model; mod model;
#[cfg(test)]
mod tests;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use actix::{Actor, ActorContext as _, Context}; use actix::{Actor, ActorContext as _, Context};
@ -18,7 +21,7 @@ use tui_scrollview::ScrollViewState;
#[derive(Debug)] #[derive(Debug)]
pub struct Tui { pub struct Tui {
terminal: Option<DefaultTerminal>, terminal: Option<DefaultTerminal>,
signal_shutdown: Sender<()>, signal_shutdown: Sender<String>,
pub state: State, pub state: State,
scroll_view_state: ScrollViewState, scroll_view_state: ScrollViewState,
} }
@ -33,7 +36,7 @@ impl Actor for Tui {
} }
} }
impl Tui { impl Tui {
pub fn new(signal_shutdown: Sender<()>) -> Self { pub fn new(signal_shutdown: Sender<String>) -> Self {
Self { Self {
terminal: None, terminal: None,
signal_shutdown, signal_shutdown,
@ -68,7 +71,7 @@ impl Tui {
match key.code { match key.code {
KeyCode::Char('q') => { KeyCode::Char('q') => {
ctx.stop(); ctx.stop();
if let Err(err) = self.signal_shutdown.send(()) { if let Err(err) = self.signal_shutdown.send(String::new()) {
tracing::error!(?err, "Failed to signal shutdown"); tracing::error!(?err, "Failed to signal shutdown");
} }
} }

View file

@ -5,7 +5,7 @@ use ratatui::{
style::{Color, Style, Stylize as _}, style::{Color, Style, Stylize as _},
symbols::border, symbols::border,
text::{Line, Span}, text::{Line, Span},
widgets::{block::Title, Block, Paragraph, StatefulWidget, Widget}, widgets::{Block, Paragraph, StatefulWidget, Widget},
}; };
use git_next_core::{ use git_next_core::{
@ -51,6 +51,10 @@ impl State {
} }
} }
fn time() -> String {
chrono::Local::now().format("%H:%M").to_string()
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum ServerState { pub enum ServerState {
/// UI has started but has no information on the state of the server /// UI has started but has no information on the state of the server
@ -278,8 +282,11 @@ impl RepoState {
#[tracing::instrument] #[tracing::instrument]
pub fn clear_alert(&mut self) { pub fn clear_alert(&mut self) {
match self { match self {
Self::Identified { .. } | Self::Configured { .. } => (), Self::Identified { alert, .. }
Self::Ready { alert, .. } => *alert = None, | Self::Configured { alert, .. }
| Self::Ready { alert, .. } => {
*alert = None;
}
} }
} }
@ -353,16 +360,19 @@ impl StatefulWidget for &State {
Self: Sized, Self: Sized,
{ {
let block = Block::bordered() let block = Block::bordered()
.title(Title::from(" Git-Next ".bold()).alignment(Alignment::Center)) .title_top(
.title( Line::from(format!(" Git-Next v{} ", clap::crate_version!()).bold())
Title::from(Line::from(vec![ .alignment(Alignment::Center),
)
.title_bottom(
Line::from(vec![
" [q]uit ".into(), " [q]uit ".into(),
self.beating_heart().into(), self.beating_heart().into(),
" ".into(), " ".into(),
])) ])
.alignment(Alignment::Center) .alignment(Alignment::Center),
.position(ratatui::widgets::block::Position::Bottom),
) )
.title_bottom(Line::from(format!(" {} ", time())).alignment(Alignment::Right))
.border_set(border::THICK); .border_set(border::THICK);
let interior = block.inner(area); let interior = block.inner(area);
block.render(area, buf); block.render(area, buf);

View file

@ -0,0 +1,99 @@
//
mod model {
mod repo_state {
use git_next_core::{git::graph::Log, RepoBranches};
use ratatui::style::Style;
use crate::{
repo::tests::given,
tui::actor::{RepoMessage, RepoState, ViewState},
};
type Alert = Option<String>;
fn identified_with_alert(alert: Alert) -> RepoState {
RepoState::Identified {
repo_alias: given::a_repo_alias(),
message: RepoMessage::builder()
.text(given::a_name())
.style(Style::default())
.build(),
alert,
}
}
fn configured_with_alert(alert: Alert) -> RepoState {
RepoState::Configured {
repo_alias: given::a_repo_alias(),
message: RepoMessage::builder()
.text(given::a_name())
.style(Style::default())
.build(),
alert,
branches: RepoBranches::new(String::new(), String::new(), String::new()),
log: Log::default(),
}
}
fn ready_with_alert(alert: Alert) -> RepoState {
RepoState::Ready {
repo_alias: given::a_repo_alias(),
message: RepoMessage::builder()
.text(given::a_name())
.style(Style::default())
.build(),
alert,
branches: RepoBranches::new(String::new(), String::new(), String::new()),
log: Log::default(),
view_state: ViewState::default(),
main: given::a_commit(),
next: given::a_commit(),
dev: given::a_commit(),
}
}
#[rstest::rstest]
#[case(identified_with_alert(None))]
#[case(configured_with_alert(None))]
#[case(ready_with_alert(None))]
fn none_alert_remains_none(#[case] mut repo_state: RepoState) {
// given
match &repo_state {
RepoState::Identified { alert, .. }
| RepoState::Configured { alert, .. }
| RepoState::Ready { alert, .. } => {
assert!(alert.is_none(), "should be none at start");
}
}
// when
repo_state.clear_alert();
// then
match &repo_state {
RepoState::Identified { alert, .. }
| RepoState::Configured { alert, .. }
| RepoState::Ready { alert, .. } => assert!(alert.is_none(), "should remain none"),
}
}
#[rstest::rstest]
#[case(identified_with_alert(Some(String::new())))]
#[case(configured_with_alert(Some(String::new())))]
#[case(ready_with_alert(Some(String::new())))]
fn some_alert_becomes_none(#[case] mut repo_state: RepoState) {
// given
match &repo_state {
RepoState::Identified { alert, .. }
| RepoState::Configured { alert, .. }
| RepoState::Ready { alert, .. } => {
assert!(alert.is_some(), "should be some at start");
}
}
// when
repo_state.clear_alert();
// then
match &repo_state {
RepoState::Identified { alert, .. }
| RepoState::Configured { alert, .. }
| RepoState::Ready { alert, .. } => assert!(alert.is_none(), "should become none"),
}
}
}
}

View file

@ -5,7 +5,8 @@ use git_next_core::{ForgeAlias, RepoAlias};
use ratatui::{ use ratatui::{
buffer::Buffer, buffer::Buffer,
layout::{Alignment, Direction, Layout, Rect}, layout::{Alignment, Direction, Layout, Rect},
widgets::{block::Title, Block, Widget}, text::Line,
widgets::{Block, Widget},
}; };
use crate::tui::{ use crate::tui::{
@ -31,8 +32,8 @@ impl<'a> Widget for ExpandedForgeWidget<'a> {
where where
Self: Sized, Self: Sized,
{ {
let block = Block::default().title( let block = Block::default().title_top(
Title::from(format!(" forge: {} ", self.forge_alias)).alignment(Alignment::Left), Line::from(format!(" forge: {} ", self.forge_alias)).alignment(Alignment::Left),
); );
let children = self.children(); let children = self.children();
let layout = Layout::default() let layout = Layout::default()

View file

@ -54,8 +54,7 @@ impl<'a> Identity<'a> {
let mut spans = vec![" ".into()]; let mut spans = vec![" ".into()];
match alert { match alert {
None => spans.push( None => spans.push(
Span::from(self.repo_alias.to_string()) Span::from(self.repo_alias.to_string()).style(Style::default().fg(Color::Cyan)),
.style(Style::default().fg(Color::Cyan).bg(Color::Black)),
), ),
Some(alert) => { Some(alert) => {
spans.push( spans.push(

View file

@ -2,10 +2,10 @@
/// `ForgeJo`: <https://{hostname}/user/settings/applications> /// `ForgeJo`: <https://{hostname}/user/settings/applications>
/// `Github`: <https://github.com/settings/tokens> /// `Github`: <https://github.com/settings/tokens>
#[derive(Clone, Debug, derive_more::Constructor)] #[derive(Clone, Debug, derive_more::Constructor)]
pub struct ApiToken(secrecy::Secret<String>); pub struct ApiToken(secrecy::SecretString);
/// The API Token is in effect a password, so it must be explicitly exposed to access its value /// The API Token is in effect a password, so it must be explicitly exposed to access its value
impl secrecy::ExposeSecret<String> for ApiToken { impl secrecy::ExposeSecret<str> for ApiToken {
fn expose_secret(&self) -> &String { fn expose_secret(&self) -> &str {
self.0.expose_secret() self.0.expose_secret()
} }
} }

View file

@ -0,0 +1,4 @@
//
use crate::newtype;
newtype!(CommitCount, u32, Default, "A number of commits");

View file

@ -11,6 +11,7 @@ pub fn forge_details(n: u32, forge_type: ForgeType) -> ForgeDetails {
hostname(n), hostname(n),
user(n), user(n),
api_token(n), api_token(n),
None,
) )
} }

View file

@ -2,6 +2,8 @@ use std::collections::BTreeMap;
use crate::config::{ApiToken, ForgeType, Hostname, RepoAlias, ServerRepoConfig, User}; use crate::config::{ApiToken, ForgeType, Hostname, RepoAlias, ServerRepoConfig, User};
use super::CommitCount;
/// Defines a Forge to connect to /// Defines a Forge to connect to
/// Maps from `git-next-server.toml` at `forge.{forge}` /// Maps from `git-next-server.toml` at `forge.{forge}`
#[derive( #[derive(
@ -22,6 +24,7 @@ pub struct ForgeConfig {
hostname: String, hostname: String,
user: String, user: String,
token: String, token: String,
max_dev_commits: Option<u32>,
repos: BTreeMap<String, ServerRepoConfig>, repos: BTreeMap<String, ServerRepoConfig>,
} }
impl ForgeConfig { impl ForgeConfig {
@ -41,6 +44,10 @@ impl ForgeConfig {
ApiToken::new(self.token.clone().into()) ApiToken::new(self.token.clone().into())
} }
pub(crate) fn max_dev_commits(&self) -> Option<CommitCount> {
self.max_dev_commits.map(CommitCount::from)
}
pub fn repos(&self) -> impl Iterator<Item = (RepoAlias, &ServerRepoConfig)> { pub fn repos(&self) -> impl Iterator<Item = (RepoAlias, &ServerRepoConfig)> {
self.repos self.repos
.iter() .iter()

View file

@ -1,5 +1,7 @@
use crate::config::{ApiToken, ForgeAlias, ForgeConfig, ForgeType, Hostname, User}; use crate::config::{ApiToken, ForgeAlias, ForgeConfig, ForgeType, Hostname, User};
use super::CommitCount;
/// The derived information about a Forge, used to create interactions with it /// The derived information about a Forge, used to create interactions with it
#[derive(Clone, Default, Debug, derive_more::Constructor, derive_with::With)] #[derive(Clone, Default, Debug, derive_more::Constructor, derive_with::With)]
pub struct ForgeDetails { pub struct ForgeDetails {
@ -8,8 +10,7 @@ pub struct ForgeDetails {
hostname: Hostname, hostname: Hostname,
user: User, user: User,
token: ApiToken, token: ApiToken,
// API Token max_dev_commits: Option<CommitCount>,
// Private SSH Key Path
} }
impl ForgeDetails { impl ForgeDetails {
#[must_use] #[must_use]
@ -35,15 +36,21 @@ impl ForgeDetails {
pub const fn token(&self) -> &ApiToken { pub const fn token(&self) -> &ApiToken {
&self.token &self.token
} }
#[must_use]
pub const fn max_dev_commits(&self) -> Option<&CommitCount> {
self.max_dev_commits.as_ref()
}
} }
impl From<(&ForgeAlias, &ForgeConfig)> for ForgeDetails { impl From<(&ForgeAlias, &ForgeConfig)> for ForgeDetails {
fn from(forge: (&ForgeAlias, &ForgeConfig)) -> Self { fn from((forge_alias, forge_config): (&ForgeAlias, &ForgeConfig)) -> Self {
Self { Self {
forge_alias: forge.0.clone(), forge_alias: forge_alias.clone(),
forge_type: forge.1.forge_type(), forge_type: forge_config.forge_type(),
hostname: forge.1.hostname(), hostname: forge_config.hostname(),
user: forge.1.user(), user: forge_config.user(),
token: forge.1.token(), token: forge_config.token(),
max_dev_commits: forge_config.max_dev_commits(),
} }
} }
} }

View file

@ -1,6 +1,7 @@
// //
mod api_token; mod api_token;
mod branch_name; mod branch_name;
mod commit_count;
pub mod common; pub mod common;
mod forge_alias; mod forge_alias;
mod forge_config; mod forge_config;
@ -26,6 +27,7 @@ mod tests;
pub use api_token::ApiToken; pub use api_token::ApiToken;
pub use branch_name::BranchName; pub use branch_name::BranchName;
pub use commit_count::CommitCount;
pub use forge_alias::ForgeAlias; pub use forge_alias::ForgeAlias;
#[allow(clippy::module_name_repetitions)] #[allow(clippy::module_name_repetitions)]
pub use forge_config::ForgeConfig; pub use forge_config::ForgeConfig;

View file

@ -10,7 +10,7 @@ use std::{
use derive_more::{Constructor, Display}; use derive_more::{Constructor, Display};
use kxio::fs::FileSystem; use kxio::fs::FileSystem;
use secrecy::Secret; use secrecy::SecretString;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::info; use tracing::info;
@ -242,8 +242,11 @@ impl Shout {
self.webhook.clone().map(|x| x.url) self.webhook.clone().map(|x| x.url)
} }
pub fn webhook_secret(&self) -> Option<Secret<String>> { pub fn webhook_secret(&self) -> Option<SecretString> {
self.webhook.clone().map(|x| x.secret).map(Secret::new) self.webhook
.clone()
.map(|x| x.secret)
.map(SecretString::from)
} }
#[must_use] #[must_use]
@ -278,8 +281,8 @@ impl OutboundWebhook {
self.url.as_ref() self.url.as_ref()
} }
#[must_use] #[must_use]
pub fn secret(&self) -> Secret<String> { pub fn secret(&self) -> SecretString {
Secret::new(self.secret.clone()) SecretString::from(self.secret.clone())
} }
} }

View file

@ -149,6 +149,8 @@ mod repo_config {
} }
} }
mod forge_config { mod forge_config {
use given::maybe_a_number;
use super::*; use super::*;
#[test] #[test]
@ -166,7 +168,7 @@ mod forge_config {
let mut repos = BTreeMap::new(); let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone()); repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name.clone(), blue.clone()); repos.insert(blue_name.clone(), blue.clone());
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos); let fc = ForgeConfig::new(forge_type, hostname, user, token, maybe_a_number(), repos);
let returned_repos = fc.repos().collect::<Vec<_>>(); let returned_repos = fc.repos().collect::<Vec<_>>();
@ -186,7 +188,7 @@ mod forge_config {
let user = given::a_name(); let user = given::a_name();
let token = given::a_name(); let token = given::a_name();
let repos = BTreeMap::new(); let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos); let fc = ForgeConfig::new(forge_type, hostname, user, token, maybe_a_number(), repos);
assert_eq!(fc.forge_type(), ForgeType::MockForge); assert_eq!(fc.forge_type(), ForgeType::MockForge);
} }
@ -197,7 +199,14 @@ mod forge_config {
let user = given::a_name(); let user = given::a_name();
let token = given::a_name(); let token = given::a_name();
let repos = BTreeMap::new(); let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname.clone(), user, token, repos); let fc = ForgeConfig::new(
forge_type,
hostname.clone(),
user,
token,
maybe_a_number(),
repos,
);
assert_eq!(fc.hostname(), Hostname::new(hostname)); assert_eq!(fc.hostname(), Hostname::new(hostname));
} }
@ -208,7 +217,14 @@ mod forge_config {
let user = given::a_name(); let user = given::a_name();
let token = given::a_name(); let token = given::a_name();
let repos = BTreeMap::new(); let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user.clone(), token, repos); let fc = ForgeConfig::new(
forge_type,
hostname,
user.clone(),
token,
maybe_a_number(),
repos,
);
assert_eq!(fc.user(), User::new(user)); assert_eq!(fc.user(), User::new(user));
} }
@ -219,7 +235,14 @@ mod forge_config {
let user = given::a_name(); let user = given::a_name();
let token = given::a_name(); let token = given::a_name();
let repos = BTreeMap::new(); let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user, token.clone(), repos); let fc = ForgeConfig::new(
forge_type,
hostname,
user,
token.clone(),
maybe_a_number(),
repos,
);
assert_eq!(fc.token().expose_secret(), token.as_str()); assert_eq!(fc.token().expose_secret(), token.as_str());
} }
@ -237,7 +260,7 @@ mod forge_config {
let mut repos = BTreeMap::new(); let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone()); repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name, blue); repos.insert(blue_name, blue);
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos); let fc = ForgeConfig::new(forge_type, hostname, user, token, maybe_a_number(), repos);
let returned_repo = fc.get_repo(red_name.as_str()); let returned_repo = fc.get_repo(red_name.as_str());
@ -255,8 +278,14 @@ mod forge_details {
let user = User::new(given::a_name()); let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into()); let token = ApiToken::new(given::a_name().into());
let forge_alias = ForgeAlias::new(given::a_name()); let forge_alias = ForgeAlias::new(given::a_name());
let forge_details = let forge_details = ForgeDetails::new(
ForgeDetails::new(forge_alias.clone(), forge_type, hostname, user, token); forge_alias.clone(),
forge_type,
hostname,
user,
token,
given::maybe_a_number().map(CommitCount::from),
);
let result = forge_details.forge_alias(); let result = forge_details.forge_alias();
@ -269,7 +298,14 @@ mod forge_details {
let user = User::new(given::a_name()); let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into()); let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name()); let forge_name = ForgeAlias::new(given::a_name());
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token); let forge_details = ForgeDetails::new(
forge_name,
forge_type,
hostname,
user,
token,
given::maybe_a_number().map(CommitCount::from),
);
let result = forge_details.forge_type(); let result = forge_details.forge_type();
@ -282,8 +318,14 @@ mod forge_details {
let user = User::new(given::a_name()); let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into()); let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name()); let forge_name = ForgeAlias::new(given::a_name());
let forge_details = let forge_details = ForgeDetails::new(
ForgeDetails::new(forge_name, forge_type, hostname.clone(), user, token); forge_name,
forge_type,
hostname.clone(),
user,
token,
given::maybe_a_number().map(CommitCount::from),
);
let result = forge_details.hostname(); let result = forge_details.hostname();
@ -296,8 +338,14 @@ mod forge_details {
let user = User::new(given::a_name()); let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into()); let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name()); let forge_name = ForgeAlias::new(given::a_name());
let forge_details = let forge_details = ForgeDetails::new(
ForgeDetails::new(forge_name, forge_type, hostname, user.clone(), token); forge_name,
forge_type,
hostname,
user.clone(),
token,
given::maybe_a_number().map(CommitCount::from),
);
let result = forge_details.user(); let result = forge_details.user();
@ -310,8 +358,14 @@ mod forge_details {
let user = User::new(given::a_name()); let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into()); let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name()); let forge_name = ForgeAlias::new(given::a_name());
let forge_details = let forge_details = ForgeDetails::new(
ForgeDetails::new(forge_name, forge_type, hostname, user, token.clone()); forge_name,
forge_type,
hostname,
user,
token.clone(),
given::maybe_a_number().map(CommitCount::from),
);
let result = forge_details.token(); let result = forge_details.token();
@ -325,7 +379,14 @@ mod forge_details {
let user = User::new(given::a_name()); let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into()); let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name()); let forge_name = ForgeAlias::new(given::a_name());
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token); let forge_details = ForgeDetails::new(
forge_name,
forge_type,
hostname,
user,
token,
given::maybe_a_number().map(CommitCount::from),
);
let result = forge_details.with_hostname(other_hostname.clone()); let result = forge_details.with_hostname(other_hostname.clone());
@ -340,12 +401,14 @@ mod forge_details {
let user = User::new(user_value.clone()); let user = User::new(user_value.clone());
let token_value = given::a_name(); let token_value = given::a_name();
let token = ApiToken::new(token_value.clone().into()); let token = ApiToken::new(token_value.clone().into());
let max_dev_commits = given::maybe_a_number();
let forge_alias = ForgeAlias::new(given::a_name()); let forge_alias = ForgeAlias::new(given::a_name());
let forge_config = ForgeConfig::new( let forge_config = ForgeConfig::new(
forge_type, forge_type,
hostname_value, hostname_value,
user_value, user_value,
token_value, token_value,
max_dev_commits,
BTreeMap::new(), BTreeMap::new(),
); );
@ -355,6 +418,12 @@ mod forge_details {
assert_eq!(forge_details.hostname(), &hostname); assert_eq!(forge_details.hostname(), &hostname);
assert_eq!(forge_details.user(), &user); assert_eq!(forge_details.user(), &user);
assert_eq!(forge_details.token().expose_secret(), token.expose_secret()); assert_eq!(forge_details.token().expose_secret(), token.expose_secret());
assert_eq!(
forge_details
.max_dev_commits()
.map(|commit_count| commit_count.clone().peel()),
max_dev_commits
);
} }
} }
mod forge_name { mod forge_name {
@ -470,7 +539,7 @@ mod server {
let shout_webhook_url = shout.webhook_url().unwrap_or_default(); let shout_webhook_url = shout.webhook_url().unwrap_or_default();
let shout_webhook_secret = shout let shout_webhook_secret = shout
.webhook_secret() .webhook_secret()
.map(|secret| secret.expose_secret().clone()) .map(|secret| secret.expose_secret().to_string())
.unwrap_or_default(); .unwrap_or_default();
let_assert!(Some(shout_email) = shout.email()); let_assert!(Some(shout_email) = shout.email());
let shout_email_from = shout_email.from(); let shout_email_from = shout_email.from();
@ -492,6 +561,12 @@ mod server {
let forge_hostname = forge_default.hostname(); let forge_hostname = forge_default.hostname();
let forge_user = forge_default.user(); let forge_user = forge_default.user();
let forge_token = forge_default.token().expose_secret().to_string(); let forge_token = forge_default.token().expose_secret().to_string();
let optional_max_dev_commits = forge_default
.max_dev_commits()
.map(CommitCount::peel)
.map_or_else(String::new, |max_dev_commits| {
format!("max_dev_commits = {max_dev_commits}")
});
let mut repos: Vec<String> = vec![]; let mut repos: Vec<String> = vec![];
for (repo_alias, server_repo_config) in forge_default.repos() { for (repo_alias, server_repo_config) in forge_default.repos() {
let repo_path = server_repo_config.repo(); let repo_path = server_repo_config.repo();
@ -542,6 +617,7 @@ forge_type = "{forge_type}"
hostname = "{forge_hostname}" hostname = "{forge_hostname}"
user = "{forge_user}" user = "{forge_user}"
token = "{forge_token}" token = "{forge_token}"
{optional_max_dev_commits}
[forge.{forge_alias}.repos] [forge.{forge_alias}.repos]
{repos} {repos}
@ -726,6 +802,23 @@ mod given {
} }
generate(5) generate(5)
} }
pub fn a_number() -> u32 {
use rand::Rng;
let mut rng = rand::thread_rng();
rng.gen_range(0..100)
}
pub fn maybe_a_number() -> Option<u32> {
use rand::Rng;
let mut rng = rand::thread_rng();
if Rng::gen_ratio(&mut rng, 1, 2) {
Some(a_number())
} else {
None
}
}
pub fn an_app_config() -> AppConfig { pub fn an_app_config() -> AppConfig {
AppConfig::new( AppConfig::new(
a_listen(), a_listen(),
@ -785,6 +878,7 @@ mod given {
a_name(), // hostname a_name(), // hostname
a_name(), // user a_name(), // user
a_name(), // token a_name(), // token
maybe_a_number(),
some_server_repo_configs(), some_server_repo_configs(),
) )
} }

View file

@ -3,6 +3,9 @@ pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("io")]
Io(#[from] std::io::Error),
#[error("unable to open repo: {0}")] #[error("unable to open repo: {0}")]
UnableToOpenRepo(String), UnableToOpenRepo(String),

View file

@ -11,7 +11,7 @@ use crate::{
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use secrecy::{ExposeSecret, Secret}; use secrecy::{ExposeSecret, SecretString};
use tracing::instrument; use tracing::instrument;
/// The derived information about a repo, used to interact with it /// The derived information about a repo, used to interact with it
@ -49,10 +49,11 @@ impl RepoDetails {
forge_config.hostname(), forge_config.hostname(),
forge_config.user(), forge_config.user(),
forge_config.token(), forge_config.token(),
forge_config.max_dev_commits(),
), ),
} }
} }
pub(crate) fn origin(&self) -> secrecy::Secret<String> { pub(crate) fn origin(&self) -> secrecy::SecretString {
let repo_details = self; let repo_details = self;
let user = &repo_details.forge.user(); let user = &repo_details.forge.user();
let hostname = &repo_details.forge.hostname(); let hostname = &repo_details.forge.hostname();
@ -77,7 +78,7 @@ impl RepoDetails {
} }
// url is a secret as it contains auth token // url is a secret as it contains auth token
pub(crate) fn url(&self) -> Secret<String> { pub(crate) fn url(&self) -> SecretString {
let user = self.forge.user(); let user = self.forge.user();
let token = self.forge.token().expose_secret(); let token = self.forge.token().expose_secret();
let auth_delim = if token.is_empty() { "" } else { ":" }; let auth_delim = if token.is_empty() { "" } else { ":" };
@ -94,12 +95,7 @@ impl RepoDetails {
|> GitDir::pathbuf |> GitDir::pathbuf
|> gix::ThreadSafeRepository::open |> gix::ThreadSafeRepository::open
}?; }?;
let repo = pike! { let repo = RealOpenRepository::new(Arc::new(RwLock::new(gix_repo)), self.forge.clone());
gix_repo
|> RwLock::new
|> Arc::new
|> RealOpenRepository::new
};
Ok(repo) Ok(repo)
} }

View file

@ -60,13 +60,14 @@ impl RepositoryFactory for RealRepositoryFactory {
fn git_clone(&self, repo_details: &RepoDetails) -> Result<Box<dyn OpenRepositoryLike>> { fn git_clone(&self, repo_details: &RepoDetails) -> Result<Box<dyn OpenRepositoryLike>> {
tracing::info!("creating"); tracing::info!("creating");
let (gix_repo, _outcome) = gix::prepare_clone_bare( let (gix_repo, _outcome) =
repo_details.origin().expose_secret().as_str(), gix::prepare_clone_bare(repo_details.origin().expose_secret(), &*repo_details.gitdir)?
&*repo_details.gitdir, .fetch_only(gix::progress::Discard, &AtomicBool::new(false))?;
)?
.fetch_only(gix::progress::Discard, &AtomicBool::new(false))?;
tracing::info!("created"); tracing::info!("created");
let repo = RealOpenRepository::new(Arc::new(RwLock::new(gix_repo.into()))); let repo = RealOpenRepository::new(
Arc::new(RwLock::new(gix_repo.into())),
repo_details.forge.clone(),
);
Ok(Box::new(repo)) Ok(Box::new(repo))
} }

View file

@ -1,6 +1,7 @@
// //
use crate::{ use crate::{
git::{ git::{
self,
repository::{ repository::{
open::{OpenRepository, OpenRepositoryLike}, open::{OpenRepository, OpenRepositoryLike},
test::TestRepository, test::TestRepository,
@ -31,8 +32,11 @@ pub enum Repository {
} }
#[cfg(test)] #[cfg(test)]
pub(crate) const fn test(fs: kxio::fs::FileSystem) -> TestRepository { pub(crate) const fn test(
TestRepository::new(fs, vec![], vec![]) fs: kxio::fs::FileSystem,
forge_details: crate::ForgeDetails,
) -> TestRepository {
TestRepository::new(fs, vec![], vec![], forge_details)
} }
/// Opens a repository, cloning if necessary /// Opens a repository, cloning if necessary
@ -44,7 +48,9 @@ pub fn open(
) -> Result<Box<dyn OpenRepositoryLike>> { ) -> Result<Box<dyn OpenRepositoryLike>> {
let open_repository = if repo_details.gitdir.exists() { let open_repository = if repo_details.gitdir.exists() {
info!("Local copy found - opening..."); info!("Local copy found - opening...");
repository_factory.open(repo_details)? let repo = repository_factory.open(repo_details)?;
repo.fetch()?;
repo
} else { } else {
info!("Local copy not found - cloning..."); info!("Local copy not found - cloning...");
repository_factory.git_clone(repo_details)? repository_factory.git_clone(repo_details)?
@ -114,6 +120,9 @@ pub enum Error {
#[error("git clone: {0}")] #[error("git clone: {0}")]
Clone(String), Clone(String),
#[error("git fetch: {0}")]
FetchError(#[from] git::fetch::Error),
#[error("open: {0}")] #[error("open: {0}")]
Open(String), Open(String),

View file

@ -37,10 +37,11 @@ pub enum OpenRepository {
} }
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
pub fn real(gix_repo: gix::Repository) -> OpenRepository { pub fn real(gix_repo: gix::Repository, forge_details: crate::ForgeDetails) -> OpenRepository {
OpenRepository::Real(oreal::RealOpenRepository::new(Arc::new(RwLock::new( OpenRepository::Real(oreal::RealOpenRepository::new(
gix_repo.into(), Arc::new(RwLock::new(gix_repo.into())),
)))) forge_details,
))
} }
#[cfg(not(tarpaulin_include))] // don't test mocks #[cfg(not(tarpaulin_include))] // don't test mocks
@ -49,8 +50,15 @@ pub(crate) fn test(
fs: &kxio::fs::FileSystem, fs: &kxio::fs::FileSystem,
on_fetch: Vec<otest::OnFetch>, on_fetch: Vec<otest::OnFetch>,
on_push: Vec<otest::OnPush>, on_push: Vec<otest::OnPush>,
forge_details: crate::ForgeDetails,
) -> OpenRepository { ) -> OpenRepository {
OpenRepository::Test(TestOpenRepository::new(gitdir, fs, on_fetch, on_push)) OpenRepository::Test(TestOpenRepository::new(
gitdir,
fs,
on_fetch,
on_push,
forge_details,
))
} }
#[allow(clippy::module_name_repetitions)] #[allow(clippy::module_name_repetitions)]

View file

@ -1,7 +1,7 @@
// //
use crate::{ use crate::{
git::{self, repository::OpenRepositoryLike}, git::{self, repository::OpenRepositoryLike},
BranchName, Hostname, RemoteUrl, RepoPath, BranchName, ForgeDetails, Hostname, RemoteUrl, RepoPath,
}; };
use derive_more::Constructor; use derive_more::Constructor;
@ -16,11 +16,14 @@ use std::{
}; };
#[derive(Clone, Debug, Constructor)] #[derive(Clone, Debug, Constructor)]
pub struct RealOpenRepository(Arc<RwLock<gix::ThreadSafeRepository>>); pub struct RealOpenRepository {
inner: Arc<RwLock<gix::ThreadSafeRepository>>,
forge_details: ForgeDetails,
}
impl super::OpenRepositoryLike for RealOpenRepository { impl super::OpenRepositoryLike for RealOpenRepository {
fn remote_branches(&self) -> git::push::Result<Vec<BranchName>> { fn remote_branches(&self) -> git::push::Result<Vec<BranchName>> {
let refs = self let refs = self
.0 .inner
.read() .read()
.map_err(|_| git::push::Error::Lock) .map_err(|_| git::push::Error::Lock)
.and_then(|repo| { .and_then(|repo| {
@ -44,7 +47,7 @@ impl super::OpenRepositoryLike for RealOpenRepository {
#[tracing::instrument] #[tracing::instrument]
fn find_default_remote(&self, direction: git::repository::Direction) -> Option<RemoteUrl> { fn find_default_remote(&self, direction: git::repository::Direction) -> Option<RemoteUrl> {
let Ok(repository) = self.0.read() else { let Ok(repository) = self.inner.read() else {
#[cfg(not(tarpaulin_include))] // don't test mutex lock failure #[cfg(not(tarpaulin_include))] // don't test mutex lock failure
tracing::debug!("no repository"); tracing::debug!("no repository");
return None; return None;
@ -64,34 +67,32 @@ impl super::OpenRepositoryLike for RealOpenRepository {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
#[cfg(not(tarpaulin_include))] // would require writing to external service #[cfg(not(tarpaulin_include))] // would require writing to external service
fn fetch(&self) -> Result<(), git::fetch::Error> { fn fetch(&self) -> Result<(), git::fetch::Error> {
use std::sync::atomic::AtomicBool; if self
.find_default_remote(git::repository::Direction::Fetch)
let Ok(repository) = self.0.read() else { .is_none()
#[cfg(not(tarpaulin_include))] // don't test mutex lock failure {
return Err(git::fetch::Error::Lock);
};
let thread_local = repository.to_thread_local();
let Some(Ok(remote)) =
thread_local.find_default_remote(git::repository::Direction::Fetch.into())
else {
#[cfg(not(tarpaulin_include))] // test is on local repo - should always have remotes
return Err(git::fetch::Error::NoFetchRemoteFound); return Err(git::fetch::Error::NoFetchRemoteFound);
}; }
remote info!("Fetching");
.connect(gix::remote::Direction::Fetch) gix::command::prepare("/usr/bin/git fetch --prune")
.map_err(|gix| git::fetch::Error::Connect(gix.to_string()))? .with_context(gix::diff::command::Context {
.prepare_fetch( git_dir: Some(
gix::progress::Discard, self.inner
gix::remote::ref_map::Options::default(), .read()
) .map_err(|_| git::fetch::Error::Lock)
.map_err(|gix| git::fetch::Error::Prepare(gix.to_string()))? .map(|r| r.git_dir().to_path_buf())?,
.receive(gix::progress::Discard, &AtomicBool::default()) ),
.map_err(|gix| git::fetch::Error::Receive(gix.to_string()))?; ..Default::default()
})
.with_shell_allow_argument_splitting()
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()?
.wait()?;
info!("Fetch okay"); info!("Fetch okay");
Ok(()) Ok(())
} }
// TODO: (#72) reimplement using `gix`
#[cfg(not(tarpaulin_include))] // would require writing to external service #[cfg(not(tarpaulin_include))] // would require writing to external service
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn push( fn push(
@ -111,13 +112,13 @@ impl super::OpenRepositoryLike for RealOpenRepository {
} }
}; };
// INFO: never log the command as it contains the API token within the 'origin' // INFO: never log the command as it contains the API token within the 'origin'
let command: secrecy::Secret<String> = format!( let command: secrecy::SecretString = format!(
"/usr/bin/git push {} {to_commit}:{branch_name} {force}", "/usr/bin/git push {} {to_commit}:{branch_name} {force}",
origin.expose_secret() origin.expose_secret()
) )
.into(); .into();
let git_dir = self let git_dir = self
.0 .inner
.read() .read()
.map_err(|_| git::push::Error::Lock) .map_err(|_| git::push::Error::Lock)
.map(|r| r.git_dir().to_path_buf())?; .map(|r| r.git_dir().to_path_buf())?;
@ -140,8 +141,14 @@ impl super::OpenRepositoryLike for RealOpenRepository {
branch_name: &BranchName, branch_name: &BranchName,
find_commits: &[git::Commit], find_commits: &[git::Commit],
) -> Result<Vec<git::Commit>, git::commit::log::Error> { ) -> Result<Vec<git::Commit>, git::commit::log::Error> {
let limit = if find_commits.is_empty() { 1 } else { 25 }; let limit: usize = if find_commits.is_empty() {
self.0 1
} else {
self.forge_details
.max_dev_commits()
.map_or(25, |commit_count| commit_count.clone().peel() as usize)
};
self.inner
.read() .read()
.map_err(|_| git::commit::log::Error::Lock) .map_err(|_| git::commit::log::Error::Lock)
.map(|repo| { .map(|repo| {
@ -196,7 +203,7 @@ impl super::OpenRepositoryLike for RealOpenRepository {
#[tracing::instrument(skip_all, fields(%branch_name, ?file_name))] #[tracing::instrument(skip_all, fields(%branch_name, ?file_name))]
fn read_file(&self, branch_name: &BranchName, file_name: &Path) -> git::file::Result<String> { fn read_file(&self, branch_name: &BranchName, file_name: &Path) -> git::file::Result<String> {
self.0 self.inner
.read() .read()
.map_err(|_| git::file::Error::Lock) .map_err(|_| git::file::Error::Lock)
.and_then(|repo| { .and_then(|repo| {

View file

@ -4,7 +4,7 @@ use crate::{
self, self,
repository::open::{OpenRepositoryLike, RealOpenRepository}, repository::open::{OpenRepositoryLike, RealOpenRepository},
}, },
BranchName, GitDir, RemoteUrl, RepoBranches, BranchName, ForgeDetails, GitDir, RemoteUrl, RepoBranches,
}; };
use derive_more::Constructor; use derive_more::Constructor;
@ -155,6 +155,7 @@ impl TestOpenRepository {
fs: &kxio::fs::FileSystem, fs: &kxio::fs::FileSystem,
on_fetch: Vec<OnFetch>, on_fetch: Vec<OnFetch>,
on_push: Vec<OnPush>, on_push: Vec<OnPush>,
forge_details: ForgeDetails,
) -> Self { ) -> Self {
let pathbuf = fs.base().join(gitdir.to_path_buf()); let pathbuf = fs.base().join(gitdir.to_path_buf());
#[allow(clippy::expect_used)] #[allow(clippy::expect_used)]
@ -165,7 +166,7 @@ impl TestOpenRepository {
fetch_counter: Arc::new(RwLock::new(0)), fetch_counter: Arc::new(RwLock::new(0)),
on_push, on_push,
push_counter: Arc::new(RwLock::new(0)), push_counter: Arc::new(RwLock::new(0)),
real: RealOpenRepository::new(Arc::new(RwLock::new(gix.into()))), real: RealOpenRepository::new(Arc::new(RwLock::new(gix.into())), forge_details),
} }
} }

View file

@ -1,3 +1,5 @@
use crate::CommitCount;
// //
use super::*; use super::*;
@ -6,7 +8,8 @@ use super::*;
fn should_return_single_item_in_commit_log_when_not_searching() -> TestResult { fn should_return_single_item_in_commit_log_when_not_searching() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp()); let_assert!(Ok(fs) = kxio::fs::temp());
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details();
let test_repository = git::repository::test(fs.clone(), forge_details);
let_assert!(Ok(open_repository) = test_repository.open(&gitdir)); let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
let repo_config = &given::a_repo_config(); let repo_config = &given::a_repo_config();
let branches = repo_config.branches(); let branches = repo_config.branches();
@ -23,7 +26,10 @@ fn should_return_capacity_25_in_commit_log_when_searching_for_garbage() -> TestR
let_assert!(Ok(fs) = kxio::fs::temp()); let_assert!(Ok(fs) = kxio::fs::temp());
let branch_name = given::a_branch_name(); let branch_name = given::a_branch_name();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details().with_max_dev_commits(Some(CommitCount::from(25)));
let_assert!(Some(max_dev_commits) = forge_details.max_dev_commits());
assert!(**max_dev_commits >= 25);
let test_repository = git::repository::test(fs.clone(), forge_details);
let_assert!(Ok(open_repository) = test_repository.open(&gitdir)); let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
for _ in [0; 25] { for _ in [0; 25] {
then::create_a_commit_on_branch(&fs, &gitdir, &branch_name)?; then::create_a_commit_on_branch(&fs, &gitdir, &branch_name)?;
@ -39,7 +45,10 @@ fn should_return_5_in_commit_log_when_searching_for_5th_item() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp(), "create temp directory"); let_assert!(Ok(fs) = kxio::fs::temp(), "create temp directory");
let branch_name = given::a_branch_name(); let branch_name = given::a_branch_name();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details().with_max_dev_commits(Some(CommitCount::from(10)));
let_assert!(Some(max_dev_commits) = forge_details.max_dev_commits());
assert!(**max_dev_commits > 5);
let test_repository = git::repository::test(fs.clone(), forge_details);
let_assert!( let_assert!(
Ok(open_repository) = test_repository.open(&gitdir), Ok(open_repository) = test_repository.open(&gitdir),
"open repository" "open repository"

View file

@ -14,7 +14,14 @@ fn should_return_repos() {
let mut repos = BTreeMap::new(); let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone()); repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name.clone(), blue.clone()); repos.insert(blue_name.clone(), blue.clone());
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos); let fc = ForgeConfig::new(
forge_type,
hostname,
user,
token,
given::maybe_a_number(),
repos,
);
let returned_repos = fc.repos().collect::<Vec<_>>(); let returned_repos = fc.repos().collect::<Vec<_>>();
@ -35,7 +42,14 @@ fn should_return_forge_type() {
let user = given::a_name(); let user = given::a_name();
let token = given::a_name(); let token = given::a_name();
let repos = BTreeMap::new(); let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos); let fc = ForgeConfig::new(
forge_type,
hostname,
user,
token,
given::maybe_a_number(),
repos,
);
assert_eq!(fc.forge_type(), ForgeType::MockForge); assert_eq!(fc.forge_type(), ForgeType::MockForge);
} }
@ -47,7 +61,14 @@ fn should_return_hostname() {
let user = given::a_name(); let user = given::a_name();
let token = given::a_name(); let token = given::a_name();
let repos = BTreeMap::new(); let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname.clone(), user, token, repos); let fc = ForgeConfig::new(
forge_type,
hostname.clone(),
user,
token,
given::maybe_a_number(),
repos,
);
assert_eq!(fc.hostname(), Hostname::new(hostname)); assert_eq!(fc.hostname(), Hostname::new(hostname));
} }
@ -59,7 +80,14 @@ fn should_return_user() {
let user = given::a_name(); let user = given::a_name();
let token = given::a_name(); let token = given::a_name();
let repos = BTreeMap::new(); let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user.clone(), token, repos); let fc = ForgeConfig::new(
forge_type,
hostname,
user.clone(),
token,
given::maybe_a_number(),
repos,
);
assert_eq!(fc.user(), User::new(user)); assert_eq!(fc.user(), User::new(user));
} }
@ -71,7 +99,14 @@ fn should_return_token() {
let user = given::a_name(); let user = given::a_name();
let token = given::a_name(); let token = given::a_name();
let repos = BTreeMap::new(); let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user, token.clone(), repos); let fc = ForgeConfig::new(
forge_type,
hostname,
user,
token.clone(),
given::maybe_a_number(),
repos,
);
assert_eq!(fc.token().expose_secret(), token.as_str()); assert_eq!(fc.token().expose_secret(), token.as_str());
} }
@ -90,7 +125,14 @@ fn should_return_repo() {
let mut repos = BTreeMap::new(); let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone()); repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name, blue); repos.insert(blue_name, blue);
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos); let fc = ForgeConfig::new(
forge_type,
hostname,
user,
token,
given::maybe_a_number(),
repos,
);
let returned_repo = fc.get_repo(red_name.as_str()); let returned_repo = fc.get_repo(red_name.as_str());

View file

@ -9,8 +9,9 @@ fn should_return_file() -> TestResult {
let file_name = given::a_pathbuf(); let file_name = given::a_pathbuf();
let contents = given::a_name(); let contents = given::a_name();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let forge_details = given::forge_details();
let test_repository = git::repository::test(fs.clone()); let test_repository = git::repository::test(fs.clone(), forge_details);
let_assert!(Ok(open_repository) = test_repository.open(&gitdir)); let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
then::commit_named_file_to_branch( then::commit_named_file_to_branch(
&file_name, &file_name,
@ -33,7 +34,8 @@ fn should_return_file() -> TestResult {
fn should_error_on_missing_file() -> TestResult { fn should_error_on_missing_file() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp()); let_assert!(Ok(fs) = kxio::fs::temp());
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details();
let test_repository = git::repository::test(fs.clone(), forge_details);
let_assert!(Ok(open_repository) = test_repository.open(&gitdir)); let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
let repo_config = &given::a_repo_config(); let repo_config = &given::a_repo_config();
let branches = repo_config.branches(); let branches = repo_config.branches();

View file

@ -13,7 +13,7 @@ use crate::{
}, },
RepoDetails, RepoDetails,
}, },
GitDir, ForgeDetails, GitDir,
}; };
#[allow(clippy::module_name_repetitions)] #[allow(clippy::module_name_repetitions)]
@ -22,6 +22,7 @@ pub struct TestRepository {
fs: kxio::fs::FileSystem, fs: kxio::fs::FileSystem,
on_fetch: Vec<git::repository::open::otest::OnFetch>, on_fetch: Vec<git::repository::open::otest::OnFetch>,
on_push: Vec<git::repository::open::otest::OnPush>, on_push: Vec<git::repository::open::otest::OnPush>,
forge_details: ForgeDetails,
} }
impl TestRepository { impl TestRepository {
pub fn on_fetch(&mut self, on_fetch: OnFetch) { pub fn on_fetch(&mut self, on_fetch: OnFetch) {
@ -39,6 +40,7 @@ impl RepositoryLike for TestRepository {
&self.fs, &self.fs,
self.on_fetch.clone(), self.on_fetch.clone(),
self.on_push.clone(), self.on_push.clone(),
self.forge_details.clone(),
)) ))
} }

View file

@ -172,6 +172,7 @@ mod repo_details {
"host".to_string(), "host".to_string(),
"user".to_string(), "user".to_string(),
"token".to_string(), "token".to_string(),
given::maybe_a_number(), // max dev commits
BTreeMap::new(), BTreeMap::new(),
), ),
GitDir::new(PathBuf::default().join("foo"), StoragePathType::Internal), GitDir::new(PathBuf::default().join("foo"), StoragePathType::Internal),
@ -184,6 +185,8 @@ mod repo_details {
} }
} }
pub mod given { pub mod given {
use crate::ForgeDetails;
use super::*; use super::*;
pub fn repo_branches() -> RepoBranches { pub fn repo_branches() -> RepoBranches {
@ -219,6 +222,22 @@ pub mod given {
generate(5) generate(5)
} }
pub fn maybe_a_number() -> Option<u32> {
use rand::Rng;
let mut rng = rand::thread_rng();
if Rng::gen_ratio(&mut rng, 1, 2) {
Some(a_number())
} else {
None
}
}
pub fn a_number() -> u32 {
use rand::Rng;
let mut rng = rand::thread_rng();
rng.gen_range(5..100)
}
pub fn a_branch_name() -> BranchName { pub fn a_branch_name() -> BranchName {
BranchName::new(a_name()) BranchName::new(a_name())
} }
@ -235,10 +254,15 @@ pub mod given {
format!("hostname-{}", a_name()), format!("hostname-{}", a_name()),
format!("user-{}", a_name()), format!("user-{}", a_name()),
format!("token-{}", a_name()), format!("token-{}", a_name()),
BTreeMap::default(), // no repos given::maybe_a_number(), // max dev commits
BTreeMap::default(), // no repos
) )
} }
pub fn forge_details() -> ForgeDetails {
(&a_forge_alias(), &a_forge_config()).into()
}
pub fn a_server_repo_config() -> ServerRepoConfig { pub fn a_server_repo_config() -> ServerRepoConfig {
let main = a_branch_name().peel(); let main = a_branch_name().peel();
let next = a_branch_name().peel(); let next = a_branch_name().peel();

View file

@ -207,7 +207,8 @@ mod positions {
//given //given
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new( test_repository.on_fetch(OnFetch::new(
repo_config.branches().clone(), repo_config.branches().clone(),
@ -257,7 +258,8 @@ mod positions {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new( test_repository.on_fetch(OnFetch::new(
repo_config.branches().clone(), repo_config.branches().clone(),
@ -343,7 +345,8 @@ mod positions {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new( test_repository.on_fetch(OnFetch::new(
repo_config.branches().clone(), repo_config.branches().clone(),
@ -416,7 +419,8 @@ mod positions {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new( test_repository.on_fetch(OnFetch::new(
repo_config.branches().clone(), repo_config.branches().clone(),
@ -501,7 +505,8 @@ mod positions {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new( test_repository.on_fetch(OnFetch::new(
repo_config.branches().clone(), repo_config.branches().clone(),
@ -598,7 +603,8 @@ mod positions {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new( test_repository.on_fetch(OnFetch::new(
repo_config.branches().clone(), repo_config.branches().clone(),

View file

@ -691,6 +691,22 @@ mod forgejo {
generate(5) generate(5)
} }
pub fn maybe_a_number() -> Option<u32> {
use rand::Rng;
let mut rng = rand::thread_rng();
if Rng::gen_ratio(&mut rng, 1, 2) {
Some(a_number())
} else {
None
}
}
pub fn a_number() -> u32 {
use rand::Rng;
let mut rng = rand::thread_rng();
rng.gen_range(0..100)
}
pub fn a_webhook_id() -> WebhookId { pub fn a_webhook_id() -> WebhookId {
WebhookId::new(a_name()) WebhookId::new(a_name())
} }
@ -711,6 +727,7 @@ mod forgejo {
a_name(), a_name(),
a_name(), a_name(),
a_name(), a_name(),
maybe_a_number(),
BTreeMap::default(), // no repos BTreeMap::default(), // no repos
) )
} }

View file

@ -600,6 +600,7 @@ mod github {
a_name(), a_name(),
a_name(), a_name(),
a_name(), a_name(),
maybe_a_number(),
BTreeMap::default(), BTreeMap::default(),
), ),
GitDir::new(PathBuf::default(), StoragePathType::External), GitDir::new(PathBuf::default(), StoragePathType::External),
@ -641,6 +642,22 @@ mod github {
generate(5) generate(5)
} }
pub fn maybe_a_number() -> Option<u32> {
use rand::Rng;
let mut rng = rand::thread_rng();
if Rng::gen_ratio(&mut rng, 1, 2) {
Some(a_number())
} else {
None
}
}
pub fn a_number() -> u32 {
use rand::Rng;
let mut rng = rand::thread_rng();
rng.gen_range(0..100)
}
pub fn a_webhook_id() -> WebhookId { pub fn a_webhook_id() -> WebhookId {
WebhookId::new(given::a_name()) WebhookId::new(given::a_name())
} }

447
demo.cast Normal file
View file

@ -0,0 +1,447 @@
{"version": 2, "width": 80, "height": 24, "timestamp": 1726490135, "env": {"SHELL": "/opt/homebrew/bin/bash", "TERM": "xterm-256color"}}
[0.029531, "o", "\u001b[?1049h"]
[0.030008, "o", "\u001b[1;1H┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[1m Git-Next v0.13.11 \u001b[22m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\u001b[2;1H┃\u001b[2;36HLoading...1\u001b[2;80H┃\u001b[3;1H┃\u001b[3;80H┃\u001b[4;1H┃\u001b[4;80H┃\u001b[5;1H┃\u001b[5;80H┃\u001b[6;1H┃\u001b[6;80H┃\u001b[7;1H┃\u001b[7;80H┃\u001b[8;1H┃\u001b[8;80H┃\u001b[9;1H┃\u001b[9;80H┃\u001b[10;1H┃\u001b[10;80H┃\u001b[11;1H┃\u001b[11;80H┃\u001b[12;1H┃\u001b[12;80H┃\u001b[13;1H┃\u001b[13;80H┃\u001b[14;1H┃\u001b[14;80H┃\u001b[15;1H┃\u001b[15;80H┃\u001b[16;1H┃\u001b[16;80H┃\u001b[17;1H┃\u001b[17;80H┃\u001b[18;1H┃\u001b[18;80H┃\u001b[19;1H┃\u001b[19;80H┃\u001b[20;1H┃\u001b[20;80H┃\u001b[21;1H┃\u001b[21;80H┃\u001b[22;1H┃\u001b[22;80H┃\u001b[23;1H┃\u001b[23;80H┃\u001b[24;1H┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[24;36H[q]uit\u001b[24;43H💚\u001b[24;47H━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[24;74H13:35\u001b[24;80H┛\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[2.552043, "o", "\u001b[2;3Hforge:\u001b[2;10Hjo\u001b[2;36H \u001b[3;3H\u001b[38;5;6;49mgit-next\u001b[3;12H\u001b[39;49m(main\u001b[3;18H->\u001b[3;21Hnext\u001b[3;26H->\u001b[3;29Hdev)\u001b[3;34H\u001b[38;5;7;49mregistering webhook...\u001b[3;57H\u001b[39;49m──────────────────────\u001b[5;3H\u001b[38;5;6;49mkxio\u001b[5;8H\u001b[39;49m(main\u001b[5;14H->\u001b[5;17Hnext\u001b[5;22H->\u001b[5;25Hdev)\u001b[5;30H\u001b[38;5;7;49mregistering webhook...\u001b[5;53H\u001b[39;49m──────────────────────────\u001b[7;3H\u001b[38;5;6;49mpodal\u001b[7;9H\u001b[39;49m(main\u001b[7;15H->\u001b[7;18Hnext\u001b[7;23H->\u001b[7;26Hdev)\u001b[7;31H\u001b[38;5;7;49mregistering webhook...\u001b[7;54H\u001b[39;49m─────────────────────────\u001b[9;3H\u001b[38;5;6;49mrefile-m4b\u001b[9;14H\u001b[39;49m(main\u001b[9;20H->\u001b[9;23Hnext\u001b[9;28H->\u001b[9;31Hdev)\u001b[9;36H\u001b[38;5;7;49mregistering webhook...\u001b[9;59H\u001b[39;49m────────────────────\u001b[11;3H\u001b[38;5;6;49mrust-action\u001b[11;15H\u001b[39;49m(main\u001b[11;21H->\u001b[11;24Hnext\u001b[11;29H->\u001b[11;32Hdev)\u001b[11;37H\u001b[38;5;7;49mregistering webhook...\u001b[11;60H"]
[2.552123, "o", "\u001b[39;49m───────────────────\u001b[13;3H\u001b[38;5;6;49mskip\u001b[13;8H\u001b[39;49m(main\u001b[13;14H->\u001b[13;17Hnext\u001b[13;22H->\u001b[13;25Hdev)\u001b[13;30H\u001b[38;5;7;49mregistering webhook...\u001b[13;53H\u001b[39;49m──────────────────────────\u001b[15;3H\u001b[38;5;6;49mtasyn\u001b[15;9H\u001b[39;49m(main\u001b[15;15H->\u001b[15;18Hnext\u001b[15;23H->\u001b[15;26Hdev)\u001b[15;31H\u001b[38;5;7;49mregistering webhook...\u001b[15;54H\u001b[39;49m─────────────────────────\u001b[17;3H\u001b[38;5;6;49mtest\u001b[17;8H\u001b[39;49m(main\u001b[17;14H->\u001b[17;17Hnext\u001b[17;22H->\u001b[17;25Hdev)\u001b[17;30H\u001b[38;5;7;49mregistering webhook...\u001b[17;53H\u001b[39;49m──────────────────────────\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[2.614517, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[2.649175, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[2.684709, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[2.71967, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[2.75525, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[2.789211, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[2.826615, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[3.673281, "o", "\u001b[7;31H\u001b[38;5;2;49mokay\u001b[39;49m ──────────────────\u001b[8;2H*\u001b[8;4H249b943\u001b[8;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[8;30H\u001b[39;49mfix(deps):\u001b[8;41Hupdate\u001b[8;48Hrust\u001b[8;53Hcrate\u001b[8;59Hscraper\u001b[8;67Hto\u001b[8;70H0.20\u001b[9;3H \u001b[9;14H \u001b[9;20H \u001b[9;23H \u001b[9;28H \u001b[9;31H \u001b[9;36H \u001b[9;59H \u001b[10;3H\u001b[38;5;6;49mrefile-m4b\u001b[10;14H\u001b[39;49m(main\u001b[10;20H->\u001b[10;23Hnext\u001b[10;28H->\u001b[10;31Hdev)\u001b[10;36H\u001b[38;5;7;49mregistering webhook...\u001b[10;59H\u001b[39;49m────────────────────\u001b[11;3H \u001b[11;15H \u001b[11;21H \u001b[11;24H \u001b[11;29H \u001b[11;32H \u001b[11;37H \u001b[11;60H \u001b[12;3H\u001b[38;5;6;49mrust-action\u001b[12;15H\u001b[39;49m(main\u001b[12;21H->\u001b[12;24Hnext\u001b[12;29H->\u001b[12;32Hdev)\u001b[12;37H\u001b[38;5;7;49mregistering webhook...\u001b[12;60H\u001b[39;49m───────────────────\u001b[13;3H \u001b[13;8H \u001b[13;14H \u001b[13;17H \u001b[13;22H \u001b[13;25H \u001b[13;30H \u001b[13;53"]
[3.673319, "o", "H \u001b[14;3H\u001b[38;5;6;49mskip\u001b[14;8H\u001b[39;49m(main\u001b[14;14H->\u001b[14;17Hnext\u001b[14;22H->\u001b[14;25Hdev)\u001b[14;30H\u001b[38;5;2;49mokay\u001b[14;35H\u001b[39;49m────────────────────────────────────────────\u001b[15;2H* fc7fbca \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/rust docker tag to \u001b[17;4H\u001b[38;5;6;49ma\u001b[17;6Hyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;7;49mregistering webh\u001b[17;48Hok\u001b[17;52H.\u001b[39;49m \u001b[19;3H\u001b[38;5;6;49mtest\u001b[19;8H\u001b[39;49m(main\u001b[19;14H->\u001b[19;17Hnext\u001b[19;22H->\u001b[19;25Hdev)\u001b[19;30H\u001b[38;5;7;49mregistering webhook...\u001b[19;53H\u001b[39;49m──────────────────────────\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[4.567297, "o", "\u001b[2;79H▲\u001b[3;34H\u001b[38;5;2;49mokay\u001b[39;49m ──────────────────\u001b[3;79H█\u001b[4;2H*\u001b[4;4H91c5973\u001b[4;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[4;30H\u001b[39;49mchore:\u001b[4;37Hrelease\u001b[4;79H█\u001b[5;3H \u001b[5;8H \u001b[5;14H \u001b[5;17H \u001b[5;22H \u001b[5;25H \u001b[5;30H \u001b[5;53H █\u001b[6;3H\u001b[38;5;6;49mkxio\u001b[6;8H\u001b[39;49m(main\u001b[6;14H->\u001b[6;17Hnext\u001b[6;22H->\u001b[6;25Hdev)\u001b[6;30H\u001b[38;5;7;49mregistering webhook...\u001b[6;53H\u001b[39;49m──────────────────────────█\u001b[7;3H \u001b[7;9H \u001b[7;15H \u001b[7;18H \u001b[7;23H \u001b[7;26H \u001b[7;31H \u001b[7;36H █\u001b[8;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────█\u001b[9;2H*\u001b[9;4H249b943\u001b[9;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[9;30H\u001b[39;49mfix(deps):\u001b[9;41Hupdate\u001b[9;48Hrust\u001b[9;53Hcrate\u001b[9;59Hs"]
[4.567379, "o", "craper\u001b[9;67Hto\u001b[9;70H0.20\u001b[9;79H█\u001b[10;3H \u001b[10;14H \u001b[10;20H \u001b[10;23H \u001b[10;28H \u001b[10;31H \u001b[10;36H \u001b[10;59H █\u001b[11;3H\u001b[38;5;6;49mrefile-m4b\u001b[11;14H\u001b[39;49m(main\u001b[11;20H->\u001b[11;23Hnext\u001b[11;28H->\u001b[11;31Hdev)\u001b[11;36H\u001b[38;5;7;49mregistering webhook...\u001b[11;59H\u001b[39;49m────────────────────█\u001b[12;3H \u001b[12;15H \u001b[12;21H \u001b[12;24H \u001b[12;29H \u001b[12;32H \u001b[12;37H \u001b[12;60H █\u001b[13;3H\u001b[38;5;6;49mrust-action\u001b[13;15H\u001b[39;49m(main\u001b[13;21H->\u001b[13;24Hnext\u001b[13;29H->\u001b[13;32Hdev)\u001b[13;37H\u001b[38;5;2;49mokay\u001b[13;42H\u001b[39;49m─────────────────────────────────────║\u001b[14;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[14;30H\u001b[39;49mfeat: ensure toolchains are up-to-date ║\u001b[15;2H \u001b[15;4H \u001b[15;12H \u001b[15;30H \u001b[15;43H \u001b[15;50H \u001b[15;65H \u001b[15;72H \u001b[15;76"]
[4.567437, "o", "H \u001b[15;79H║\u001b[16;3H\u001b[38;5;6;49mskip\u001b[16;8H\u001b[39;49m(main\u001b[16;14H->\u001b[16;17Hnext\u001b[16;22H->\u001b[16;25Hdev)\u001b[16;30H\u001b[38;5;2;49mokay\u001b[16;35H\u001b[39;49m────────────────────────────────────────────║\u001b[17;2H* fc7fbca \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/rust docker tag to ║\u001b[18;79H║\u001b[19;4H\u001b[38;5;6;49ma\u001b[19;6Hyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────\u001b[19;79H║\u001b[20;2H*\u001b[20;4H24fe845\u001b[20;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[20;30H\u001b[39;49mchore(deps):\u001b[20;43Hupdate\u001b[20;50Hdocker.io/postgres:16.4-alpin║\u001b[21;79H║\u001b[22;3H\u001b[38;5;6;49mtest\u001b[22;8H\u001b[39;49m(main\u001b[22;14H->\u001b[22;17Hnext\u001b[22;22H->\u001b[22;25Hdev)\u001b[22;30H\u001b[38;5;7;49mregistering webhook...\u001b[22;53H\u001b[39;49m──────────────────────────║\u001b[23;79H▼\u001b[24;43H💚\u001b[24;45H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[4.603024, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[4.636433, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.373579, "o", "\u001b[11;36H\u001b[38;5;2;49mokay\u001b[39;49m ──────────────────\u001b[12;2H*\u001b[12;4H7fba5fb\u001b[12;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[12;30H\u001b[39;49mSupport\u001b[12;38H--version,\u001b[12;49H--help\u001b[12;56Hand\u001b[12;60H--directory\u001b[12;72Hcli\u001b[12;76Harg\u001b[13;3H \u001b[13;15H \u001b[13;21H \u001b[13;24H \u001b[13;29H \u001b[13;32H \u001b[13;37H \u001b[13;42H \u001b[14;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────\u001b[15;2H*\u001b[15;4Hde4785c\u001b[15;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[15;30H\u001b[39;49mfeat:\u001b[15;36Hensure\u001b[15;43Htoolchains\u001b[15;54Hare\u001b[15;58Hup-to-date\u001b[16;3H \u001b[16;8H \u001b[16;14H \u001b[16;17H \u001b[16;22H \u001b[16;25H \u001b[16;30H \u001b[16;35H \u001b[17;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[17;11Hin -> next -> dev)\u001b[17;30H\u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────"]
[5.373661, "o", "───────────────────────\u001b[18;2H*\u001b[18;4Hfc7fbca\u001b[18;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[18;30H\u001b[39;49mchore(deps):\u001b[18;43Hupdate\u001b[18;50Hdocker.io/rust\u001b[18;65Hdocker\u001b[18;72Htag\u001b[18;76Hto\u001b[19;3H \u001b[19;9H \u001b[19;15H \u001b[19;18H \u001b[19;23H \u001b[19;26H \u001b[19;31H \u001b[19;36H \u001b[20;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[21;2H*\u001b[21;4H24fe845\u001b[21;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[21;30H\u001b[39;49mchore(deps):\u001b[21;43Hupdate\u001b[21;50Hdocker.io/postgres:16.4-alpin\u001b[22;3H \u001b[22;8H \u001b[22;14H \u001b[22;17H \u001b[22;22H \u001b[22;25H \u001b[22;30H \u001b[22;53H \u001b[23;3H\u001b[38;5;6;49mtest\u001b[23;8H\u001b[39;49m(main\u001b[23;14H->\u001b[23;17Hnext\u001b[23;22H->\u001b[23;25Hdev)\u001b[23;30H\u001b[38;5;2;49mokay\u001b[23;35H\u001b[39;49m─────────────────"]
[5.373769, "o", "───────────────────────────\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.667499, "o", "\u001b[6;3H\u001b[38;5;15;48;5;1mkxio\u001b[6;8Hcommit is a Work-in-progress\u001b[39;49m (main -> next -> dev) \u001b[38;5;1;49mALERT\u001b[39;49m \u001b[7;2H*\u001b[7;4H805d0b3\u001b[7;12H\u001b[38;5;15;48;5;4m(dev)\u001b[7;18H\u001b[39;49mWIP:\u001b[7;23Hdocs(fs):\u001b[7;33Hadd\u001b[7;37Hsome\u001b[7;42Hdocumentation\u001b[8;2H* 9943a0f \u001b[38;5;15;48;5;4m(main)(next)\u001b[39;49m chore(deps): update docker.io/rust docker tag to v1.81\u001b[9;2H \u001b[9;4H \u001b[9;12H \u001b[9;30H \u001b[9;41H \u001b[9;48H \u001b[9;53H \u001b[9;59H \u001b[9;67H \u001b[9;70H \u001b[10;3H\u001b[38;5;6;49mpodal\u001b[10;9H\u001b[39;49m(main\u001b[10;15H->\u001b[10;18Hnext\u001b[10;23H->\u001b[10;26Hdev)\u001b[10;31H\u001b[38;5;2;49mokay\u001b[10;36H\u001b[39;49m───────────────────────────────────────────\u001b[11;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps):\u001b[11;41Hupdate rust crate scraper to 0.20 \u001b[12;2H \u001b[12;4H \u001b[12;12H \u001b[12;30H \u001b[12;38H \u001b[12;49H \u001b[12;56H \u001b[12;60H \u001b[12;72H \u001b[12;76H ║\u001b[13;3H\u001b[38;5;6;49mr"]
[5.667549, "o", "efile-m4b\u001b[13;14H\u001b[39;49m(main\u001b[13;20H->\u001b[13;23Hnext\u001b[13;28H->\u001b[13;31Hdev)\u001b[13;36H\u001b[38;5;2;49mokay\u001b[13;41H\u001b[39;49m──────────────────────────────────────\u001b[14;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[15;2H \u001b[15;4H \u001b[15;12H \u001b[15;30H \u001b[15;36H \u001b[15;43H \u001b[15;54H \u001b[15;58H \u001b[16;3H\u001b[38;5;6;49mrust-action\u001b[16;15H\u001b[39;49m(main\u001b[16;21H->\u001b[16;24Hnext\u001b[16;29H->\u001b[16;32Hdev)\u001b[16;37H\u001b[38;5;2;49mokay\u001b[16;42H\u001b[39;49m─────────────────────────────────────\u001b[17;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[17;30H\u001b[39;49mfeat: ensure toolchains are up-to-date \u001b[18;2H \u001b[18;4H \u001b[18;12H \u001b[18;30H \u001b[18;43H \u001b[18;50H \u001b[18;65H \u001b[18;72H \u001b[18;76H \u001b[19;3H\u001b[38;5;6;49mskip\u001b[19;8H\u001b[39;49m(main\u001b[19;14H->\u001b[19;17Hnext\u001b"]
[5.667635, "o", "[19;22H->\u001b[19;25Hdev)\u001b[19;30H\u001b[38;5;2;49mokay\u001b[19;35H\u001b[39;49m────────────────────────────────────────────\u001b[20;2H* fc7fbca \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/rust docker tag to \u001b[21;2H \u001b[21;4H \u001b[21;12H \u001b[21;30H \u001b[21;43H \u001b[21;50H \u001b[22;3H\u001b[38;5;6;49mtasyn\u001b[22;9H\u001b[39;49m(main\u001b[22;15H->\u001b[22;18Hnext\u001b[22;23H->\u001b[22;26Hdev)\u001b[22;31H\u001b[38;5;2;49mokay\u001b[22;36H\u001b[39;49m───────────────────────────────────────────\u001b[23;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[23;30H\u001b[39;49mchore(deps): update docker.io/postgres:16.4-alpin\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.703032, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.738998, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.774205, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.808972, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.844592, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.881476, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.91946, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.956562, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[5.993522, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.031996, "o", "\u001b[24;43H💚\u001b[24;45H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.069336, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.109029, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.1469, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.184289, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.220107, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.256197, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.293358, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.331134, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.366742, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.402568, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.439465, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.478813, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.517268, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.560687, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.594301, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.629263, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.669428, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.706761, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.742165, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.78203, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.822172, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.859887, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.897026, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.938296, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[6.975571, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.01065, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.047644, "o", "\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.08457, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.121364, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.157513, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.195418, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.228658, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.264878, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.305593, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.3437, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.381122, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.418853, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.454425, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.492184, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.540907, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.578132, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.614231, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.65196, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.69186, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.731624, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.767836, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.804511, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.840577, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.877375, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.913764, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.952312, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[7.989869, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.031084, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.068302, "o", "\u001b[24;43H💚\u001b[24;45H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.104549, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.140569, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.177935, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.21429, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.251383, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.289527, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.329826, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.367669, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.404116, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.441232, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.479153, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.516467, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.554111, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.590533, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.631784, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.672414, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.708258, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.745135, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.78123, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.82166, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.861283, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.898503, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.934001, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[8.971135, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.008281, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.045177, "o", "\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.082668, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.119739, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.156767, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.194149, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.23081, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.2488, "o", "\u001b[2;3H\u001b[38;5;6;49mgit-next\u001b[39;49m (main\u001b[2;18H->\u001b[2;21Hnext\u001b[2;26H->\u001b[2;29Hdev)\u001b[2;34H\u001b[38;5;2;49mokay\u001b[2;39H\u001b[39;49m────────────────────────────────────────\u001b[3;2H* 91c5973\u001b[3;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore: release \u001b[4;2H \u001b[4;4H \u001b[4;12H \u001b[4;30H \u001b[4;37H \u001b[5;3H\u001b[38;5;15;48;5;1mkxio\u001b[5;8Hcommit is a Work-in-progress\u001b[5;37H\u001b[39;49m(main\u001b[5;43H->\u001b[5;46Hnext\u001b[5;51H->\u001b[5;54Hdev)\u001b[5;59H\u001b[38;5;1;49mALERT\u001b[5;65H\u001b[39;49m──────────────\u001b[6;2H* 805d0b3 \u001b[38;5;15;48;5;4m(dev)\u001b[39;49m WIP: docs(fs): add\u001b[6;37Hsome docum\u001b[6;48Hn\u001b[6;50Hation \u001b[6;59H \u001b[6;65H \u001b[7;4H9943a0f\u001b[7;13H\u001b[38;5;15;48;5;4mmain)(next)\u001b[39;49m \u001b[7;26Hhore(deps): update docker.\u001b[7;54H/rust\u001b[7;60Hdocker\u001b[7;67Htag\u001b[7;71Hto\u001b[7;74Hv1.81\u001b[8;2H \u001b[8;4H \u001b[8;12H \u001b[8;25H \u001b[8;38H \u001b[8;45H \u001b[8;60H \u001b[8;67H "]
[9.248957, "o", "\u001b[8;71H \u001b[8;74H \u001b[9;3H\u001b[38;5;6;49mpodal\u001b[9;9H\u001b[39;49m(main\u001b[9;15H->\u001b[9;18Hnext\u001b[9;23H->\u001b[9;26Hdev)\u001b[9;31H\u001b[38;5;2;49mokay\u001b[9;36H\u001b[39;49m───────────────────────────────────────────\u001b[10;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust crate scraper to 0.20 \u001b[11;2H \u001b[11;4H \u001b[11;12H \u001b[11;30H \u001b[11;41H \u001b[11;48H \u001b[11;53H \u001b[11;59H \u001b[11;67H \u001b[11;70H \u001b[12;3H\u001b[38;5;6;49mrefile-m4b\u001b[12;14H\u001b[39;49m(main\u001b[12;20H->\u001b[12;23Hnext\u001b[12;28H->\u001b[12;31Hdev)\u001b[12;36H\u001b[38;5;2;49mokay\u001b[12;41H\u001b[39;49m──────────────────────────────────────█\u001b[13;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[14;2H \u001b[14;4H \u001b[14;12H \u001b[14;30H \u001b[14;38H \u001b[14;49H \u001b[14;56H \u001b[14;60H \u001b[14;72H \u001b[14;76H "]
[9.249109, "o", " \u001b[15;3H\u001b[38;5;6;49mrust-action\u001b[15;15H\u001b[39;49m(main\u001b[15;21H->\u001b[15;24Hnext\u001b[15;29H->\u001b[15;32Hdev)\u001b[15;37H\u001b[38;5;2;49mokay\u001b[15;42H\u001b[39;49m─────────────────────────────────────\u001b[16;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m feat: ensure toolchains are up-to-date \u001b[17;2H \u001b[17;4H \u001b[17;12H \u001b[17;30H \u001b[17;36H \u001b[17;43H \u001b[17;54H \u001b[17;58H \u001b[18;3H\u001b[38;5;6;49mskip\u001b[18;8H\u001b[39;49m(main\u001b[18;14H->\u001b[18;17Hnext\u001b[18;22H->\u001b[18;25Hdev)\u001b[18;30H\u001b[38;5;2;49mokay\u001b[18;35H\u001b[39;49m────────────────────────────────────────────\u001b[19;2H* fc7fbc\u001b[19;11H \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[19;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to \u001b[20;2H \u001b[20;4H \u001b[20;12H \u001b[20;30H \u001b[20;43H \u001b[20;50H \u001b[20;65H \u001b[20;72H \u001b[20;76H \u001b[21;3H\u001b[38;5;6;49mtasyn\u001b["]
[9.249125, "o", "21;9H\u001b[39;49m(main\u001b[21;15H->\u001b[21;18Hnext\u001b[21;23H->\u001b[21;26Hdev)\u001b[21;31H\u001b[38;5;2;49mokay\u001b[21;36H\u001b[39;49m───────────────────────────────────────────\u001b[22;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/postgres:16.4-alpin\u001b[23;2H \u001b[23;4H \u001b[23;12H \u001b[23;30H \u001b[23;43H \u001b[23;50H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.269683, "o", "\u001b[2;2H* 91c5973\u001b[2;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore: release \u001b[3;2H \u001b[3;4H \u001b[3;12H \u001b[3;30H \u001b[3;37H \u001b[3;79H║\u001b[4;3H\u001b[38;5;15;48;5;1mkxio\u001b[4;8Hcommit is a Work-in-progress\u001b[4;37H\u001b[39;49m(main\u001b[4;43H->\u001b[4;46Hnext\u001b[4;51H->\u001b[4;54Hdev)\u001b[4;59H\u001b[38;5;1;49mALERT\u001b[4;65H\u001b[39;49m──────────────\u001b[5;2H* 805d0b3 \u001b[38;5;15;48;5;4m(dev)\u001b[39;49m WIP: docs(fs): add\u001b[5;37Hsome docum\u001b[5;48Hn\u001b[5;50Hation \u001b[5;59H \u001b[5;65H \u001b[6;4H9943a0f\u001b[6;13H\u001b[38;5;15;48;5;4mmain)(next)\u001b[39;49m \u001b[6;26Hhore(deps): update docker.\u001b[6;54H/rust\u001b[6;60Hdocker\u001b[6;67Htag\u001b[6;71Hto\u001b[6;74Hv1.81\u001b[7;2H \u001b[7;4H \u001b[7;12H \u001b[7;25H \u001b[7;38H \u001b[7;45H \u001b[7;60H \u001b[7;67H \u001b[7;71H \u001b[7;74H \u001b[8;3H\u001b[38;5;6;49mpodal\u001b[8;9H\u001b[39;49m(main\u001b[8;15H->\u001b[8;18Hnext\u001b[8;23H->\u001b[8;26Hdev)\u001b[8;31H\u001b[38;5;2;49mokay\u001b[8;36H\u001b[39;49m─────────────────────────────"]
[9.271446, "o", "──────────────\u001b[9;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust crate scraper to 0.20 \u001b[10;2H \u001b[10;4H \u001b[10;12H \u001b[10;30H \u001b[10;41H \u001b[10;48H \u001b[10;53H \u001b[10;59H \u001b[10;67H \u001b[10;70H \u001b[11;3H\u001b[38;5;6;49mrefile-m4b\u001b[11;14H\u001b[39;49m(main\u001b[11;20H->\u001b[11;23Hnext\u001b[11;28H->\u001b[11;31Hdev)\u001b[11;36H\u001b[38;5;2;49mokay\u001b[11;41H\u001b[39;49m──────────────────────────────────────\u001b[12;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[13;2H \u001b[13;4H \u001b[13;12H \u001b[13;30H \u001b[13;38H \u001b[13;49H \u001b[13;56H \u001b[13;60H \u001b[13;72H \u001b[13;76H \u001b[14;3H\u001b[38;5;6;49mrust-action\u001b[14;15H\u001b[39;49m(main\u001b[14;21H->\u001b[14;24Hnext\u001b[14;29H->\u001b[14;32Hdev)\u001b[14;37H\u001b[38;5;2;49mokay\u001b[14;42H\u001b[39;49m───────────────────────────────"]
[9.271582, "o", "──────\u001b[15;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m feat: ensure toolchains are up-to-date \u001b[16;2H \u001b[16;4H \u001b[16;12H \u001b[16;30H \u001b[16;36H \u001b[16;43H \u001b[16;54H \u001b[16;58H \u001b[17;3H\u001b[38;5;6;49mskip\u001b[17;8H\u001b[39;49m(main\u001b[17;14H->\u001b[17;17Hnext\u001b[17;22H->\u001b[17;25Hdev)\u001b[17;30H\u001b[38;5;2;49mokay\u001b[17;35H\u001b[39;49m────────────────────────────────────────────\u001b[18;2H* fc7fbc\u001b[18;11H \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[18;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to \u001b[19;2H \u001b[19;4H \u001b[19;12H \u001b[19;30H \u001b[19;43H \u001b[19;50H \u001b[19;65H \u001b[19;72H \u001b[19;76H \u001b[20;3H\u001b[38;5;6;49mtasyn\u001b[20;9H\u001b[39;49m(main\u001b[20;15H->\u001b[20;18Hnext\u001b[20;23H->\u001b[20;26Hdev)\u001b[20;31H\u001b[38;5;2;49mokay\u001b[20;36H\u001b[39;49m──────────────────────────────────────────"]
[9.271711, "o", "─\u001b[21;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/postgres:16.4-alpin\u001b[22;2H \u001b[22;4H \u001b[22;12H \u001b[22;30H \u001b[22;43H \u001b[22;50H \u001b[23;3H\u001b[38;5;6;49mtest\u001b[23;8H\u001b[39;49m(main\u001b[23;14H->\u001b[23;17Hnext\u001b[23;22H->\u001b[23;25Hdev)\u001b[23;30H\u001b[38;5;2;49mokay\u001b[23;35H\u001b[39;49m────────────────────────────────────────────\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.291758, "o", "\u001b[2;2H \u001b[2;4H \u001b[2;12H \u001b[2;30H \u001b[2;37H \u001b[3;3H\u001b[38;5;15;48;5;1mkxio\u001b[3;8Hcommit is a Work-in-progress\u001b[3;37H\u001b[39;49m(main\u001b[3;43H->\u001b[3;46Hnext\u001b[3;51H->\u001b[3;54Hdev)\u001b[3;59H\u001b[38;5;1;49mALERT\u001b[3;65H\u001b[39;49m──────────────\u001b[4;2H* 805d0b3 \u001b[38;5;15;48;5;4m(dev)\u001b[39;49m WIP: docs(fs): add\u001b[4;37Hsome docum\u001b[4;48Hn\u001b[4;50Hation \u001b[4;59H \u001b[4;65H \u001b[5;4H9943a0f\u001b[5;13H\u001b[38;5;15;48;5;4mmain)(next)\u001b[39;49m \u001b[5;26Hhore(deps): update docker.\u001b[5;54H/rust\u001b[5;60Hdocker\u001b[5;67Htag\u001b[5;71Hto\u001b[5;74Hv1.81\u001b[6;2H \u001b[6;4H \u001b[6;12H \u001b[6;25H \u001b[6;38H \u001b[6;45H \u001b[6;60H \u001b[6;67H \u001b[6;71H \u001b[6;74H \u001b[7;3H\u001b[38;5;6;49mpodal\u001b[7;9H\u001b[39;49m(main\u001b[7;15H->\u001b[7;18Hnext\u001b[7;23H->\u001b[7;26Hdev)\u001b[7;31H\u001b[38;5;2;49mokay\u001b[7;36H\u001b[39;49m───────────────────────────────────────────\u001b[8;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust "]
[9.292005, "o", "crate scraper to 0.20 \u001b[9;2H \u001b[9;4H \u001b[9;12H \u001b[9;30H \u001b[9;41H \u001b[9;48H \u001b[9;53H \u001b[9;59H \u001b[9;67H \u001b[9;70H \u001b[10;3H\u001b[38;5;6;49mrefile-m4b\u001b[10;14H\u001b[39;49m(main\u001b[10;20H->\u001b[10;23Hnext\u001b[10;28H->\u001b[10;31Hdev)\u001b[10;36H\u001b[38;5;2;49mokay\u001b[10;41H\u001b[39;49m──────────────────────────────────────\u001b[11;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[12;2H \u001b[12;4H \u001b[12;12H \u001b[12;30H \u001b[12;38H \u001b[12;49H \u001b[12;56H \u001b[12;60H \u001b[12;72H \u001b[12;76H \u001b[13;3H\u001b[38;5;6;49mrust-action\u001b[13;15H\u001b[39;49m(main\u001b[13;21H->\u001b[13;24Hnext\u001b[13;29H->\u001b[13;32Hdev)\u001b[13;37H\u001b[38;5;2;49mokay\u001b[13;42H\u001b[39;49m─────────────────────────────────────\u001b[14;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m feat: ensure toolchains are up-to-date \u001b[15;2H \u001b"]
[9.292207, "o", "[15;4H \u001b[15;12H \u001b[15;30H \u001b[15;36H \u001b[15;43H \u001b[15;54H \u001b[15;58H \u001b[16;3H\u001b[38;5;6;49mskip\u001b[16;8H\u001b[39;49m(main\u001b[16;14H->\u001b[16;17Hnext\u001b[16;22H->\u001b[16;25Hdev)\u001b[16;30H\u001b[38;5;2;49mokay\u001b[16;35H\u001b[39;49m────────────────────────────────────────────\u001b[17;2H* fc7fbc\u001b[17;11H \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[17;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to \u001b[18;2H \u001b[18;4H \u001b[18;12H \u001b[18;30H \u001b[18;43H \u001b[18;50H \u001b[18;65H \u001b[18;72H \u001b[18;76H \u001b[19;3H\u001b[38;5;6;49mtasyn\u001b[19;9H\u001b[39;49m(main\u001b[19;15H->\u001b[19;18Hnext\u001b[19;23H->\u001b[19;26Hdev)\u001b[19;31H\u001b[38;5;2;49mokay\u001b[19;36H\u001b[39;49m───────────────────────────────────────────\u001b[20;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/postgres:16.4-alpin\u001b[21;2H \u001b[21;4H \u001b[21"]
[9.292245, "o", ";12H \u001b[21;30H \u001b[21;43H \u001b[21;50H \u001b[22;3H\u001b[38;5;6;49mtest\u001b[22;8H\u001b[39;49m(main\u001b[22;14H->\u001b[22;17Hnext\u001b[22;22H->\u001b[22;25Hdev)\u001b[22;30H\u001b[38;5;2;49mokay\u001b[22;35H\u001b[39;49m────────────────────────────────────────────\u001b[23;2H* b7340df \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[23;30H\u001b[39;49mchore: update 6 \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.314279, "o", "\u001b[2;3H\u001b[38;5;15;48;5;1mkxio\u001b[2;8Hcommit is a Work-in-progress\u001b[2;37H\u001b[39;49m(main\u001b[2;43H->\u001b[2;46Hnext\u001b[2;51H->\u001b[2;54Hdev)\u001b[2;59H\u001b[38;5;1;49mALERT\u001b[2;65H\u001b[39;49m──────────────\u001b[3;2H* 805d0b3 \u001b[38;5;15;48;5;4m(dev)\u001b[39;49m WIP: docs(fs): add\u001b[3;37Hsome docum\u001b[3;48Hn\u001b[3;50Hation \u001b[3;59H \u001b[3;65H \u001b[4;4H9943a0f\u001b[4;13H\u001b[38;5;15;48;5;4mmain)(next)\u001b[39;49m \u001b[4;26Hhore(deps): update docker.\u001b[4;54H/rust\u001b[4;60Hdocker\u001b[4;67Htag\u001b[4;71Hto\u001b[4;74Hv1.81║\u001b[5;2H \u001b[5;4H \u001b[5;12H \u001b[5;25H \u001b[5;38H \u001b[5;45H \u001b[5;60H \u001b[5;67H \u001b[5;71H \u001b[5;74H \u001b[6;3H\u001b[38;5;6;49mpodal\u001b[6;9H\u001b[39;49m(main\u001b[6;15H->\u001b[6;18Hnext\u001b[6;23H->\u001b[6;26Hdev)\u001b[6;31H\u001b[38;5;2;49mokay\u001b[6;36H\u001b[39;49m───────────────────────────────────────────\u001b[7;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust crate scraper to 0.20 \u001b[8;2H \u001b[8;4H \u001b[8;12H "]
[9.314894, "o", " \u001b[8;30H \u001b[8;41H \u001b[8;48H \u001b[8;53H \u001b[8;59H \u001b[8;67H \u001b[8;70H \u001b[9;3H\u001b[38;5;6;49mrefile-m4b\u001b[9;14H\u001b[39;49m(main\u001b[9;20H->\u001b[9;23Hnext\u001b[9;28H->\u001b[9;31Hdev)\u001b[9;36H\u001b[38;5;2;49mokay\u001b[9;41H\u001b[39;49m──────────────────────────────────────\u001b[10;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[11;2H \u001b[11;4H \u001b[11;12H \u001b[11;30H \u001b[11;38H \u001b[11;49H \u001b[11;56H \u001b[11;60H \u001b[11;72H \u001b[11;76H \u001b[12;3H\u001b[38;5;6;49mrust-action\u001b[12;15H\u001b[39;49m(main\u001b[12;21H->\u001b[12;24Hnext\u001b[12;29H->\u001b[12;32Hdev)\u001b[12;37H\u001b[38;5;2;49mokay\u001b[12;42H\u001b[39;49m─────────────────────────────────────\u001b[13;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m feat: ensure toolchains are up-to-date █\u001b[14;2H \u001b[14;4H \u001b[14;12H \u001b[14;30H \u001b[14;36H \u001b[14;43H"]
[9.315054, "o", " \u001b[14;54H \u001b[14;58H \u001b[15;3H\u001b[38;5;6;49mskip\u001b[15;8H\u001b[39;49m(main\u001b[15;14H->\u001b[15;17Hnext\u001b[15;22H->\u001b[15;25Hdev)\u001b[15;30H\u001b[38;5;2;49mokay\u001b[15;35H\u001b[39;49m────────────────────────────────────────────\u001b[16;2H* fc7fbc\u001b[16;11H \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[16;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to \u001b[17;2H \u001b[17;4H \u001b[17;12H \u001b[17;30H \u001b[17;43H \u001b[17;50H \u001b[17;65H \u001b[17;72H \u001b[17;76H \u001b[18;3H\u001b[38;5;6;49mtasyn\u001b[18;9H\u001b[39;49m(main\u001b[18;15H->\u001b[18;18Hnext\u001b[18;23H->\u001b[18;26Hdev)\u001b[18;31H\u001b[38;5;2;49mokay\u001b[18;36H\u001b[39;49m───────────────────────────────────────────\u001b[19;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/postgres:16.4-alpin\u001b[20;2H \u001b[20;4H \u001b[20;12H \u001b[20;30H \u001b[20;43H \u001b[20;50H "]
[9.315195, "o", " \u001b[21;3H\u001b[38;5;6;49mtest\u001b[21;8H\u001b[39;49m(main\u001b[21;14H->\u001b[21;17Hnext\u001b[21;22H->\u001b[21;25Hdev)\u001b[21;30H\u001b[38;5;2;49mokay\u001b[21;35H\u001b[39;49m────────────────────────────────────────────\u001b[22;2H* b7340df \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[22;30H\u001b[39;49mchore: update 6 \u001b[23;2H \u001b[23;4H \u001b[23;12H \u001b[23;30H \u001b[23;37H \u001b[23;44H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.334731, "o", "\u001b[2;2H* 805d0b3 \u001b[38;5;15;48;5;4m(dev)\u001b[39;49m WIP: docs(fs): add\u001b[2;37Hsome docum\u001b[2;48Hn\u001b[2;50Hation \u001b[2;59H \u001b[2;65H \u001b[3;4H9943a0f\u001b[3;13H\u001b[38;5;15;48;5;4mmain)(next)\u001b[39;49m \u001b[3;26Hhore(deps): update docker.\u001b[3;54H/rust\u001b[3;60Hdocker\u001b[3;67Htag\u001b[3;71Hto\u001b[3;74Hv1.81\u001b[4;2H \u001b[4;4H \u001b[4;12H \u001b[4;25H \u001b[4;38H \u001b[4;45H \u001b[4;60H \u001b[4;67H \u001b[4;71H \u001b[4;74H \u001b[5;3H\u001b[38;5;6;49mpodal\u001b[5;9H\u001b[39;49m(main\u001b[5;15H->\u001b[5;18Hnext\u001b[5;23H->\u001b[5;26Hdev)\u001b[5;31H\u001b[38;5;2;49mokay\u001b[5;36H\u001b[39;49m───────────────────────────────────────────\u001b[6;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust crate scraper to 0.20 \u001b[7;2H \u001b[7;4H \u001b[7;12H \u001b[7;30H \u001b[7;41H \u001b[7;48H \u001b[7;53H \u001b[7;59H \u001b[7;67H \u001b[7;70H \u001b[8;3H\u001b[38;5;6;49mrefile-m4b\u001b[8;14H\u001b[39;49m(main\u001b[8;20H->\u001b[8;23Hnext\u001b[8;28H->\u001b[8;31Hdev)\u001b[8;36H\u001b[38;5;2;49mokay\u001b[8;4"]
[9.334863, "o", "1H\u001b[39;49m──────────────────────────────────────\u001b[9;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[10;2H \u001b[10;4H \u001b[10;12H \u001b[10;30H \u001b[10;38H \u001b[10;49H \u001b[10;56H \u001b[10;60H \u001b[10;72H \u001b[10;76H \u001b[11;3H\u001b[38;5;6;49mrust-action\u001b[11;15H\u001b[39;49m(main\u001b[11;21H->\u001b[11;24Hnext\u001b[11;29H->\u001b[11;32Hdev)\u001b[11;37H\u001b[38;5;2;49mokay\u001b[11;42H\u001b[39;49m─────────────────────────────────────\u001b[12;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m feat: ensure toolchains are up-to-date \u001b[13;2H \u001b[13;4H \u001b[13;12H \u001b[13;30H \u001b[13;36H \u001b[13;43H \u001b[13;54H \u001b[13;58H \u001b[14;3H\u001b[38;5;6;49mskip\u001b[14;8H\u001b[39;49m(main\u001b[14;14H->\u001b[14;17Hnext\u001b[14;22H->\u001b[14;25Hdev)\u001b[14;30H\u001b[38;5;2;49mokay\u001b[14;35H\u001b[39;49m──────────────"]
[9.334916, "o", "──────────────────────────────\u001b[15;2H* fc7fbc\u001b[15;11H \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[15;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to \u001b[16;2H \u001b[16;4H \u001b[16;12H \u001b[16;30H \u001b[16;43H \u001b[16;50H \u001b[16;65H \u001b[16;72H \u001b[16;76H \u001b[17;3H\u001b[38;5;6;49mtasyn\u001b[17;9H\u001b[39;49m(main\u001b[17;15H->\u001b[17;18Hnext\u001b[17;23H->\u001b[17;26Hdev)\u001b[17;31H\u001b[38;5;2;49mokay\u001b[17;36H\u001b[39;49m───────────────────────────────────────────\u001b[18;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/postgres:16.4-alpin\u001b[19;2H \u001b[19;4H \u001b[19;12H \u001b[19;30H \u001b[19;43H \u001b[19;50H \u001b[20;3H\u001b[38;5;6;49mtest\u001b[20;8H\u001b[39;49m(main\u001b[20;14H->\u001b[20;17Hnext\u001b[20;22H->\u001b[20;25Hdev)\u001b[20;30H\u001b[38;5;2;49mokay\u001b[20;35H\u001b[39;49m─────────────────────"]
[9.335083, "o", "───────────────────────\u001b[21;2H* b7340df \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[21;30H\u001b[39;49mchore: update 6 \u001b[22;2H \u001b[22;4H \u001b[22;12H \u001b[22;30H \u001b[22;37H \u001b[22;44H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.355977, "o", "\u001b[2;4H9943a0f\u001b[2;13H\u001b[38;5;15;48;5;4mmain)(next)\u001b[39;49m \u001b[2;26Hhore(deps): update docker.\u001b[2;54H/rust\u001b[2;60Hdocker\u001b[2;67Htag\u001b[2;71Hto\u001b[2;74Hv1.81\u001b[3;2H \u001b[3;4H \u001b[3;12H \u001b[3;25H \u001b[3;38H \u001b[3;45H \u001b[3;60H \u001b[3;67H \u001b[3;71H \u001b[3;74H \u001b[4;3H\u001b[38;5;6;49mpodal\u001b[4;9H\u001b[39;49m(main\u001b[4;15H->\u001b[4;18Hnext\u001b[4;23H->\u001b[4;26Hdev)\u001b[4;31H\u001b[38;5;2;49mokay\u001b[4;36H\u001b[39;49m───────────────────────────────────────────\u001b[5;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust crate scraper to 0.20 ║\u001b[6;2H \u001b[6;4H \u001b[6;12H \u001b[6;30H \u001b[6;41H \u001b[6;48H \u001b[6;53H \u001b[6;59H \u001b[6;67H \u001b[6;70H \u001b[7;3H\u001b[38;5;6;49mrefile-m4b\u001b[7;14H\u001b[39;49m(main\u001b[7;20H->\u001b[7;23Hnext\u001b[7;28H->\u001b[7;31Hdev)\u001b[7;36H\u001b[38;5;2;49mokay\u001b[7;41H\u001b[39;49m──────────────────────────────────────\u001b[8;2H* 7fb"]
[9.357512, "o", "a5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[9;2H \u001b[9;4H \u001b[9;12H \u001b[9;30H \u001b[9;38H \u001b[9;49H \u001b[9;56H \u001b[9;60H \u001b[9;72H \u001b[9;76H \u001b[10;3H\u001b[38;5;6;49mrust-action\u001b[10;15H\u001b[39;49m(main\u001b[10;21H->\u001b[10;24Hnext\u001b[10;29H->\u001b[10;32Hdev)\u001b[10;37H\u001b[38;5;2;49mokay\u001b[10;42H\u001b[39;49m─────────────────────────────────────\u001b[11;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m feat: ensure toolchains are up-to-date \u001b[12;2H \u001b[12;4H \u001b[12;12H \u001b[12;30H \u001b[12;36H \u001b[12;43H \u001b[12;54H \u001b[12;58H \u001b[13;3H\u001b[38;5;6;49mskip\u001b[13;8H\u001b[39;49m(main\u001b[13;14H->\u001b[13;17Hnext\u001b[13;22H->\u001b[13;25Hdev)\u001b[13;30H\u001b[38;5;2;49mokay\u001b[13;35H\u001b[39;49m────────────────────────────────────────────\u001b[14;2H* fc7fbc\u001b[14;11H \u001b[38;5;15;48;5;4m(dev)(main)(nex"]
[9.358399, "o", "t)\u001b[14;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to █\u001b[15;2H \u001b[15;4H \u001b[15;12H \u001b[15;30H \u001b[15;43H \u001b[15;50H \u001b[15;65H \u001b[15;72H \u001b[15;76H \u001b[16;3H\u001b[38;5;6;49mtasyn\u001b[16;9H\u001b[39;49m(main\u001b[16;15H->\u001b[16;18Hnext\u001b[16;23H->\u001b[16;26Hdev)\u001b[16;31H\u001b[38;5;2;49mokay\u001b[16;36H\u001b[39;49m───────────────────────────────────────────\u001b[17;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/postgres:16.4-alpin\u001b[18;2H \u001b[18;4H \u001b[18;12H \u001b[18;30H \u001b[18;43H \u001b[18;50H \u001b[19;3H\u001b[38;5;6;49mtest\u001b[19;8H\u001b[39;49m(main\u001b[19;14H->\u001b[19;17Hnext\u001b[19;22H->\u001b[19;25Hdev)\u001b[19;30H\u001b[38;5;2;49mokay\u001b[19;35H\u001b[39;49m────────────────────────────────────────────\u001b[20;2H* b7340df \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[20;30H\u001b[39;49mchore:"]
[9.358584, "o", " update 6 \u001b[21;2H \u001b[21;4H \u001b[21;12H \u001b[21;30H \u001b[21;37H \u001b[21;44H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.392711, "o", "\u001b[2;2H \u001b[2;4H \u001b[2;12H \u001b[2;25H \u001b[2;38H \u001b[2;45H \u001b[2;60H \u001b[2;67H \u001b[2;71H \u001b[2;74H \u001b[3;3H\u001b[38;5;6;49mpodal\u001b[3;9H\u001b[39;49m(main\u001b[3;15H->\u001b[3;18Hnext\u001b[3;23H->\u001b[3;26Hdev)\u001b[3;31H\u001b[38;5;2;49mokay\u001b[3;36H\u001b[39;49m───────────────────────────────────────────\u001b[4;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust crate scraper to 0.20 \u001b[5;2H \u001b[5;4H \u001b[5;12H \u001b[5;30H \u001b[5;41H \u001b[5;48H \u001b[5;53H \u001b[5;59H \u001b[5;67H \u001b[5;70H \u001b[6;3H\u001b[38;5;6;49mrefile-m4b\u001b[6;14H\u001b[39;49m(main\u001b[6;20H->\u001b[6;23Hnext\u001b[6;28H->\u001b[6;31Hdev)\u001b[6;36H\u001b[38;5;2;49mokay\u001b[6;41H\u001b[39;49m──────────────────────────────────────\u001b[7;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[8;2H \u001b[8;4H \u001b[8;12H \u001b[8;30H "]
[9.392818, "o", " \u001b[8;38H \u001b[8;49H \u001b[8;56H \u001b[8;60H \u001b[8;72H \u001b[8;76H \u001b[9;3H\u001b[38;5;6;49mrust-action\u001b[9;15H\u001b[39;49m(main\u001b[9;21H->\u001b[9;24Hnext\u001b[9;29H->\u001b[9;32Hdev)\u001b[9;37H\u001b[38;5;2;49mokay\u001b[9;42H\u001b[39;49m─────────────────────────────────────\u001b[10;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m feat: ensure toolchains are up-to-date \u001b[11;2H \u001b[11;4H \u001b[11;12H \u001b[11;30H \u001b[11;36H \u001b[11;43H \u001b[11;54H \u001b[11;58H \u001b[12;3H\u001b[38;5;6;49mskip\u001b[12;8H\u001b[39;49m(main\u001b[12;14H->\u001b[12;17Hnext\u001b[12;22H->\u001b[12;25Hdev)\u001b[12;30H\u001b[38;5;2;49mokay\u001b[12;35H\u001b[39;49m────────────────────────────────────────────\u001b[13;2H* fc7fbc\u001b[13;11H \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[13;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to \u001b[14;2H \u001b[14;4H \u001b[14;12H \u001b[14;30H \u001b[14;43H \u001b[14;50H "]
[9.392892, "o", " \u001b[14;65H \u001b[14;72H \u001b[14;76H \u001b[15;3H\u001b[38;5;6;49mtasyn\u001b[15;9H\u001b[39;49m(main\u001b[15;15H->\u001b[15;18Hnext\u001b[15;23H->\u001b[15;26Hdev)\u001b[15;31H\u001b[38;5;2;49mokay\u001b[15;36H\u001b[39;49m───────────────────────────────────────────\u001b[16;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/postgres:16.4-alpin\u001b[17;2H \u001b[17;4H \u001b[17;12H \u001b[17;30H \u001b[17;43H \u001b[17;50H \u001b[18;3H\u001b[38;5;6;49mtest\u001b[18;8H\u001b[39;49m(main\u001b[18;14H->\u001b[18;17Hnext\u001b[18;22H->\u001b[18;25Hdev)\u001b[18;30H\u001b[38;5;2;49mokay\u001b[18;35H\u001b[39;49m────────────────────────────────────────────\u001b[19;2H* b7340df \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[19;30H\u001b[39;49mchore: update 6 \u001b[20;2H \u001b[20;4H \u001b[20;12H \u001b[20;30H \u001b[20;37H \u001b[20;44H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.413689, "o", "\u001b[2;3H\u001b[38;5;6;49mpodal\u001b[2;9H\u001b[39;49m(main\u001b[2;15H->\u001b[2;18Hnext\u001b[2;23H->\u001b[2;26Hdev)\u001b[2;31H\u001b[38;5;2;49mokay\u001b[2;36H\u001b[39;49m───────────────────────────────────────────\u001b[3;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust crate scraper to 0.20 \u001b[4;2H \u001b[4;4H \u001b[4;12H \u001b[4;30H \u001b[4;41H \u001b[4;48H \u001b[4;53H \u001b[4;59H \u001b[4;67H \u001b[4;70H \u001b[5;3H\u001b[38;5;6;49mrefile-m4b\u001b[5;14H\u001b[39;49m(main\u001b[5;20H->\u001b[5;23Hnext\u001b[5;28H->\u001b[5;31Hdev)\u001b[5;36H\u001b[38;5;2;49mokay\u001b[5;41H\u001b[39;49m──────────────────────────────────────\u001b[6;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[7;2H \u001b[7;4H \u001b[7;12H \u001b[7;30H \u001b[7;38H \u001b[7;49H \u001b[7;56H \u001b[7;60H \u001b[7;72H \u001b[7;76H \u001b[8;3H\u001b[38;5;6;49mrust-action\u001b[8;15H\u001b[39;49m(main\u001b[8"]
[9.413775, "o", ";21H->\u001b[8;24Hnext\u001b[8;29H->\u001b[8;32Hdev)\u001b[8;37H\u001b[38;5;2;49mokay\u001b[8;42H\u001b[39;49m─────────────────────────────────────\u001b[9;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m feat: ensure toolchains are up-to-date \u001b[10;2H \u001b[10;4H \u001b[10;12H \u001b[10;30H \u001b[10;36H \u001b[10;43H \u001b[10;54H \u001b[10;58H \u001b[11;3H\u001b[38;5;6;49mskip\u001b[11;8H\u001b[39;49m(main\u001b[11;14H->\u001b[11;17Hnext\u001b[11;22H->\u001b[11;25Hdev)\u001b[11;30H\u001b[38;5;2;49mokay\u001b[11;35H\u001b[39;49m────────────────────────────────────────────\u001b[12;2H* fc7fbc\u001b[12;11H \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[12;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to \u001b[13;2H \u001b[13;4H \u001b[13;12H \u001b[13;30H \u001b[13;43H \u001b[13;50H \u001b[13;65H \u001b[13;72H \u001b[13;76H \u001b[14;3H\u001b[38;5;6;49mtasyn\u001b[14;9H\u001b[39;49m(main\u001b[14;15H->\u001b[14;18Hnext\u001b[14;23H->\u001b[14;26Hdev)\u001b"]
[9.413866, "o", "[14;31H\u001b[38;5;2;49mokay\u001b[14;36H\u001b[39;49m───────────────────────────────────────────\u001b[15;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker.io/postgres:16.4-alpin█\u001b[16;2H \u001b[16;4H \u001b[16;12H \u001b[16;30H \u001b[16;43H \u001b[16;50H \u001b[17;3H\u001b[38;5;6;49mtest\u001b[17;8H\u001b[39;49m(main\u001b[17;14H->\u001b[17;17Hnext\u001b[17;22H->\u001b[17;25Hdev)\u001b[17;30H\u001b[38;5;2;49mokay\u001b[17;35H\u001b[39;49m────────────────────────────────────────────\u001b[18;2H* b7340df \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[18;30H\u001b[39;49mchore: update 6 \u001b[19;2H \u001b[19;4H \u001b[19;12H \u001b[19;30H \u001b[19;37H \u001b[19;44H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.434833, "o", "\u001b[2;2H* 249b943 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m fix(deps): update rust crate scraper to 0.20 \u001b[3;2H \u001b[3;4H \u001b[3;12H \u001b[3;30H \u001b[3;41H \u001b[3;48H \u001b[3;53H \u001b[3;59H \u001b[3;67H \u001b[3;70H \u001b[4;3H\u001b[38;5;6;49mrefile-m4b\u001b[4;14H\u001b[39;49m(main\u001b[4;20H->\u001b[4;23Hnext\u001b[4;28H->\u001b[4;31Hdev)\u001b[4;36H\u001b[38;5;2;49mokay\u001b[4;41H\u001b[39;49m──────────────────────────────────────\u001b[5;2H* 7fba5fb \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m Support --version, --help and --directory cli arg\u001b[6;2H \u001b[6;4H \u001b[6;12H \u001b[6;30H \u001b[6;38H \u001b[6;49H \u001b[6;56H \u001b[6;60H \u001b[6;72H \u001b[6;76H ║\u001b[7;3H\u001b[38;5;6;49mrust-action\u001b[7;15H\u001b[39;49m(main\u001b[7;21H->\u001b[7;24Hnext\u001b[7;29H->\u001b[7;32Hdev)\u001b[7;37H\u001b[38;5;2;49mokay\u001b[7;42H\u001b[39;49m─────────────────────────────────────\u001b[8;2H* de4785c \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m f"]
[9.435142, "o", "eat: ensure toolchains are up-to-date \u001b[9;2H \u001b[9;4H \u001b[9;12H \u001b[9;30H \u001b[9;36H \u001b[9;43H \u001b[9;54H \u001b[9;58H \u001b[10;3H\u001b[38;5;6;49mskip\u001b[10;8H\u001b[39;49m(main\u001b[10;14H->\u001b[10;17Hnext\u001b[10;22H->\u001b[10;25Hdev)\u001b[10;30H\u001b[38;5;2;49mokay\u001b[10;35H\u001b[39;49m────────────────────────────────────────────\u001b[11;2H* fc7fbc\u001b[11;11H \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[11;30H\u001b[39;49mchore(deps): update docker.io/rust docker tag to \u001b[12;2H \u001b[12;4H \u001b[12;12H \u001b[12;30H \u001b[12;43H \u001b[12;50H \u001b[12;65H \u001b[12;72H \u001b[12;76H \u001b[13;3H\u001b[38;5;6;49mtasyn\u001b[13;9H\u001b[39;49m(main\u001b[13;15H->\u001b[13;18Hnext\u001b[13;23H->\u001b[13;26Hdev)\u001b[13;31H\u001b[38;5;2;49mokay\u001b[13;36H\u001b[39;49m───────────────────────────────────────────\u001b[14;2H* 24fe845 \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[39;49m chore(deps): update docker"]
[9.435248, "o", ".io/postgres:16.4-alpin\u001b[15;2H \u001b[15;4H \u001b[15;12H \u001b[15;30H \u001b[15;43H \u001b[15;50H \u001b[16;3H\u001b[38;5;6;49mtest\u001b[16;8H\u001b[39;49m(main\u001b[16;14H->\u001b[16;17Hnext\u001b[16;22H->\u001b[16;25Hdev)\u001b[16;30H\u001b[38;5;2;49mokay\u001b[16;35H\u001b[39;49m────────────────────────────────────────────\u001b[17;2H* b7340df \u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[17;30H\u001b[39;49mchore: update 6 \u001b[18;2H \u001b[18;4H \u001b[18;12H \u001b[18;30H \u001b[18;37H \u001b[18;44H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.472802, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.510706, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.547102, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.586478, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.621846, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.656939, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.694522, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.730343, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.768279, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.804285, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.844258, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.881454, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.920047, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.957121, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[9.995542, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.03548, "o", "\u001b[24;43H💚\u001b[24;45H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.072844, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.110417, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.146604, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.183643, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.223757, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.259221, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.298185, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.335671, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.377657, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.413226, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.449543, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.485979, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.530581, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.568398, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.588452, "o", "\u001b[2;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[3;2H*\u001b[3;4H249b943\u001b[3;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[3;30H\u001b[39;49mfix(deps):\u001b[3;41Hupdate\u001b[3;48Hrust\u001b[3;53Hcrate\u001b[3;59Hscraper\u001b[3;67Hto\u001b[3;70H0.20\u001b[4;3H \u001b[4;14H \u001b[4;20H \u001b[4;23H \u001b[4;28H \u001b[4;31H \u001b[4;36H \u001b[4;41H \u001b[5;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────\u001b[6;2H*\u001b[6;4H7fba5fb\u001b[6;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[6;30H\u001b[39;49mSupport\u001b[6;38H--version,\u001b[6;49H--help\u001b[6;56Hand\u001b[6;60H--directory\u001b[6;72Hcli\u001b[6;76Harg█\u001b[7;3H \u001b[7;15H \u001b[7;21H \u001b[7;24H \u001b[7;29H \u001b[7;32H \u001b[7;37H \u001b[7;42H \u001b[8;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> "]
[10.588626, "o", "next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────\u001b[9;2H*\u001b[9;4Hde4785c\u001b[9;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[9;30H\u001b[39;49mfeat:\u001b[9;36Hensure\u001b[9;43Htoolchains\u001b[9;54Hare\u001b[9;58Hup-to-date\u001b[10;3H \u001b[10;8H \u001b[10;14H \u001b[10;17H \u001b[10;22H \u001b[10;25H \u001b[10;30H \u001b[10;35H \u001b[11;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[11;11Hin -> next -> dev)\u001b[11;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[12;2H*\u001b[12;4Hfc7fbca\u001b[12;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[12;30H\u001b[39;49mchore(deps):\u001b[12;43Hupdate\u001b[12;50Hdocker.io/rust\u001b[12;65Hdocker\u001b[12;72Htag\u001b[12;76Hto\u001b[13;3H \u001b[13;9H \u001b[13;15H \u001b[13;18H \u001b[13;23H \u001b[13;26H \u001b[13;31H \u001b[13;36H \u001b[14;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─"]
[10.588685, "o", "──────────────────────────────────────────\u001b[15;2H*\u001b[15;4H24fe845\u001b[15;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[15;30H\u001b[39;49mchore(deps):\u001b[15;43Hupdate\u001b[15;50Hdocker.io/postgres:16.4-alpin\u001b[16;3H \u001b[16;8H \u001b[16;14H \u001b[16;17H \u001b[16;22H \u001b[16;25H \u001b[16;30H \u001b[16;35H \u001b[17;2H \u001b[38;5;6;49mtest\u001b[39;49m (main -> next -> dev)\u001b[17;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[18;2H*\u001b[18;4Hb7340df\u001b[18;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[18;30H\u001b[39;49mchore:\u001b[18;37Hupdate\u001b[18;44H6\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.607079, "o", "\u001b[2;3H \u001b[2;9H \u001b[2;15H \u001b[2;18H \u001b[2;23H \u001b[2;26H \u001b[2;31H \u001b[2;36H \u001b[3;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[4;2H*\u001b[4;4H249b943\u001b[4;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[4;30H\u001b[39;49mfix(deps):\u001b[4;41Hupdate\u001b[4;48Hrust\u001b[4;53Hcrate\u001b[4;59Hscraper\u001b[4;67Hto\u001b[4;70H0.20\u001b[5;3H \u001b[5;14H \u001b[5;20H \u001b[5;23H \u001b[5;28H \u001b[5;31H \u001b[5;36H \u001b[5;41H \u001b[6;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────\u001b[7;2H*\u001b[7;4H7fba5fb\u001b[7;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[7;30H\u001b[39;49mSupport\u001b[7;38H--version,\u001b[7;49H--help\u001b[7;56Hand\u001b[7;60H--directory\u001b[7;72Hcli\u001b[7;76Harg\u001b[8;3H \u001b[8;15H \u001b[8;21H \u001b[8;24H \u001b[8"]
[10.607191, "o", ";29H \u001b[8;32H \u001b[8;37H \u001b[8;42H \u001b[9;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────\u001b[10;2H*\u001b[10;4Hde4785c\u001b[10;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[10;30H\u001b[39;49mfeat:\u001b[10;36Hensure\u001b[10;43Htoolchains\u001b[10;54Hare\u001b[10;58Hup-to-date\u001b[11;3H \u001b[11;8H \u001b[11;14H \u001b[11;17H \u001b[11;22H \u001b[11;25H \u001b[11;30H \u001b[11;35H \u001b[12;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[12;11Hin -> next -> dev)\u001b[12;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[13;2H*\u001b[13;4Hfc7fbca\u001b[13;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[13;30H\u001b[39;49mchore(deps):\u001b[13;43Hupdate\u001b[13;50Hdocker.io/rust\u001b[13;65Hdocker\u001b[13;72Htag\u001b[13;76Hto\u001b[14;3H \u001b[14;9H \u001b[14;15H \u001b[14;18H \u001b[14;23H \u001b[14;26H \u001b[14;31H \u001b[14;36H "]
[10.607383, "o", " \u001b[15;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────║\u001b[16;2H*\u001b[16;4H24fe845\u001b[16;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[16;30H\u001b[39;49mchore(deps):\u001b[16;43Hupdate\u001b[16;50Hdocker.io/postgres:16.4-alpin\u001b[17;3H \u001b[17;8H \u001b[17;14H \u001b[17;17H \u001b[17;22H \u001b[17;25H \u001b[17;30H \u001b[17;35H \u001b[18;2H \u001b[38;5;6;49mtest\u001b[39;49m (main -> next -> dev)\u001b[18;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[19;2H*\u001b[19;4Hb7340df\u001b[19;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[19;30H\u001b[39;49mchore:\u001b[19;37Hupdate\u001b[19;44H6\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.625804, "o", "\u001b[2;2H*\u001b[2;4H9943a0f\u001b[2;12H\u001b[38;5;15;48;5;4m(main)(next)\u001b[2;25H\u001b[39;49mchore(deps):\u001b[2;38Hupdate\u001b[2;45Hdocker.io/rust\u001b[2;60Hdocker\u001b[2;67Htag\u001b[2;71Hto\u001b[2;74Hv1.81\u001b[3;3H \u001b[3;9H \u001b[3;15H \u001b[3;18H \u001b[3;23H \u001b[3;26H \u001b[3;31H \u001b[3;36H \u001b[4;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[5;2H*\u001b[5;4H249b943\u001b[5;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[5;30H\u001b[39;49mfix(deps):\u001b[5;41Hupdate\u001b[5;48Hrust\u001b[5;53Hcrate\u001b[5;59Hscraper\u001b[5;67Hto\u001b[5;70H0.20\u001b[6;3H \u001b[6;14H \u001b[6;20H \u001b[6;23H \u001b[6;28H \u001b[6;31H \u001b[6;36H \u001b[6;41H \u001b[7;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────\u001b[8;2H*\u001b[8;4H7fba5fb\u001b[8;12H\u001b[38;5;15;48;5;4m(dev)(ma"]
[10.625948, "o", "in)(next)\u001b[8;30H\u001b[39;49mSupport\u001b[8;38H--version,\u001b[8;49H--help\u001b[8;56Hand\u001b[8;60H--directory\u001b[8;72Hcli\u001b[8;76Harg\u001b[9;3H \u001b[9;15H \u001b[9;21H \u001b[9;24H \u001b[9;29H \u001b[9;32H \u001b[9;37H \u001b[9;42H \u001b[10;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────\u001b[11;2H*\u001b[11;4Hde4785c\u001b[11;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[11;30H\u001b[39;49mfeat:\u001b[11;36Hensure\u001b[11;43Htoolchains\u001b[11;54Hare\u001b[11;58Hup-to-date\u001b[12;3H \u001b[12;8H \u001b[12;14H \u001b[12;17H \u001b[12;22H \u001b[12;25H \u001b[12;30H \u001b[12;35H \u001b[13;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[13;11Hin -> next -> dev)\u001b[13;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[14;2H*\u001b[14;4Hfc7fbca\u001b[14;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[14;30H\u001b[39;49mchore(deps"]
[10.626011, "o", "):\u001b[14;43Hupdate\u001b[14;50Hdocker.io/rust\u001b[14;65Hdocker\u001b[14;72Htag\u001b[14;76Hto\u001b[15;3H \u001b[15;9H \u001b[15;15H \u001b[15;18H \u001b[15;23H \u001b[15;26H \u001b[15;31H \u001b[15;36H \u001b[16;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[17;2H*\u001b[17;4H24fe845\u001b[17;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[17;30H\u001b[39;49mchore(deps):\u001b[17;43Hupdate\u001b[17;50Hdocker.io/postgres:16.4-alpin\u001b[18;3H \u001b[18;8H \u001b[18;14H \u001b[18;17H \u001b[18;22H \u001b[18;25H \u001b[18;30H \u001b[18;35H \u001b[19;2H \u001b[38;5;6;49mtest\u001b[39;49m (main -> next -> dev)\u001b[19;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[20;2H*\u001b[20;4Hb7340df\u001b[20;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[20;30H\u001b[39;49mchore:\u001b[20;37Hupdate\u001b[20;44H6\u001b[39m\u001b[4"]
[10.626024, "o", "9m\u001b[59m\u001b[0m\u001b[?25l"]
[10.644844, "o", "\u001b[2;4H805d0b3\u001b[2;13H\u001b[38;5;15;48;5;4mdev)\u001b[39;49m WIP: do\u001b[2;26Hs(fs): add some documentat\u001b[2;54Hn \u001b[2;60H \u001b[2;67H \u001b[2;71H \u001b[2;74H \u001b[3;2H*\u001b[3;4H9943a0f\u001b[3;12H\u001b[38;5;15;48;5;4m(main)(next)\u001b[3;25H\u001b[39;49mchore(deps):\u001b[3;38Hupdate\u001b[3;45Hdocker.io/rust\u001b[3;60Hdocker\u001b[3;67Htag\u001b[3;71Hto\u001b[3;74Hv1.81\u001b[4;3H \u001b[4;9H \u001b[4;15H \u001b[4;18H \u001b[4;23H \u001b[4;26H \u001b[4;31H \u001b[4;36H \u001b[5;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────█\u001b[6;2H*\u001b[6;4H249b943\u001b[6;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[6;30H\u001b[39;49mfix(deps):\u001b[6;41Hupdate\u001b[6;48Hrust\u001b[6;53Hcrate\u001b[6;59Hscraper\u001b[6;67Hto\u001b[6;70H0.20\u001b[7;3H \u001b[7;14H \u001b[7;20H \u001b[7;23H \u001b[7;28H \u001b[7;31H \u001b[7;36H \u001b[7;41H \u001b[8;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────"]
[10.644992, "o", "─────────────────────────────────\u001b[9;2H*\u001b[9;4H7fba5fb\u001b[9;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[9;30H\u001b[39;49mSupport\u001b[9;38H--version,\u001b[9;49H--help\u001b[9;56Hand\u001b[9;60H--directory\u001b[9;72Hcli\u001b[9;76Harg\u001b[10;3H \u001b[10;15H \u001b[10;21H \u001b[10;24H \u001b[10;29H \u001b[10;32H \u001b[10;37H \u001b[10;42H \u001b[11;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────\u001b[12;2H*\u001b[12;4Hde4785c\u001b[12;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[12;30H\u001b[39;49mfeat:\u001b[12;36Hensure\u001b[12;43Htoolchains\u001b[12;54Hare\u001b[12;58Hup-to-date\u001b[13;3H \u001b[13;8H \u001b[13;14H \u001b[13;17H \u001b[13;22H \u001b[13;25H \u001b[13;30H \u001b[13;35H \u001b[14;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[14;11Hin -> next -> dev)\u001b[14;30H\u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────"]
[10.645137, "o", "───────────────────────║\u001b[15;2H*\u001b[15;4Hfc7fbca\u001b[15;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[15;30H\u001b[39;49mchore(deps):\u001b[15;43Hupdate\u001b[15;50Hdocker.io/rust\u001b[15;65Hdocker\u001b[15;72Htag\u001b[15;76Hto\u001b[16;3H \u001b[16;9H \u001b[16;15H \u001b[16;18H \u001b[16;23H \u001b[16;26H \u001b[16;31H \u001b[16;36H \u001b[17;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[18;2H*\u001b[18;4H24fe845\u001b[18;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[18;30H\u001b[39;49mchore(deps):\u001b[18;43Hupdate\u001b[18;50Hdocker.io/postgres:16.4-alpin\u001b[19;3H \u001b[19;8H \u001b[19;14H \u001b[19;17H \u001b[19;22H \u001b[19;25H \u001b[19;30H \u001b[19;35H \u001b[20;2H \u001b[38;5;6;49mtest\u001b[39;49m (main -> next -> dev)\u001b[20;30H\u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────"]
[10.645277, "o", "───────────────\u001b[21;2H*\u001b[21;4Hb7340df\u001b[21;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[21;30H\u001b[39;49mchore:\u001b[21;37Hupdate\u001b[21;44H6\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.663505, "o", "\u001b[2;2H \u001b[38;5;15;48;5;1mkxio\u001b[39;49m \u001b[38;5;15;48;5;1mcommit is a Work-in-progress\u001b[2;37H\u001b[39;49m(main -> n\u001b[2;48Hx\u001b[2;50H -> dev)\u001b[2;59H\u001b[38;5;1;49mALERT\u001b[2;65H\u001b[39;49m──────────────\u001b[3;4H805d0b3\u001b[3;13H\u001b[38;5;15;48;5;4mdev)\u001b[39;49m WIP: do\u001b[3;26Hs(fs): add some documentat\u001b[3;54Hn \u001b[3;60H \u001b[3;67H \u001b[3;71H \u001b[3;74H \u001b[4;2H*\u001b[4;4H9943a0f\u001b[4;12H\u001b[38;5;15;48;5;4m(main)(next)\u001b[4;25H\u001b[39;49mchore(deps):\u001b[4;38Hupdate\u001b[4;45Hdocker.io/rust\u001b[4;60Hdocker\u001b[4;67Htag\u001b[4;71Hto\u001b[4;74Hv1.81\u001b[5;3H \u001b[5;9H \u001b[5;15H \u001b[5;18H \u001b[5;23H \u001b[5;26H \u001b[5;31H \u001b[5;36H \u001b[6;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[7;2H*\u001b[7;4H249b943\u001b[7;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[7;30H\u001b[39;49mfix(deps):\u001b[7;41Hupdate\u001b[7;48Hrust\u001b[7;53Hcrate\u001b[7;59Hscraper\u001b[7;67Hto\u001b[7;70H0.20\u001b[8;3H \u001b["]
[10.663582, "o", "8;14H \u001b[8;20H \u001b[8;23H \u001b[8;28H \u001b[8;31H \u001b[8;36H \u001b[8;41H \u001b[9;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────\u001b[10;2H*\u001b[10;4H7fba5fb\u001b[10;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[10;30H\u001b[39;49mSupport\u001b[10;38H--version,\u001b[10;49H--help\u001b[10;56Hand\u001b[10;60H--directory\u001b[10;72Hcli\u001b[10;76Harg\u001b[11;3H \u001b[11;15H \u001b[11;21H \u001b[11;24H \u001b[11;29H \u001b[11;32H \u001b[11;37H \u001b[11;42H \u001b[12;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────\u001b[13;2H*\u001b[13;4Hde4785c\u001b[13;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[13;30H\u001b[39;49mfeat:\u001b[13;36Hensure\u001b[13;43Htoolchains\u001b[13;54Hare\u001b[13;58Hup-to-date\u001b[14;3H \u001b[14;8H \u001b[14;14H \u001b[14;17H \u001b[14;22H \u001b[14;25H \u001b[14;"]
[10.663631, "o", "30H \u001b[14;35H \u001b[15;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[15;11Hin -> next -> dev)\u001b[15;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[16;2H*\u001b[16;4Hfc7fbca\u001b[16;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[16;30H\u001b[39;49mchore(deps):\u001b[16;43Hupdate\u001b[16;50Hdocker.io/rust\u001b[16;65Hdocker\u001b[16;72Htag\u001b[16;76Hto\u001b[17;3H \u001b[17;9H \u001b[17;15H \u001b[17;18H \u001b[17;23H \u001b[17;26H \u001b[17;31H \u001b[17;36H \u001b[18;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[19;2H*\u001b[19;4H24fe845\u001b[19;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[19;30H\u001b[39;49mchore(deps):\u001b[19;43Hupdate\u001b[19;50Hdocker.io/postgres:16.4-alpin\u001b[20;3H \u001b[20;8H \u001b[20;14H \u001b[20;17H \u001b[20;22H \u001b[20;25H \u001b[20;30H \u001b[20;35H "]
[10.663766, "o", " \u001b[21;2H \u001b[38;5;6;49mtest\u001b[39;49m (main -> next -> dev)\u001b[21;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[22;2H*\u001b[22;4Hb7340df\u001b[22;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[22;30H\u001b[39;49mchore:\u001b[22;37Hupdate\u001b[22;44H6\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.682703, "o", "\u001b[2;3H \u001b[2;8H \u001b[2;37H \u001b[2;43H \u001b[2;46H \u001b[2;51H \u001b[2;54H \u001b[2;59H \u001b[2;65H \u001b[3;2H \u001b[38;5;15;48;5;1mkxio\u001b[39;49m \u001b[38;5;15;48;5;1mcommit is a Work-in-progress\u001b[3;37H\u001b[39;49m(main -> n\u001b[3;48Hx\u001b[3;50H -> dev)\u001b[3;59H\u001b[38;5;1;49mALERT\u001b[3;65H\u001b[39;49m──────────────\u001b[4;4H805d0b3\u001b[4;13H\u001b[38;5;15;48;5;4mdev)\u001b[39;49m WIP: do\u001b[4;26Hs(fs): add some documentat\u001b[4;54Hn \u001b[4;60H \u001b[4;67H \u001b[4;71H \u001b[4;74H █\u001b[5;2H*\u001b[5;4H9943a0f\u001b[5;12H\u001b[38;5;15;48;5;4m(main)(next)\u001b[5;25H\u001b[39;49mchore(deps):\u001b[5;38Hupdate\u001b[5;45Hdocker.io/rust\u001b[5;60Hdocker\u001b[5;67Htag\u001b[5;71Hto\u001b[5;74Hv1.81\u001b[6;3H \u001b[6;9H \u001b[6;15H \u001b[6;18H \u001b[6;23H \u001b[6;26H \u001b[6;31H \u001b[6;36H \u001b[7;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[8;2H*\u001b[8;4H249b943\u001b[8;12H\u001b[38;5;15;48;5;"]
[10.682851, "o", "4m(dev)(main)(next)\u001b[8;30H\u001b[39;49mfix(deps):\u001b[8;41Hupdate\u001b[8;48Hrust\u001b[8;53Hcrate\u001b[8;59Hscraper\u001b[8;67Hto\u001b[8;70H0.20\u001b[9;3H \u001b[9;14H \u001b[9;20H \u001b[9;23H \u001b[9;28H \u001b[9;31H \u001b[9;36H \u001b[9;41H \u001b[10;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────\u001b[11;2H*\u001b[11;4H7fba5fb\u001b[11;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[11;30H\u001b[39;49mSupport\u001b[11;38H--version,\u001b[11;49H--help\u001b[11;56Hand\u001b[11;60H--directory\u001b[11;72Hcli\u001b[11;76Harg\u001b[12;3H \u001b[12;15H \u001b[12;21H \u001b[12;24H \u001b[12;29H \u001b[12;32H \u001b[12;37H \u001b[12;42H \u001b[13;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────║\u001b[14;2H*\u001b[14;4Hde4785c\u001b[14;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[14;30H\u001b[39;49m"]
[10.683246, "o", "feat:\u001b[14;36Hensure\u001b[14;43Htoolchains\u001b[14;54Hare\u001b[14;58Hup-to-date\u001b[15;3H \u001b[15;8H \u001b[15;14H \u001b[15;17H \u001b[15;22H \u001b[15;25H \u001b[15;30H \u001b[15;35H \u001b[16;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[16;11Hin -> next -> dev)\u001b[16;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[17;2H*\u001b[17;4Hfc7fbca\u001b[17;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[17;30H\u001b[39;49mchore(deps):\u001b[17;43Hupdate\u001b[17;50Hdocker.io/rust\u001b[17;65Hdocker\u001b[17;72Htag\u001b[17;76Hto\u001b[18;3H \u001b[18;9H \u001b[18;15H \u001b[18;18H \u001b[18;23H \u001b[18;26H \u001b[18;31H \u001b[18;36H \u001b[19;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[20;2H*\u001b[20;4H24fe845\u001b[20;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[20;30H\u001b[39;49mchore(deps):\u001b[20;"]
[10.683342, "o", "43Hupdate\u001b[20;50Hdocker.io/postgres:16.4-alpin\u001b[21;3H \u001b[21;8H \u001b[21;14H \u001b[21;17H \u001b[21;22H \u001b[21;25H \u001b[21;30H \u001b[21;35H \u001b[22;2H \u001b[38;5;6;49mtest\u001b[39;49m (main -> next -> dev)\u001b[22;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[23;2H*\u001b[23;4Hb7340df\u001b[23;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[23;30H\u001b[39;49mchore:\u001b[23;37Hupdate\u001b[23;44H6\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.702671, "o", "\u001b[2;2H*\u001b[2;4H91c5973\u001b[2;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[2;30H\u001b[39;49mchore:\u001b[2;37Hrelease\u001b[3;3H \u001b[3;8H \u001b[3;37H \u001b[3;43H \u001b[3;46H \u001b[3;51H \u001b[3;54H \u001b[3;59H \u001b[3;65H \u001b[4;2H \u001b[38;5;15;48;5;1mkxio\u001b[39;49m \u001b[38;5;15;48;5;1mcommit is a Work-in-progress\u001b[4;37H\u001b[39;49m(main -> n\u001b[4;48Hx\u001b[4;50H -> dev)\u001b[4;59H\u001b[38;5;1;49mALERT\u001b[4;65H\u001b[39;49m──────────────\u001b[5;4H805d0b3\u001b[5;13H\u001b[38;5;15;48;5;4mdev)\u001b[39;49m WIP: do\u001b[5;26Hs(fs): add some documentat\u001b[5;54Hn \u001b[5;60H \u001b[5;67H \u001b[5;71H \u001b[5;74H \u001b[6;2H*\u001b[6;4H9943a0f\u001b[6;12H\u001b[38;5;15;48;5;4m(main)(next)\u001b[6;25H\u001b[39;49mchore(deps):\u001b[6;38Hupdate\u001b[6;45Hdocker.io/rust\u001b[6;60Hdocker\u001b[6;67Htag\u001b[6;71Hto\u001b[6;74Hv1.81\u001b[7;3H \u001b[7;9H \u001b[7;15H \u001b[7;18H \u001b[7;23H \u001b[7;26H \u001b[7;31H \u001b[7;36H \u001b[8;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────"]
[10.703019, "o", "─────────────────\u001b[9;2H*\u001b[9;4H249b943\u001b[9;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[9;30H\u001b[39;49mfix(deps):\u001b[9;41Hupdate\u001b[9;48Hrust\u001b[9;53Hcrate\u001b[9;59Hscraper\u001b[9;67Hto\u001b[9;70H0.20\u001b[10;3H \u001b[10;14H \u001b[10;20H \u001b[10;23H \u001b[10;28H \u001b[10;31H \u001b[10;36H \u001b[10;41H \u001b[11;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────\u001b[12;2H*\u001b[12;4H7fba5fb\u001b[12;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[12;30H\u001b[39;49mSupport\u001b[12;38H--version,\u001b[12;49H--help\u001b[12;56Hand\u001b[12;60H--directory\u001b[12;72Hcli\u001b[12;76Harg\u001b[13;3H \u001b[13;15H \u001b[13;21H \u001b[13;24H \u001b[13;29H \u001b[13;32H \u001b[13;37H \u001b[13;42H \u001b[14;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────"]
[10.703551, "o", "──────\u001b[15;2H*\u001b[15;4Hde4785c\u001b[15;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[15;30H\u001b[39;49mfeat:\u001b[15;36Hensure\u001b[15;43Htoolchains\u001b[15;54Hare\u001b[15;58Hup-to-date\u001b[16;3H \u001b[16;8H \u001b[16;14H \u001b[16;17H \u001b[16;22H \u001b[16;25H \u001b[16;30H \u001b[16;35H \u001b[17;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[17;11Hin -> next -> dev)\u001b[17;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[18;2H*\u001b[18;4Hfc7fbca\u001b[18;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[18;30H\u001b[39;49mchore(deps):\u001b[18;43Hupdate\u001b[18;50Hdocker.io/rust\u001b[18;65Hdocker\u001b[18;72Htag\u001b[18;76Hto\u001b[19;3H \u001b[19;9H \u001b[19;15H \u001b[19;18H \u001b[19;23H \u001b[19;26H \u001b[19;31H \u001b[19;36H \u001b[20;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────────"]
[10.703743, "o", "─\u001b[21;2H*\u001b[21;4H24fe845\u001b[21;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[21;30H\u001b[39;49mchore(deps):\u001b[21;43Hupdate\u001b[21;50Hdocker.io/postgres:16.4-alpin\u001b[22;3H \u001b[22;8H \u001b[22;14H \u001b[22;17H \u001b[22;22H \u001b[22;25H \u001b[22;30H \u001b[22;35H \u001b[23;2H \u001b[38;5;6;49mtest\u001b[39;49m (main -> next -> dev)\u001b[23;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.72275, "o", "\u001b[2;2H \u001b[38;5;6;49mgit-next\u001b[2;12H\u001b[39;49m(main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────\u001b[3;2H*\u001b[3;4H91c5973\u001b[3;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[3;30H\u001b[39;49mchore:\u001b[3;37Hrelease\u001b[3;79H█\u001b[4;3H \u001b[4;8H \u001b[4;37H \u001b[4;43H \u001b[4;46H \u001b[4;51H \u001b[4;54H \u001b[4;59H \u001b[4;65H \u001b[5;2H \u001b[38;5;15;48;5;1mkxio\u001b[39;49m \u001b[38;5;15;48;5;1mcommit is a Work-in-progress\u001b[5;37H\u001b[39;49m(main -> n\u001b[5;48Hx\u001b[5;50H -> dev)\u001b[5;59H\u001b[38;5;1;49mALERT\u001b[5;65H\u001b[39;49m──────────────\u001b[6;4H805d0b3\u001b[6;13H\u001b[38;5;15;48;5;4mdev)\u001b[39;49m WIP: do\u001b[6;26Hs(fs): add some documentat\u001b[6;54Hn \u001b[6;60H \u001b[6;67H \u001b[6;71H \u001b[6;74H \u001b[7;2H*\u001b[7;4H9943a0f\u001b[7;12H\u001b[38;5;15;48;5;4m(main)(next)\u001b[7;25H\u001b[39;49mchore(deps):\u001b[7;38Hupdate\u001b[7;45Hdocker.io/rust\u001b[7;60Hdocker\u001b[7;67Htag\u001b[7;71Hto\u001b[7;74Hv1.81\u001b[8;3H \u001b[8;9H \u001b[8;15H \u001b[8;18H \u001b[8;23H \u001b[8;26H "]
[10.7228, "o", "\u001b[8;31H \u001b[8;36H \u001b[9;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[10;2H*\u001b[10;4H249b943\u001b[10;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[10;30H\u001b[39;49mfix(deps):\u001b[10;41Hupdate\u001b[10;48Hrust\u001b[10;53Hcrate\u001b[10;59Hscraper\u001b[10;67Hto\u001b[10;70H0.20\u001b[11;3H \u001b[11;14H \u001b[11;20H \u001b[11;23H \u001b[11;28H \u001b[11;31H \u001b[11;36H \u001b[11;41H \u001b[12;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────\u001b[13;2H*\u001b[13;4H7fba5fb\u001b[13;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[13;30H\u001b[39;49mSupport\u001b[13;38H--version,\u001b[13;49H--help\u001b[13;56Hand\u001b[13;60H--directory\u001b[13;72Hcli\u001b[13;76Harg\u001b[14;3H \u001b[14;15H \u001b[14;21H \u001b[14;24H \u001b[14;29H \u001b[14;32H \u001b[14;37H "]
[10.723231, "o", " \u001b[14;42H \u001b[15;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────\u001b[16;2H*\u001b[16;4Hde4785c\u001b[16;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[16;30H\u001b[39;49mfeat:\u001b[16;36Hensure\u001b[16;43Htoolchains\u001b[16;54Hare\u001b[16;58Hup-to-date\u001b[17;3H \u001b[17;8H \u001b[17;14H \u001b[17;17H \u001b[17;22H \u001b[17;25H \u001b[17;30H \u001b[17;35H \u001b[18;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[18;11Hin -> next -> dev)\u001b[18;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[19;2H*\u001b[19;4Hfc7fbca\u001b[19;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[19;30H\u001b[39;49mchore(deps):\u001b[19;43Hupdate\u001b[19;50Hdocker.io/rust\u001b[19;65Hdocker\u001b[19;72Htag\u001b[19;76Hto\u001b[20;3H \u001b[20;9H \u001b[20;15H \u001b[20;18H \u001b[20;23H \u001b[20;26H \u001b[20;31H \u001b[20;36H "]
[10.723349, "o", " \u001b[21;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[22;2H*\u001b[22;4H24fe845\u001b[22;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[22;30H\u001b[39;49mchore(deps):\u001b[22;43Hupdate\u001b[22;50Hdocker.io/postgres:16.4-alpin\u001b[23;3H \u001b[23;8H \u001b[23;14H \u001b[23;17H \u001b[23;22H \u001b[23;25H \u001b[23;30H \u001b[23;35H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.742361, "o", "\u001b[2;3Hforge: jo \u001b[2;18H \u001b[2;21H \u001b[2;26H \u001b[2;29H \u001b[2;34H \u001b[2;39H \u001b[3;2H \u001b[38;5;6;49mgit-next\u001b[3;12H\u001b[39;49m(main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────\u001b[4;2H*\u001b[4;4H91c5973\u001b[4;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[4;30H\u001b[39;49mchore:\u001b[4;37Hrelease\u001b[5;3H \u001b[5;8H \u001b[5;37H \u001b[5;43H \u001b[5;46H \u001b[5;51H \u001b[5;54H \u001b[5;59H \u001b[5;65H \u001b[6;2H \u001b[38;5;15;48;5;1mkxio\u001b[39;49m \u001b[38;5;15;48;5;1mcommit is a Work-in-progress\u001b[6;37H\u001b[39;49m(main -> n\u001b[6;48Hx\u001b[6;50H -> dev)\u001b[6;59H\u001b[38;5;1;49mALERT\u001b[6;65H\u001b[39;49m──────────────\u001b[7;4H805d0b3\u001b[7;13H\u001b[38;5;15;48;5;4mdev)\u001b[39;49m WIP: do\u001b[7;26Hs(fs): add some documentat\u001b[7;54Hn \u001b[7;60H \u001b[7;67H \u001b[7;71H \u001b[7;74H \u001b[8;2H*\u001b[8;4H9943a0f\u001b[8;12H\u001b[38;5;15;48;5;4m(main)(next)\u001b[8;25H\u001b[39;49mchore(deps):\u001b[8;38Hupdate\u001b[8;45Hdocker.io/ru"]
[10.742451, "o", "st\u001b[8;60Hdocker\u001b[8;67Htag\u001b[8;71Hto\u001b[8;74Hv1.81\u001b[9;3H \u001b[9;9H \u001b[9;15H \u001b[9;18H \u001b[9;23H \u001b[9;26H \u001b[9;31H \u001b[9;36H \u001b[10;2H \u001b[38;5;6;49mpodal\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[11;2H*\u001b[11;4H249b943\u001b[11;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[11;30H\u001b[39;49mfix(deps):\u001b[11;41Hupdate\u001b[11;48Hrust\u001b[11;53Hcrate\u001b[11;59Hscraper\u001b[11;67Hto\u001b[11;70H0.20\u001b[12;3H \u001b[12;14H \u001b[12;20H \u001b[12;23H \u001b[12;28H \u001b[12;31H \u001b[12;36H \u001b[12;41H ║\u001b[13;2H \u001b[38;5;6;49mrefile-m4b\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ──────────────────────────────────────\u001b[14;2H*\u001b[14;4H7fba5fb\u001b[14;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[14;30H\u001b[39;49mSupport\u001b[14;38H--version,\u001b[14;49H--help\u001b[14;56Hand\u001b[14;60H--dire"]
[10.742513, "o", "ctory\u001b[14;72Hcli\u001b[14;76Harg\u001b[15;3H \u001b[15;15H \u001b[15;21H \u001b[15;24H \u001b[15;29H \u001b[15;32H \u001b[15;37H \u001b[15;42H \u001b[16;2H \u001b[38;5;6;49mrust-action\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ─────────────────────────────────────\u001b[17;2H*\u001b[17;4Hde4785c\u001b[17;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[17;30H\u001b[39;49mfeat:\u001b[17;36Hensure\u001b[17;43Htoolchains\u001b[17;54Hare\u001b[17;58Hup-to-date\u001b[18;3H \u001b[18;8H \u001b[18;14H \u001b[18;17H \u001b[18;22H \u001b[18;25H \u001b[18;30H \u001b[18;35H \u001b[19;2H \u001b[38;5;6;49mskip\u001b[39;49m (m\u001b[19;11Hin -> next -> dev)\u001b[19;30H\u001b[38;5;2;49mokay\u001b[39;49m ────────────────────────────────────────────\u001b[20;2H*\u001b[20;4Hfc7fbca\u001b[20;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[20;30H\u001b[39;49mchore(deps):\u001b[20;43Hupdate\u001b[20;50Hdocker.io/rust\u001b[20;65Hdocker\u001b[20;72Htag\u001b[20;76Hto\u001b"]
[10.742578, "o", "[21;3H \u001b[21;9H \u001b[21;15H \u001b[21;18H \u001b[21;23H \u001b[21;26H \u001b[21;31H \u001b[21;36H \u001b[22;2H \u001b[38;5;6;49mtasyn\u001b[39;49m (main -> next -> dev) \u001b[38;5;2;49mokay\u001b[39;49m ───────────────────────────────────────────\u001b[23;2H*\u001b[23;4H24fe845\u001b[23;12H\u001b[38;5;15;48;5;4m(dev)(main)(next)\u001b[23;30H\u001b[39;49mchore(deps):\u001b[23;43Hupdate\u001b[23;50Hdocker.io/postgres:16.4-alpin\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.761454, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.782349, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.801877, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.822526, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.842672, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.861559, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.879728, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.898859, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.917931, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.946604, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.966104, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[10.985743, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.023523, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.061011, "o", "\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.100465, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.138144, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.174063, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.211364, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.24819, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.285142, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.322272, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.358341, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.39547, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.431719, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.469256, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.511171, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.546092, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.583528, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.618694, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.657656, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.693252, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.731247, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.769971, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.804203, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.841036, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.877137, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.914426, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.954717, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[11.990908, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.028533, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.066414, "o", "\u001b[24;43H💚\u001b[24;45H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.107049, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.142204, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.179509, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.216573, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.253728, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.289769, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.326863, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.363905, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.40106, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.43865, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.475818, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.512054, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.550834, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.589195, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.626474, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.665855, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.702211, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.737993, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.774967, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.815764, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.852596, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.892064, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.92746, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[12.964137, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.002874, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.03968, "o", "\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.076753, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.113219, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.151584, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.188483, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.229106, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.264818, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.301622, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.338226, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.375524, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.411574, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.447307, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.483603, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.520106, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.560836, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.598547, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.635115, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.672651, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.712643, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.7507, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.786601, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.82323, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.858725, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.8978, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.934514, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[13.972287, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.008834, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.046299, "o", "\u001b[24;43H💚\u001b[24;45H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.083399, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.120763, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.155627, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.193875, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.231887, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.267898, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.302399, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.343537, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.380356, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.417261, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.453534, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.49263, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.530636, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.566577, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.606367, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.641865, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.679072, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.71686, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.760187, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.795183, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.83164, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.868972, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.905974, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.94637, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[14.983598, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.019429, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.056081, "o", "\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.093109, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.130406, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.172122, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.209038, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.244863, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.282047, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.316998, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.360829, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.39752, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.435025, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.476643, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.518269, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.551214, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.588339, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.626227, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.668075, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.704194, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.740791, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.778154, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.819102, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.855694, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.891253, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.927393, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[15.963477, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.000617, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.036768, "o", "\u001b[24;43H💚\u001b[24;45H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.074615, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.109168, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.147181, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.184001, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.221019, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.259995, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.298455, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.335449, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.374772, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.412157, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.45174, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.487307, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.525937, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.564883, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.602005, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.638765, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.674415, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.712126, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.746806, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.783087, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.821257, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.859364, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.897079, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.934126, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[16.970609, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.007439, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.043377, "o", "\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.081879, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.119148, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.156208, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.192544, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.229623, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.268441, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.304077, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.341245, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.377117, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.417434, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.456131, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.492909, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.530736, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.568137, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.608431, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.646151, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.683754, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.718481, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.75638, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.792562, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.829402, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.866012, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.903936, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.943019, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[17.984106, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.021665, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.059774, "o", "\u001b[24;43H💚\u001b[24;45H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.098821, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.135715, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.17206, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.208703, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.245904, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.283908, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.31957, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.355896, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.393339, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.429976, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.466756, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.506115, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.545975, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.583191, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.621038, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.656638, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.701071, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.739152, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.776111, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.81384, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.850448, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.888985, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.925942, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[18.962416, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[19.000353, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[19.039442, "o", "\u001b[24;43H 💚\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
[19.042413, "o", "\u001b[?25h"]
[19.042469, "o", "\u001b[?1049l"]
[19.110591, "o", "\u001b[?1049l"]

BIN
demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 KiB

View file

@ -47,24 +47,17 @@ grcov-coverage:
find . -name '*.profraw' -exec rm "{}" \; find . -name '*.profraw' -exec rm "{}" \;
echo "Now:\n\topen target/debug/coverage/index.html" echo "Now:\n\topen target/debug/coverage/index.html"
publish version: docker-build-builder:
#!/usr/bin/bash -e docker build -t git.kemitix.net/kemitix/git-next-builder:2024.08.04 -f Dockerfile.builder .
echo "Publishing git-next v{{version}} to crates.io..."
if [ -z $(git status --short) ]; then docker-build: docker-build-builder
echo "Worktree is clean - proceeding" docker build -t git.kemitix.net/kemitix/git-next:latest .
else
echo "Worktree is Dirty - aborting" ; exit docker-run: docker-build
fi docker run -it -p "7777:8888" -v .:/app/ git.kemitix.net/kemitix/git-next:latest server start --ui
git checkout v{{version}}
ORDER=$(cargo publish-workspace --target-version {{version}} --crate-prefix git-next --show-order 2>/dev/null | cut -d\ -f2-) run *args:
echo "Publishing crates in order: ${ORDER}" cargo run -- {{ args }}
# INFO: Why not use publish-workspace to publish? It doesn't support when crates-io registry is replaced
for P in ${ORDER} run-ui:
do just run server start --ui
echo "Publishing ${P}..."
cargo publish --registry crates-io -p $P
echo "Done: ${P}"
echo "======================================"
done
echo "All crates published"
git checkout dev