Keyboard shortcuts

Press โ† or โ†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Devlog 1: XVC Root and Python Bindings Debugging

I should have a dispatch method that receives an XvcRootOpt and runs a command with it. The dispatcher can also update the XvcRoot from None to Some(XvcRoot) in some cases, so it should receive a mutable XvcRootOpt or return one after receiving ownership.

The issue with having a mutable element is that we need to update XvcRoot from Arc<XvcRootInner> to Arc<RwLock<XvcRootInner>>, which will cause almost all machinery around XvcRoot to require locking the object first. This is too large a refactoring.

However, I can write an xvc_root! macro to replace xvc_root.read() or whatever is required to minimize the code changes. XvcRoot can also be a wrapper object, but thatโ€™s too much fuss, and the responsibility may be misplaced.

Letโ€™s update the type and let the dispatcher update XvcRootInner only when necessary. Letโ€™s see what will require updating.


We have a bug in the Python bindings where xvc.file.track() enters an infinite loop.

There can be multiple reasons, but are you sure that the xvc CLI works with the same command?

Letโ€™s start testing by creating another notebook server.

The notebook creates an Xvc repo successfully and returns the root with xvc.root(), but xvc.file().track("test-data/dir-0001/") never finishes.

Itโ€™s likely that this is caused by something in the background threads.

The CLI command completes successfully, but we may be passing the command incorrectly. Letโ€™s try dir-0001 only.

Even if an incorrect path is provided, it shouldnโ€™t enter an infinite loop.

When I provide the correct path, dir-0001, it returns. There may be something going on with finding the root of the Xvc repo.

It returns, but itโ€™s also giving an error that it cannot find a repository. I should take a closer look.

The Xvc struct wasnโ€™t implementing Debug, so I added it.

Letโ€™s add it to others, XvcFile, etc.

Added it. Recompiling to get more info when we run track with an incorrect path.

You can also make it run after a commit to avoid starting a new server if one is already running.

Added conditionals, and now it runs the server if none is running in the background.

PORT=7979
if [[ ! -s "$(ps ax | rg -v ps | rg jupyter-lab | rg $PORT)" ]] ; then
  jupyter lab --port=$PORT --notebook-dir=Readme/ &
  open http://localhost:${PORT}/lab/workspaces/auto-H/tree/Readme.ipynb
fi

devlog 2

We were debugging Python bindings for Xvc. xvc.file.track enters an infinite loop when given a non-existent path.

To make debugging easier, we can add file deletions in the ./start-readme script to the notebook itself.

Instead, Iโ€™ll run my watcher run-after-commit.sh to ensure that the files are deleted after commits.

That may also work; you can use both as well.

I noticed cli-opts pass --no-system-config etc. by default. Letโ€™s deal with this first.

I fixed that.

Iโ€™m testing whether we are in the directory that we should be in with xvc.root("--absolute"), but it looks like itโ€™s not possible to pass a string argument to the command.

Let me take a look at this.

It looks like there have been some changes in optional parameter handling in PyO3. You may need to deal with keyword arguments with decorators.

Now, letโ€™s try xvc.file().track() once more with dir-0001/.

It seems to be working now. With an existing directory, it doesnโ€™t show an error.

What does xvc.file().list() show?

It shows a single string with \n in it. It looks like we need to handle this in the output thread.

I added a replace("\\n", "\n") to output_str at the end, but it didnโ€™t make a difference. I tested in the notebook with list_result.split("\n"), and these \n characters are indeed CRLF. So Jupyter shows CRLF in strings with \n, and this is not something we should try to fix, I think.

You can search how to show \n characters in a Jupyter notebook with CRLF.

The output string should be fed into repr, as in:

# Define a string with CRLF characters
text_crlf = "Hello\r\nWorld\r\nThis is a test string."

# Use repr to show the \r\n characters explicitly
print(repr(text_crlf))

I tested this, and GPT misleads. repr is when you want to show \n, not vice versa. When I print(list_result), it prints the results properly.

It looks like we donโ€™t need to make this a priority now. We can tell the user in the notebook that the commands are intentionally returning strings, and they can process or print them however they want.

devlog 3

Letโ€™s begin this session by removing debug statements from both the Xvc library and the Python bindings.

Another issue is the restart script. The if in that script that restarts the server doesnโ€™t work; it always restarts the notebook server.

I removed some println! statements from xvc.py. There doesnโ€™t seem to be anything in the library related to outputs.

Letโ€™s search for how to check if a jupyter-lab command with the port 7979 runs in the background.

It looks like the bug is in the condition; itโ€™s not -s, itโ€™s -z.

Oops, yeah.

Letโ€™s do a bit of tidying and check if the script is fixed.

I have a clippy warning with a new function that says these usually donโ€™t take self as a parameter. This is for the pipeline new command, and it receives a self as an XvcPipeline object. It seems best to turn off the clippy warning for this.

I allowed two clippy warnings, and the script seems to work fine.

Letโ€™s go on to copying the content from the Xvc README to the notebook.

There is an issue with the run-after-commit script. When Xvc commits the changes, the command we give is run again.

The issue is that git initializes the directory in the test-data/ directory, while xvc works in the current directory. I think we can either git init in the current directory or xvc init in test-data.

Weโ€™re already deleting .git and .xvc directories in the start-readme script. I think it may be easier to update git init to just initialize in the current directory.

Yep, letโ€™s do it that way.

I see there are still extra outputs from the commands. We need to deal with this first.

I removed them, but there are still pink outputs. These are from dbg! statements, it looks like, or weโ€™re initializing the output thread incorrectly.

Fixed those as well. I wrote up the xvc file list command examples as well. Now we have issues with xvc storage new s3 not running, and not even showing any debug output. Weโ€™ll deal with it in the next devlog, though.

devlog 4

Letโ€™s start by looking at the debug output issue. We can start by replacing the eprintln! macros with println!, perhaps.

I replaced the eprintln!s with println!, but it didnโ€™t make any difference. Maybe we should remove those statements completely.

I canโ€™t really find the place that kills the kernel. The last command to run is xvc file list:

print(xvc_test_data.file().list("test-data/dir-0002"))

and the output it produces is:

[src/output.rs:144:13] &output_str = "SS         131 2024-06-05 08:57:11 41e16be7          test-data/dir-0002/file-0003.bin\nSS         131 2024-06-05 08:57:11 27f0
efd0          test-data/dir-0002/file-0002.bin\nSS         131 2024-06-05 08:57:11 66de5084          test-data/dir-0002/file-0001.bin\nTotal #: 3 Workspace Size:
      393 Cached Size:        6006\n"

print may be causing the crash, but the more likely cause is the command that comes after this:

!ls -l test-data/dir-0001/

I replaced this with lsd, which also failed. Maybe itโ€™s actually a Python crash or bug.

The way to understand is to create a notebook file with only that cell and try to run it.

The ls line runs fine with a new notebook. It even runs on the README file when run at the beginning. The line that makes the kernel crash is:

xvc_test_data.storage().new_s3(name="backup", bucket_name="xvc-test", region="eu-central-1", storage_prefix="xvc-storage")

We can start by removing the new_s3 part.

The storage() method runs fine. It returns an XvcStorage() object, as it should.

When I run storage().list(), it takes a very long time. The bug is likely related to storage().

It looks like the storage object was adding file instead of storage as a subcommand. Iโ€™ve fixed it now.

That was the bug. The README notebook now creates the S3 storage.

What was the reason behind this?

Parsing the CLI to the XvcCLI object was perhaps the culprit. Letโ€™s look at it more clearly.

Letโ€™s try xvc file new s3 as a command to see how it behaves.

It says unrecognized subcommand for new.

This is how it should be, but I wonder why it doesnโ€™t work for the XvcCLI parser.

Anyway, itโ€™s already 13:00, so letโ€™s stop here for today.

devlog 5

While writing Xvc tests for Python, I hit an error caused by the ECS single-load protection.

The single-loader allows only one instance of Xvc to be run in a single process. This is no problem for the shell, but it looks like it wonโ€™t be possible to use multiple Xvc instances in a single Python process.

Itโ€™s possible to overcome this with an elaborate multiprocessing setup in the wrapper, but I wonโ€™t bother with it for now.

devlog 6

I have a habit of testing against the CLIโ€™s help string output. It allows me to keep the documentation up to date and makes me aware of any undocumented options. When new features are added, the help text changes and the test fails, which prompts me to add those options to the documentation.

I tried the same approach when testing Python bindings with Pytest:

def test_pipeline_step_dependency(empty_xvc_repo):
    dep_help = empty_xvc_repo.pipeline().step().dependency(help=True)
    expected = """
Usage: xvc pipeline step dependency [OPTIONS] --step-name <STEP_NAME>

Options:
  -s, --step-name <STEP_NAME>
          Name of the step to add the dependency to
"""
    assert dep_help == expected

This doesnโ€™t work because the help text is generated by clap and skips the usual thread-based output handler. All command output and errors in Xvc are returned as strings from the command, except for the help text thatโ€™s generated by clap automatically.

There are probably workarounds for this, but I wonโ€™t pursue them further as I donโ€™t test the actual functionality in the Python bindings anyway.

devlog 7

I noticed that I often forget to update the Xvc CHANGELOG. To fix this, I added a pre-push hook that checks the files Iโ€™m pushing. If the CHANGELOG is not among them and there are changes to Rust files, it prevents the push. I hope this will remind me to update the logs more frequently.

#!/bin/bash

# Git pre-push hook to check if CHANGELOG.md is included in the push and if the branch is develop

# Get the current branch name

current_branch=$(git rev-parse --abbrev-ref HEAD)

# Check if the branch is develop

if [ "$current_branch" == "main" ]; then
	echo "You are on the main branch. Skipping CHANGELOG.md check."
	exit 0
fi

# remote="$1"
# url="$2"

# Get the list of commits to be pushed
commits=$(git rev-list '@{u}..HEAD')

has_rust_files=$(false)

# TODO: We can iterate to get file list only once
# Get the list of files that are going to be pushed
for commit in $commits; do
	if git diff-tree --no-commit-id --name-only -r "${commit}" | grep -q "\\.rs$"; then
		has_rust_files=$(true)
	fi
done

if [[ ! $has_rust_files ]]; then
	echo "No .rs files in the push, no need to check CHANGELOG"
	exit 0
fi

# Check if CHANGELOG.md is among the files in the commits
for commit in $commits; do
	if git diff-tree --no-commit-id --name-only -r "${commit}" | grep -q "CHANGELOG.md"; then
		echo "CHANGELOG.md is included in the push."
		exit 0
	fi
done

echo "ERROR: CHANGELOG.md is not included in the push."
exit 1

Edit (2025-02-08): Added a check for when only code files are changed.

devlog 8

๐Ÿข The only failure was the patch coverage in Xvc. Letโ€™s see what needs to be done.

๐Ÿ‡ It looks, from the coverage page, that our additions to HStore donโ€™t have any tests. Maybe we can add some unit tests to the new joins.

๐Ÿข I donโ€™t find unit tests particularly useful, but letโ€™s use GitHub Copilot to add some for us.

๐Ÿ‡ I added a unit test and a doc test for full_join. I think doc tests have more value; they provide documentation, and we can readily see how to use a function from its docs. Itโ€™s better to increase coverage with doc tests.

๐Ÿข There are things I should keep in mind while writing doc tests. Imports must use the full path, not crate. Also, the tested struct doesnโ€™t have implicit imports.

๐Ÿ‡ The ceremony for adding keys and values is a bit too much. It might be worthwhile to add an insert method for anything that implements Into<XvcEntity>.

๐Ÿข That would certainly save timeโ€”no more typing .into() for each key! :)

๐Ÿ‡ Pushed to test again. Should we have some way to test coverage locally?

๐Ÿข I donโ€™t think we need to check coverage locally. Itโ€™s not worth our time right now.

๐Ÿ‡ Now, while waiting for the tests to complete, what else can we do?

gh -R iesahin/xvc run list
completed failure v0.6.13 Rust-CI v0.6.13 pull_request 12543831360 3m54s 2024-12-30T08:15:06Z
...

๐Ÿข That didnโ€™t take too long. Letโ€™s view the results:

gh -R iesahin/xvc run view 12543831360
...
  X Run Current Dev Tests
...
To see what failed, try: 
View this run on GitHub: https://github.com/iesahin/xvc/actions/runs/12543831360

๐Ÿข The current dev tests are failing for some reason. Letโ€™s run them locally.

๐Ÿ‡ Weโ€™re missing llvm-tools-preview locally. How do I install this?

๐Ÿข The command is rustup component add llvm-tools-preview:

info: component 'llvm-tools' for target 'aarch64-apple-darwin' is up to date

๐Ÿ‡ Itโ€™s already installed. We just need to set the environment variables.

๐Ÿข Instead, we can just turn off dev tests for the time being. We donโ€™t really need them; our local tests pass.

๐Ÿ‡ Yeah, okay. We donโ€™t need to solve every single bit of these issues right now.

gh -R iesahin/xvc run list
...

๐Ÿ‡ Okay, letโ€™s take a look at the run again.

gh -R iesahin/xvc run list
completed failure v0.6.13 Rust-CI v0.6.13 pull_request 12544020064 3m39s 2024-12-30T08:33:25Z

gh -R iesahin/xvc run view 12544020064
...
  X Test and Coverage
...

gh run view 12544020064 --log-failed
...
Test and Coverage (stable) Test and Coverage 2024-12-30T08:36:58.8911690Z Error: ProcessError { stdout: "", stderr: "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\ncurl: (7) Failed to connect to e1.xvc.dev port 80 after 160 ms: Couldn't connect to server\n" }
...

๐Ÿข We need to start Nginx on the server. We forgot to do that yesterday.

๐Ÿ‡ Ah, right. After that dufs installation. Okay.

๐Ÿข We also need to add a reverse proxy for dufs somehow, but thatโ€™s for later.

๐Ÿ‡ For this use case, I donโ€™t think itโ€™s necessary. We can just adjust the port to a non-standard one if we need 443 for something else. Letโ€™s look at the tests again.

๐Ÿข Letโ€™s add another doc test, this time to XvcStore.

cargo test -p xvc-ecs --doc
...
test result: ok. 8 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 2.87s

๐Ÿ‡ Sent the files again. Waiting for the tests to finish.

๐Ÿข Letโ€™s check the keymaps file in the meantime.

๐Ÿ‡ I tried reading the documentation but didnโ€™t see an error. Maybe we should just set it to non-lazy and disallow remaps.

๐Ÿข Weโ€™ve already spent too much time on this.

๐Ÿ‡ Yes, letโ€™s check the tests again.

gh -R iesahin/xvc run list
completed success v0.6.13 Rust-CI v0.6.13 pull_request 12544291068 8m10s 2024-12-30T09:00:42Z
...

๐Ÿข Oh, nice, the merge is ready.

๐Ÿ‡ Patch coverage is still behind the target, though.

๐Ÿข Yeah, but letโ€™s release this one and ensure the next one is better covered. Also, Iโ€™m not sure if the doc tests actually affected the coverage report.

๐Ÿ‡ If we look at the coverage page again, we can see.

๐Ÿข It seems codecov.io doesnโ€™t consider coverage for doc tests. Thatโ€™s a bit weird, but letโ€™s not spend more time on it.

๐Ÿ‡ Sure, letโ€™s merge.

๐Ÿข I think we forgot to bump the versions in the Cargo.toml files. Weโ€™ll have to do that in main.

๐Ÿ‡ Oh, yeah. Letโ€™s bump them and tag the release as well.

๐Ÿข Now we can wait for all the files to be produced. Whatโ€™s next?

๐Ÿ‡ We can release the Python version too. It shouldnโ€™t need any changes.

๐Ÿข Right. Maybe we can add a few tests there as well.

๐Ÿ‡ Letโ€™s bump the version first and see.

๐Ÿข Iโ€™ve bumped the versions in Cargo.toml and run maturin develop.

๐Ÿ‡ It seems ready now.

๐Ÿข The main branch is failing, though. The โ€œPublish cratesโ€ action is looking for libsqlite3. Letโ€™s investigate.

gh -R iesahin/xvc run list
completed failure Release v0.6.13 Publish Crates v0.6.13 push 12544753359 6m1s 2024-12-30T09:41:56Z

๐Ÿข The issue is that the VM doesnโ€™t have libsqlite3-dev. Letโ€™s add it.

๐Ÿ‡ We need to restart the job manually. Letโ€™s skip tagging this time.

๐Ÿข Some of the packages were already published, and now theyโ€™re causing failures because crates.io says they already exist. Maybe we can check if a package is already published before trying.

๐Ÿ‡ Letโ€™s see if we can make cargo publish more forgiving.

๐Ÿข There doesnโ€™t seem to be an easy option. Letโ€™s search for โ€œhow to skip published packages in workspace to avoid errors with cargo publish.โ€

๐Ÿ‡ Claude is hallucinating again. Letโ€™s try a manual approach: how to skip already published packages?

๐Ÿข It might be easier to just add a check. How do we get that info?

๐Ÿ‡ Or we can just move on to the next package if one is already available.

๐Ÿข Letโ€™s push the missing packages manually this time.

๐Ÿข We should have a key to open garden files quickly. What does Fzf-Lua files receive as arguments?

๐Ÿ‡ Letโ€™s check the help page: fzf-lua.

๐Ÿข Before that, maybe we can try to fix why selected lines are not searched in Visual-Line mode?

๐Ÿ‡ Yep, letโ€™s fix our search first.

๐Ÿข How do you set a key in visual line mode in Neovim Lua?

๐Ÿ‡ The abbreviation is V. Letโ€™s try that.

๐Ÿข Looks like we need to restart the session.

๐Ÿ‡ Itโ€™s still not working. Letโ€™s check :map.

๐Ÿข It seems it needs more care; Iโ€™ll check it later.

๐Ÿ‘จ๐Ÿพโ€๐Ÿฆฒ ๐Ÿข Bence kendini biraz daha anlamlฤฑ bir iลŸle uฤŸraลŸtฤฑrmalฤฑsฤฑn. ๐Ÿ‡ Ne gibi? ๐Ÿข Belki biraz daha yayฤฑn yapmalฤฑsฤฑn, biraz daha iลŸe baลŸvurmalฤฑsฤฑn. ๐Ÿ‡ โ€œMeliโ€, โ€œmalฤฑโ€ ile geรงiyor รถmrรผmรผz.

โŒš ๐Ÿข It looks like we can move most of the daily template to links or commands. We can call it ref/daily. My idea is to fill the page intentionally, without templates.

๐Ÿ‡ It might be better to start with a blank page, yeah. The template makes me a bit nervous. There are too many things to fill in, and most of them arenโ€™t things I like being pushed into doing.

๐Ÿข Letโ€™s start by moving the daily templates to ref/daily. No more daily template.

๐Ÿ‡ Now we can delete the rest of this page. Weโ€™ll use the daily links page and maybe have reminders at the end of these sessions.

๐Ÿ‘จ๐Ÿฝโ€โš•๏ธ

devlog 9

๐Ÿข Today Iโ€™m planning to add cross-compilation to the Xvc 0.6.13 branch to provide more platform support.

๐Ÿ‡ It looks like you first need to turn off this ghost text from the blink output. It makes writing insufferable.

๐Ÿข Yep, letโ€™s do that first.

๐Ÿ‡ Now, letโ€™s restart Neovim.

๐Ÿข I donโ€™t know why blink.cmp doesnโ€™t prioritize the emojis I use. Maybe we can just use #tor and #rab for ourselves.

๐Ÿ‡ I think over time it will learn that the emojis I defined in the snippets file should have higher priority, but letโ€™s skip this for now. What do we need to do to add cross-compilation?

๐Ÿข Maybe we can just make the completion menu wait a bit longer. It shows up almost instantly, and I want it to wait for a few more milliseconds.

๐Ÿ‡ Okay, letโ€™s look at the config.

๐Ÿข The configuration file doesnโ€™t seem to have a key for this. Letโ€™s search: blink.cmp.

๐Ÿ‡ I think the culprit is typo resistance; it causes better options to be pushed down: https://cmp.saghen.dev/configuration/reference#fuzzy

๐Ÿข Letโ€™s turn that off.

๐Ÿ‡ There is an error in the configuration file. I donโ€™t know why itโ€™s failing.

๐Ÿข I added emojis to Espanso and checked the error message. The configuration I copied from their docs seems to be broken. The message is:

...share/nvim/lazy/blink.cmp/lua/blink/cmp/config/utils.lua:14: fuzzy.max_items: unexpected field found in configuration

Iโ€™ll just delete that line.

๐Ÿ‡ It still doesnโ€™t prioritize snippets, but we can look into this later. Espanso seems to be a better tool for this anyway.

๐Ÿข Yep. Letโ€™s look into cross-compilation support for Rust.

๐Ÿ‡ The well-known option is cross.rs: https://github.com/cross-rs/cross

๐Ÿข We can start with that. Installation is from the Git repository:

$ cargo install cross --git https://github.com/cross-rs/cross
    Updating git repository `https://github.com/cross-rs/cross`
    Updating git submodule `https://github.com/cross-rs/cross-toolchains.git`
  Installing cross v0.2.5 (https://github.com/cross-rs/cross#4090beca)
...
   Installed package `cross v0.2.5 (https://github.com/cross-rs/cross#4090beca)` (executables `cross`, `cross-util`)

๐Ÿ‡ Now we have the cross and cross-util commands. Cross-compilation requires Podman on Linux or Docker on macOS. Do we have Docker?

๐Ÿข It looks like we donโ€™t. Maybe we can just set up a remote build using Podman or GitHub Actions. I saw a crate for that yesterday; I remember saving it somewhere but canโ€™t find it now. Searching again seems easier, which says something about my archival and retrieval habits.

๐Ÿ‡ Maybe later you can add some vector search capabilities to your archiveโ€”semantic search.

๐Ÿข Yep, sometime later.

๐Ÿ‡ Now letโ€™s search for โ€œadding rust cross compilation to github actions.โ€

๐Ÿข I found a link to the action: https://github.com/marketplace/actions/build-rust-projects-with-cross

Letโ€™s look at the example:

jobs:
  release:
    name: Release - ${{ matrix.platform.os-name }}
    strategy:
      matrix:
        platform:
          - os-name: FreeBSD-x86_64
            runs-on: ubuntu-20.04
            target: x86_64-unknown-freebsd
            skip_tests: true

          - os-name: Linux-x86_64
            runs-on: ubuntu-20.04
            target: x86_64-unknown-linux-musl

          - os-name: Linux-aarch64
            runs-on: ubuntu-20.04
            target: aarch64-unknown-linux-musl

          - os-name: Linux-riscv64
            runs-on: ubuntu-20.04
            target: riscv64gc-unknown-linux-gnu

          - os-name: Windows-x86_64
            runs-on: windows-latest
            target: x86_64-pc-windows-msvc

          - os-name: macOS-x86_64
            runs-on: macOS-latest
            target: x86_64-apple-darwin

          # more targets here ...

    runs-on: ${{ matrix.platform.runs-on }}
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Build binary
        uses: houseabsolute/actions-rust-cross@v0
        with:
           command: ${{ matrix.platform.command }}
          target: ${{ matrix.platform.target }}
          args: "--locked --release"
          strip: true
      - name: Publish artifacts and release
        uses: houseabsolute/actions-rust-release@v0
        with:
          executable-name: ubi
          target: ${{ matrix.platform.target }}

๐Ÿ‡ We can just convert the current configuration to this and see if it works.

๐Ÿข Letโ€™s do that. I created .github/workflows/release.yml and will update it.

๐Ÿ‡ Letโ€™s check the results with gh:

$ gh -R iesahin/xvc run list
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525070531	34s	2024-12-28T08:17:28Z

๐Ÿข It says the release has completed.

$ gh -R iesahin/xvc run view 12525070531

X v0.6.13 Release iesahin/xvc#263 ยท 12525070531
Triggered via pull_request about 3 minutes ago

JOBS
X Release - FreeBSD-x86_64 in 12s (ID 34936257653)
  โœ“ Set up job
  โœ“ Checkout
  X Build binary
  - Publish artifacts and release
  โœ“ Post Build binary
  โœ“ Post Checkout
  โœ“ Complete job
...

Now letโ€™s look at the failure:

$ gh -R iesahin/xvc run view 12525070531 --log-failed

๐Ÿ‡ Change the order and see what happens. Maybe we should first succeed in a non-cross-compilation build.

$ gh -R iesahin/xvc run list
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525178664	35s	2024-12-28T08:34:18Z
...
$ gh -R iesahin/xvc run view   12525178664 --log-failed
...

๐Ÿข Itโ€™s the same error. The usage example is actually much more sophisticated.

๐Ÿ‡ The issue is that weโ€™re asking for command from the matrix, but the matrix doesnโ€™t define it. Thatโ€™s the second time today an example from the documentation has failed. Iโ€™ve added command for each platform. We can also add separate features this way to make platform-specific functionality work.

๐Ÿข Agreed. Letโ€™s look at it once more.

$ gh -R iesahin/xvc run list | rg Release | head -n 1
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525253982	41s	2024-12-28T08:46:23Z

$ gh -R iesahin/xvc run view 12525253982 --log-failed
...
Release - macOS-x86_64	Build binary	2024-12-28T08:46:44.8464010Z  [1m [31merror [0m [1m: [0m the lock file /Users/runner/work/xvc/xvc/Cargo.lock needs to be updated but --locked was passed to prevent this

๐Ÿ‡ Ah, thatโ€™s a different error. Letโ€™s remove the --locked flag and retry.

$ gh -R iesahin/xvc run list | rg Release | head -n 1
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525289176	1m8s	2024-12-28T08:53:23Z

๐Ÿข Weโ€™re finally starting to get some good news. Now weโ€™re getting OpenSSL errors. We need feature flags for these platforms or to specify where OpenSSL is. Letโ€™s add bundled-openssl to the failed ones. We also need bundled-sqlite for Windows binaries.

gh -R iesahin/xvc run list | rg Release | head -n 1
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525354179	3m9s	2024-12-28T09:04:55Z

๐Ÿ‡ It looks like the Changes.md file is missing, and it canโ€™t upload the binaries as a release because of this. Iโ€™ll set the changes file to CHANGELOG.md.

๐Ÿข Itโ€™s weird to fail because of that. I think we should report these errors and possibly send a PR to make that file optional.

gh -R iesahin/xvc run list | rg Release | head -n 1
in_progress		v0.6.13	Release	v0.6.13	pull_request	12525449368	1m38s	2024-12-28T09:18:40Z
...
โœ“ Release - macOS-x86_64 in 2m39s (ID 34937017695)
โœ“ Release - macOS-aarch64 in 2m42s (ID 34937017787)
..
ARTIFACTS
xvc-macOS-x86_64.tar.gz
xvc-macOS-arm64.tar.gz

๐Ÿข It looks like Linux-riscv64 has OpenSSL compilation errors. I think we can skip this platform for now. The goal was to add aarch64 for macOS, and that seems to have succeeded.

gh -R iesahin/xvc run list | rg Release | head -n 1
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525504079	3m24s	2024-12-28T09:26:43Z
...
X Release - Linux-x86_64	Build binary	2024-12-28T09:29:16.2262518Z  [0m [1m [38;5;9merror[E0308] [0m [0m [1m: mismatched types [0m
...

๐Ÿข It looks like Linux-x86_64 doesnโ€™t support reflinks. Maybe we can make reflinks an optional feature and add it specifically to macOS and Windows targets.

๐Ÿ‡ Iโ€™ve removed reflink from the default features. This is a breaking change, but fortunately we donโ€™t have many users who will be affected by it.

Letโ€™s check the results once more.

gh -R iesahin/xvc run list | rg Release | head -n 1
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525597866	3m39s	2024-12-28T09:42:44Z

๐Ÿข Now turn off the NetBSD target as well.

๐Ÿ‡ FreeBSD and Linux-x86_64 targets are building. Letโ€™s see why the ARM Linux targets are failing.

gh -R iesahin/xvc run list | rg Release | head -n 1
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525643284	4m46s	2024-12-28T09:52:04Z

๐Ÿ‡ It looks like those targets donโ€™t have libsqlite3 installed. Letโ€™s add bundled-sqlite to these targets too.

gh -R iesahin/xvc run list | rg Release | head -n 1
completed	failure	v0.6.13	Release	v0.6.13	pull_request	12525734325	4m11s	2024-12-28T10:07:36Z

๐Ÿข Adding Android as a target didnโ€™t work. Letโ€™s remove it for now; it seems to require more work.

๐Ÿ‡ I think weโ€™ll eventually move to using Xvc to distribute binaries. We could build the Android binary on Termux and link it on the releases page or push it as a release artifact.

๐Ÿข I need to learn more about GitHub releases. If we can add artifacts to the release, maybe we can do some of this work locally.

๐Ÿ‡ We can start by looking at the capabilities of gh commands.

๐Ÿข It looks like itโ€™s possible to upload assets to releases. Letโ€™s check how that works.

gh release upload --help

๐Ÿ‡ So basically, we can just upload files to tags. We can list releases and work with them like anything else.

gh -R iesahin/xvc release list

And we can delete releases:

for r in v0.4.2-alpha.8 v0.4.2-alpha.7 v0.4.2-alpha.6 v0.4.2-alpha.5 v0.4.2-alpha.0 v0.4.1-alpha.0; do
  gh -R iesahin/xvc release delete "${r}"
done

๐Ÿข Now letโ€™s list them again.

gh -R iesahin/xvc release list

๐Ÿ‡ The latest one has failed again.

X Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run

๐Ÿข It looks like weโ€™re coming to the end of this session. Under what conditions will we create a release?

๐Ÿ‡ I think itโ€™s better to release only non-alpha tags.

๐Ÿข Then the rule in the workflow will be something like:

on:
  workflow_dispatch:
  push:
    tags:
      - "v*.*.*"
      - "!v.*.*-alpha.*"

๐Ÿ‡ And now we have this result:

โœ“ v0.6.13 Release iesahin/xvc#263 ยท 12533498361
Triggered via pull_request about 21 minutes ago

JOBS
โœ“ Release - FreeBSD-x86_64 in 4m14s
โœ“ Release - Linux-x86_64 in 4m3s
โœ“ Release - Linux-aarch64 in 4m50s
โœ“ Release - Windows-x86_64 in 8m47s
โœ“ Release - Windows-aarch64 in 6m42s
โœ“ Release - macOS-x86_64 in 2m37s
โœ“ Release - macOS-aarch64 in 2m36s

๐Ÿข Nice! Letโ€™s check the release list.

gh -R iesahin/xvc release list

๐Ÿ‡ Why is there no โ€œlatestโ€ release?

๐Ÿข We have to tag it first.

๐Ÿ‡ Ah, right. Letโ€™s tag it then.

๐Ÿข Iโ€™ve pushed the changes and tagged them with v0.6.13-alpha.5.

๐Ÿ‡ I think itโ€™s possible to make a release today.

๐Ÿข It looks like it, yes.

๐Ÿ‡ We can merge the PR and tag it. Then everything should work.

๐Ÿข We need some cleanup in the YAML files, though.

๐Ÿ‡ We can leave that for the next release.

gh -R iesahin/xvc run list
in_progress		v0.6.13	Rust-CI	v0.6.13	pull_request	12533690474	9m18s	2024-12-29T08:02:29Z
completed	success	Release	Release	v0.6.13-alpha.5	push	12533689099	9m11s	2024-12-29T08:02:19Z

๐Ÿข Now we have another failure in the regular CI. Letโ€™s look at it.

๐Ÿ‡ The issue seems to be in the doc tests:

- Total #: 8 Workspace Size:         276 Cached Size:          19
+ Total #: 8 Workspace Size:         278 Cached Size:          19

๐Ÿข These tests are brittle, but they provide valuable information. Letโ€™s fix it and push again.

๐Ÿ‡ Done. We can also remove some of the watches that produce so many logs.

๐Ÿข Iโ€™m a bit ambivalent about them. I thought we could use these watches when debugging, but experience has shown that we need more granular watches during debugging and almost never use these otherwise. Letโ€™s remove some of them.

๐Ÿ‡ We can increase the output for certain commands, but the tracing output doesnโ€™t help much in regular runs. If Xvc gets popular enough that we canโ€™t cope with bug reports, we can always add more watches.

๐Ÿข Another option is to exclude the watch code from the release build, but that wonโ€™t change anything for our debug cycles.

๐Ÿ‡ I think removing them is a fair trial. We can always put them back when debugging.

๐Ÿข Watches could also produce regular output instead of tracing. That way we wonโ€™t forget to remove them.

๐Ÿ‡ Ah, yep, thatโ€™s a good option too.

๐Ÿข We can have a trace! macro similar to the current one for user consumption, and a watch! macro that sends output to stderr.

๐Ÿ‡ Good idea. Letโ€™s do that in the next release.

๐Ÿข Letโ€™s check the tests before pushing this cleanup.

gh -R iesahin/xvc run list
completed	success	v0.6.13	Rust-CI	v0.6.13	pull_request	12533993607	13m49s	2024-12-29T08:47:50Z

๐Ÿ‡ CI succeeded, and the release didnโ€™t run. Letโ€™s do some more cleanup.

gh -R iesahin/xvc run list
in_progress		v0.6.13	Rust-CI	v0.6.13	pull_request	12534416152	43s	2024-12-29T09:57:02Z

Devlog 10: Codecov, Doctests, and Cargo Publish Struggles

๐Ÿข Now weโ€™re going into the real deep work. The only failure was the Codecov patch in Xvc. Letโ€™s see what needs to be done.

๐Ÿ‡ It looks, from the coverage page, that our additions to HStore donโ€™t have any tests. We can add some unit tests to new joins, maybe.

๐Ÿข I donโ€™t find unit tests particularly useful, but letโ€™s use GitHub Copilot to add tests for us.

๐Ÿ‡ Added a unit test and a doc test for full_join, and I think doc tests have more value. They provide documentation, and we can readily see how to use a function from its docs. Better to increase coverage with doc tests.

๐Ÿข There are points that I should learn while writing doc tests. The imports must use the full path, not crate::. The tested struct also doesnโ€™t have implicit imports.

๐Ÿ‡ The ceremony of adding keys and values is a bit too much. It may be worthwhile to add insert for any Into<XvcEntity>.

๐Ÿข It will certainly save time if we donโ€™t have to type .into() for each key :)

๐Ÿ‡ Pushed to test again. Should we have some means to test coverage locally?

๐Ÿข I donโ€™t think we need to consider coverage locally. Itโ€™s not worth our time.

๐Ÿ‡ Now while waiting for tests to be completed, what can we do?

gh -R iesahin/xvc run list
completed	failure	v0.6.13	Rust-CI	v0.6.13	pull_request	12543831360	3m54s	2024-12-30T08:15:06Z
...

๐Ÿข I donโ€™t think it took too much time. Letโ€™s view the results:

gh -R iesahin/xvc run view 12543831360
...
  X Run Current Dev Tests
...
To see what failed, try: 
View this run on GitHub: https://github.com/iesahin/xvc/actions/runs/12543831360

๐Ÿข The current dev tests fail for some reason. Letโ€™s run these locally.

๐Ÿ‡ Weโ€™re missing llvm-tools-preview locally. How do we install this?

๐Ÿข The command is:

rustup component add llvm-tools-preview
info: component 'llvm-tools' for target 'aarch64-apple-darwin' is up to date

๐Ÿ‡ Itโ€™s already installed. We need to set the environment variables.

๐Ÿข Instead, we can just turn off dev tests for the time being. We donโ€™t need them. Our local tests pass.

๐Ÿ‡ Yeah, ok, we donโ€™t need to solve each and every bit of these issues.

gh -R iesahin/xvc run list

โ€ฆ ๐Ÿ‡ Ok. Letโ€™s take a look at the run again.

gh -R iesahin/xvc run list
completed	failure	v0.6.13	Rust-CI	v0.6.13	pull_request	12544020064	3m39s	2024-12-30T08:33:25Z

gh -R iesahin/xvc run view 12544020064
...
  X Test and Coverage
...

gh run view 12544020064 --log-failed
...
Test and Coverage (stable)	Test and Coverage	2024-12-30T08:36:58.8911690Z Error: ProcessError { stdout: "", stderr: "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\ncurl: (7) Failed to connect to e1.xvc.dev port 80 after 160 ms: Couldn't connect to server\n" }
...

๐Ÿข We need to start nginx on the server. We forgot it yesterday.

๐Ÿ‡ Ah, yeah. After adding that dufs installation. Ok.

๐Ÿข We also need to add a reverse proxy to dufs somehow, but this is for later.

๐Ÿ‡ For the use case, I donโ€™t think itโ€™s necessary. We can just adjust the port to a non-standard one if we need to use 443 for another thing, but letโ€™s take a look at the tests again.

๐Ÿข Letโ€™s add another doc test. This time to XvcStore.

cargo test -p xvc-ecs --doc
...
test result: ok. 8 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 2.87s

๐Ÿ‡ Sent the files again. Waiting for tests to finish.

๐Ÿข Letโ€™s check the keymaps file in the meantime.

๐Ÿ‡ I tried to read some documentation but didnโ€™t see an error. Maybe we should just set it to non-lazy and avoid allowing remaps.

๐Ÿข We already spent too much time with this.

๐Ÿ‡ Yes, letโ€™s take a look at the tests again.

gh -R iesahin/xvc run list
completed	success	v0.6.13	Rust-CI	v0.6.13	pull_request	12544291068	8m10s	2024-12-30T09:00:42Z
...

๐Ÿข Oh, yeah, the merge is ready.

๐Ÿ‡ Patch coverage is still behind the target, though.

๐Ÿข Yeah, but letโ€™s release this one and make the next better covered. Also, Iโ€™m not sure if the doc tests had any effect on coverage.

๐Ÿ‡ If we look at the coverage page again, we can see if the doc tests had any effect.

๐Ÿข It seems codecov.io doesnโ€™t consider coverage for doc tests. This is a bit weird, but letโ€™s not spend more time on this.

๐Ÿ‡ Sure, letโ€™s merge.

๐Ÿข I think we forgot to bump the version in Cargo.toml. Weโ€™ll have to do that in main.

๐Ÿ‡ Oh, yeah. Letโ€™s bump it and tag as well.

๐Ÿข Now, we can wait for all files to be produced. What will we do next?

๐Ÿ‡ We can just release the Python version as well. It shouldnโ€™t need any changes.

๐Ÿข Umm, right. Maybe we can add a few tests as well.

๐Ÿ‡ Letโ€™s bump the version first and see.

๐Ÿข Bumped versions in Cargo.toml and ran maturin develop.

๐Ÿ‡ It seems ready now.

๐Ÿข The main fails, though. The โ€œPublish Cratesโ€ action looks for libsqlite3. Letโ€™s take a look at it.

gh -R iesahin/xvc run list
completed	failure	Release v0.6.13	Publish Crates	v0.6.13	push	12544753359	6m1s	2024-12-30T09:41:56Z

๐Ÿข The issue is that the VM doesnโ€™t have libsqlite3-dev. Letโ€™s add it.

๐Ÿ‡ We need to start the job manually again. Letโ€™s not tag this time.

๐Ÿข Some of the packages were already published. Now they break. crates.io says theyโ€™re already published. Maybe we can check if a package is already published.

๐Ÿ‡ Letโ€™s check if we can make cargo publish more forgiving.

๐Ÿข There doesnโ€™t seem to be an option. Letโ€™s search โ€œhow to skip published packages in the workspace to avoid errors with cargo publishโ€.

๐Ÿ‡ Claude is bullshitting again. Letโ€™s try a manual approach: how to skip already published packages.

๐Ÿข It may be easier to just add a check if the package is published. How do we get the info?

๐Ÿ‡ Or we can just go on to the next package if the package is already available.

๐Ÿข Letโ€™s do this manually this time.

Devlog 11: Homebrew Taps and Automation

๐Ÿข Letโ€™s move slowly. Letโ€™s create a brew project first.

๐ŸฆŠ There is a brew create command, but could we really copy it from someone else?

๐Ÿข I read the espanso example, and Iโ€™ll start by adding a repository. Welcome https://github.com/iesahin/homebrew-xvc

๐ŸฆŠ We can search to automate this for Rust stuff first. Maybe itโ€™s easier to automate than to do it manually with examples.

๐Ÿข Letโ€™s search โ€œhow to automate homebrew tap formula updates with github actionsโ€

๐Ÿฒ There is a shell script that updates the brew repo with a shell script. It fires the action from the main repository with a command like:

gh workflow run release.yml -f version=${{ env.VERSION }} -R itspriddle/homebrew-slack-notify

๐Ÿข It looks a little brittle, though.

๐Ÿฒ There is also a GitHub action: https://github.com/marketplace/actions/homebrew-tap but it doesnโ€™t look very popular. There is another one https://github.com/marketplace/actions/bump-homebrew-formula but this is for formulas, or the default settings are those.

๐Ÿข There is an example https://github.com/marketplace/actions/bump-homebrew-formula#examples for taps as well.

๐Ÿฒ This one is simpler, and probably we can just add this first: https://github.com/marketplace/actions/homebrew-bump-formula Most of the fields are optional.

๐Ÿ‡ I think this is much easier than the other. Letโ€™s start with this.

๐Ÿข Now letโ€™s add a token to the Xvc repo. โœ…

๐ŸฆŠ Add a branch to Xvc and add the release action file.

๐Ÿข Added the release action file ~/github.com/iesahin/xvc/.github/workflows/homebrew.yml, but we still donโ€™t have a tap; maybe we can just add one.

๐ŸฆŠ Can you check espansoโ€™s for example?

๐Ÿข It has a cask and is in the core. We need a tap example.

๐Ÿฒ I believe we need to clone homebrew-xvc and run gh workflow run.

๐Ÿข Yes, letโ€™s clone the repo and try to run it manually.

๐Ÿ‡ We donโ€™t have a formula yet.

๐ŸฆŠ Whatโ€™s the directory structure of a Homebrew tap project?

๐Ÿข Itโ€™s something like this, but we donโ€™t need tests for this, I believe.

.
โ”œโ”€โ”€ Formula
โ”‚   โ””โ”€โ”€ <formula_name>.rb
โ”œโ”€โ”€ LICENSE
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ .github
    โ””โ”€โ”€ workflows
        โ””โ”€โ”€ tests.yml

๐Ÿ‡ What goes into the Formula?

๐ŸฆŠ Letโ€™s take a look at ripgrepโ€™s example:

class Ripgrep < Formula
  desc "Search tool like grep and The Silver Searcher"
  homepage "https://github.com/BurntSushi/ripgrep"
  url "https://github.com/BurntSushi/ripgrep/archive/refs/tags/14.1.1.tar.gz"
  sha256 "4dad02a2f9c8c3c8d89434e47337aa654cb0e2aa50e806589132f186bf5c2b66"
  license "Unlicense"
  head "https://github.com/BurntSushi/ripgrep.git", branch: "master"

  livecheck do
    url :stable
    strategy :github_latest
  end

  bottle do
    sha256 cellar: :any,                 arm64_sequoia:  "b8bf5e73c9c9b441de067ec86ac167b071ecc2078dcb1d89d2cebbb151feab35"
    sha256 cellar: :any,                 arm64_sonoma:   "47b9c3515c866b147f0e98735cab165d6471b9f28fab1ba2c57e59c43da5c10b"
    sha256 cellar: :any,                 arm64_ventura:  "e14a94e84c028ff53c1be3b106fdeb5aca4d7c893a819e7fb967e0719b946a28"
    sha256 cellar: :any,                 arm64_monterey: "ad8dc4ab475c84e2a1e60f5b3107f52dd59e33f84a08284b19681d8b98508fd7"
    sha256 cellar: :any,                 sonoma:         "71d434eeabc2af220285b037f7264563ce9bc77a41af35eabe2213276a37ec2b"
    sha256 cellar: :any,                 ventura:        "0cdb547c696992d08c6613c40934218964f4a061b5413c4b2f013c3f0c3ed253"
    sha256 cellar: :any,                 monterey:       "2ce54302e4524ad28389aca5a16333d4193128e911de2881e6b0e953559d89cd"
    sha256 cellar: :any_skip_relocation, x86_64_linux:   "97d7cbd33b4d0ed09551e3dbc07f830d3df018c2aefbb2222a12ccfb829aae30"
  end

  depends_on "asciidoctor" => :build
  depends_on "pkgconf" => :build
  depends_on "rust" => :build
  depends_on "pcre2"

  def install
    system "cargo", "install", "--features", "pcre2", *std_cargo_args

    generate_completions_from_executable(bin/"rg", "--generate", shell_parameter_format: "complete-")
    (man1/"rg.1").write Utils.safe_popen_read(bin/"rg", "--generate", "man")
  end

  test do
    (testpath/"Hello.txt").write("Hello World!")
    system bin/"rg", "Hello World!", testpath
  end
end

๐Ÿ‡ Whatโ€™s in that tar file?

wget https://github.com/BurntSushi/ripgrep/archive/refs/tags/14.1.1.tar.gz 
--2025-01-03 06:28:07--  https://github.com/BurntSushi/ripgrep/archive/refs/tags/14.1.1.tar.gz
Resolving github.com (github.com)... 140.82.121.3
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/BurntSushi/ripgrep/tar.gz/refs/tags/14.1.1 [following]
--2025-01-03 06:28:08--  https://codeload.github.com/BurntSushi/ripgrep/tar.gz/refs/tags/14.1.1
Resolving codeload.github.com (codeload.github.com)... 140.82.121.10
Connecting to codeload.github.com (codeload.github.com)|140.82.121.10|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: โ€˜14.1.1.tar.gzโ€™

     0K .......... .......... .......... .......... ..........  355K
    50K .......... .......... .......... .......... .......... 1.05M
   100K .......... .......... .......... .......... .......... 1.35M
   150K .......... .......... .......... .......... .......... 3.61M
   200K .......... .......... .......... .......... ..........  876K
   250K .......... .......... .......... .......... .......... 6.50M
   300K .......... .......... .......... .......... .......... 1.15M
   350K .......... .......... .......... .......... .......... 1.65M
   400K .......... .......... .......... .......... .......... 4.88M
   450K .......... .......... .......... .......... .......... 1.22M
   500K .......... .......... .......... .......... .......... 1.56M
   550K .......... .......                                     3.62M=0.5s

2025-01-03 06:28:09 (1.21 MB/s) - โ€˜14.1.1.tar.gzโ€™ saved [581402]

mv 14.1.1.tar.gz $HOME/Downloads/ripgrep-14.1.1.tar.gz

tar xvzf $HOME/Downloads/ripgrep-14.1.1.tar.gz 

๐Ÿข Itโ€™s a source distribution. It doesnโ€™t contain any binaries.

๐ŸฆŠ We can use the same URL format, it looks. Letโ€™s try this:

wget https://github.com/iesahin/xvc/archive/refs/tags/0.6.13.tar.gz 
--2025-01-03 06:34:00--  https://github.com/iesahin/xvc/archive/refs/tags/0.6.13.tar.gz
Resolving github.com (github.com)... 140.82.121.4
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/iesahin/xvc/tar.gz/refs/tags/0.6.13 [following]
--2025-01-03 06:34:01--  https://codeload.github.com/iesahin/xvc/tar.gz/refs/tags/0.6.13
Resolving codeload.github.com (codeload.github.com)... 140.82.121.9
Connecting to codeload.github.com (codeload.github.com)|140.82.121.9|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2025-01-03 06:34:01 ERROR 404: Not Found.

๐Ÿข Our source should be in another location.

๐Ÿ‡ We had an unpublished release. Didnโ€™t we release the binaries a few days ago?

๐Ÿข Umm, there must be something about the gh command where we set draft=false, but seemingly it didnโ€™t work out.

๐Ÿ‡ Ok, the URL is something like:

wget https://github.com/iesahin/xvc/archive/refs/tags/v0.6.13.tar.gz 

๐Ÿข Ok, this works. Weโ€™ll use this one for the URL.

๐Ÿ‡ What should we put for SHA256?

๐ŸฆŠ Letโ€™s check ripgrepโ€™s again.

๐Ÿข Downloading and using xvc file hash (alias xvcfh):

wget https://github.com/BurntSushi/ripgrep/archive/refs/tags/14.1.1.tar.gz 
...

xvc file hash -a sha2 14.1.1.tar.gz
4dad02a2f9c8c3c8d89434e47337aa654cb0e2aa50e806589132f186bf5c2b66	14.1.1.tar.gz

๐Ÿข Yep, this matches the source.

๐ŸฆŠ Then we can just use the same.

๐Ÿ‡ I want to learn how to download to $TMPDIR with wget.

๐Ÿข The -P option is used for this.

wget -P $TMPDIR https://github.com/iesahin/xvc/archive/refs/tags/v0.6.13.tar.gz 
--2025-01-03 06:55:02--  https://github.com/iesahin/xvc/archive/refs/tags/v0.6.13.tar.gz
Resolving github.com (github.com)... 140.82.121.4
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/iesahin/xvc/tar.gz/refs/tags/v0.6.13 [following]
--2025-01-03 06:55:03--  https://codeload.github.com/iesahin/xvc/tar.gz/refs/tags/v0.6.13
Resolving codeload.github.com (codeload.github.com)... 140.82.121.9
Connecting to codeload.github.com (codeload.github.com)|140.82.121.9|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: โ€˜/var/folders/gf/18gghtz15_zf0cr8v6dymglw0000gn/T/v0.6.13.tar.gzโ€™
...
2025-01-03 06:55:07 (2.59 MB/s) - โ€˜/var/folders/gf/18gghtz15_zf0cr8v6dymglw0000gn/T/v0.6.13.tar.gzโ€™ saved [8785449]
xvcfh -a sha2 $TMPDIR/v0.6.13.tar.gz
01bee5d840eefec7be1f52cc75546e1ffd7e332dfac83d875655f84e61e3a9f6	/var/folders/gf/18gghtz15_zf0cr8v6dymglw0000gn/T//v0.6.13.tar.gz

๐Ÿ‡ According to documentation, bottles are produced by Brew itself, and the documentation around bottling taps is limited.

๐ŸฆŠ We can just start with the source distribution. We can test it now.

๐Ÿข The source distribution seems to work, but it requires Rust, and Brew downloads everything related to it.

๐Ÿ‡ I think that may be enough for the time being. With 0.6.14, we can work on providing bottles.

๐Ÿข Umm, yes. Iโ€™m already bored with this stuff.

devlog 12

๐Ÿข What are todayโ€™s plans?

๐Ÿ‡ I think we can start by improving xvc.py. I mean, releasing. Yesterday we finished our work with the Rust library.

๐Ÿข Then we can start to look at the GUI, I believe. We can replace that 3-column view with a table and preview. It will be much easier that way.

๐Ÿ‡ Yep. Letโ€™s finish and release the Python version first. Then weโ€™ll go on to the GUI.

๐Ÿข Letโ€™s take a look at the PRs first.

ghpl 
36	Bump pyo3 from 0.22.2 to 0.23.3	dependabot/cargo/pyo3-0.23.3	OPEN	2024-12-04T03:52:03Z

gh pr close 36
โœ“ Closed pull request iesahin/xvc.py#36 (Bump pyo3 from 0.22.2 to 0.23.3)

๐Ÿข We have already upgraded to pyo3 0.23. No need for this. Letโ€™s create a PR for the current branch.

git push --set-upstream origin v0.6.13

ghpC --fill
https://github.com/iesahin/xvc.py/pull/37

๐Ÿ‡ We can also tag and push the tags.

๐Ÿข Iโ€™d like to have some more coverage for certain parts of the code. Letโ€™s add some tests.

๐Ÿ‡ It looks like we mainly lack the storage tests. They need configuration to add keys to GitHub, and we already skip some of these even in the Rust code.

๐Ÿข Umm, I see. We also need a way to measure coverage. Could we do this with Codecov, I wonder?

๐Ÿ‡ I found an example here: https://github.com/codecov/example-python/blob/main/.github/workflows/ci.yml. It needs coverage and pytest-cov in the requirements.

๐Ÿข Letโ€™s try this then.

๐Ÿ‡ Added coverage.yml file. Itโ€™s simpler than the other GitHub action. Need to update the token now.

๐Ÿข There is an issue installing the requirements.

๐Ÿ‡ I forgot to add sudo to apt-get. Will take care of it now.

๐Ÿข Letโ€™s check the run.

ghrl
in_progress		Release v0.6.13	coverage	v0.6.13	pull_request	12556590550	2m31s	2024-12-31T06:59:58Z
in_progress		Release v0.6.13	publish-to-pypi	v0.6.13	pull_request	12556590548	2m31s	2024-12-31T06:59:58Z
...

๐Ÿ‡ It takes a while and we still didnโ€™t add the CODECOV_TOKEN.

๐Ÿข Letโ€™s add it, and after that, we need to take a look at this blink configuration. It adds letters after the selection.

๐Ÿ‡ Added the secret and configured xvc.py for coverage. There is an error with the build, though. Maybe the command we should be using is maturin develop instead of build to make Xvc available for the environment.

๐Ÿข Letโ€™s update and try it then.

๐Ÿ‡ maturin develop requires a virtual environment.

๐Ÿข I checked the options for build, and I think there is an option, but letโ€™s search first.

๐Ÿ‡ I searched, but it looks like we can just pass an --out directory or install xvc from the target/wheels/ directory. The second option requires less maintenance.

๐Ÿข Okay. Letโ€™s add a step to the action then.

๐Ÿ‡ Now, letโ€™s wait for the run to finish with gh run watch.

๐Ÿข It failed. Letโ€™s take a look at the logs:

ghrl
completed	failure	Release v0.6.13	coverage	v0.6.13	pull_request	12556939836	3m26s	2024-12-31T07:38:54Z

gh run view 12556939836 --log-failed

๐Ÿ‡ It looks like some of the tests are failing. Letโ€™s run the tests locally.

๐Ÿข We should run with the --forked option, and it looks like we have a test to update with the xvc file list.

๐Ÿ‡ Updated the test, and I noticed we forgot to supply the new --show-directories option in the Python interface.

๐Ÿข Yep. Passing these options as command-line options in strings is not robust. Itโ€™s very easy to forget things. I think we should start using CLI structs directly, but itโ€™s not time yet.

๐Ÿ‡ I agree. Itโ€™s one of the goals for building a GUI, actually.

๐Ÿข The tests failed again.

ghrl | rg failure
completed	failure	Release v0.6.13	coverage	v0.6.13	pull_request	12557031091	3m49s	2024-12-31T07:51:19Z

gh run view 12557031091 --log-failed

๐Ÿ‡ There is a Git error now. We need to add a Git user and email to the action.

๐Ÿข Added those and watching the results again now.

๐Ÿ‡ Why do you think the publish action always works? It can only run with the main branch, I think. No need to run it with other pushes.

๐Ÿข Yes, letโ€™s configure it now.

ghrl | rg failure
completed	failure	Release v0.6.13	coverage	v0.6.13	pull_request	12557083393	3m21s	2024-12-31T07:59:18Z

gh run view 12557083393 --log-failed

๐Ÿ‡ It looks like we also need xvc-test-helper in the path. Letโ€™s cargo install it and add it to the path.

๐Ÿข Added .cargo/bin to the path like:

- name: Add cargo bin to PATH
  run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH

and installed the helper with cargo install xvc-test-helper.

๐Ÿ‡ By the way, GitHub Copilot is hallucinating about a method to update the path.

๐Ÿข I searched and it may not be hallucinating. We can use the ::add-path:: command with echo, it looks like. This is new to me.

๐Ÿ‡ The tests failed again.

ghrl | rg failure
completed	failure	Release v0.6.13	coverage	v0.6.13	pull_request	12557208723	3m34s	2024-12-31T08:12:09Z

gh run view 12557208723 --log-failed

๐Ÿข file().list() had a mistake, and we need to install rg for the tests.

๐Ÿ‡ Watching the test run. In the meantime, maybe we can reviewโ€ฆ

๐Ÿข Failed again.

ghrl | rg failure
completed	failure	Release v0.6.13	coverage	v0.6.13	pull_request	12557294105	3m46s	2024-12-31T08:21:17Z

gh run view 12557294105 --log-failed

๐Ÿ‡ Added db.commit() to two places in the code. This should pass now.

๐Ÿข Yeah!

ghrl | head -n 1
completed	success	Release v0.6.13	coverage	v0.6.13	pull_request	12557509836	3m46s	2024-12-31T08:44:33Z

gh run view 12557509836

โœ“ v0.6.13 coverage iesahin/xvc.py#37 ยท 12557509836
Triggered via pull_request about 4 minutes ago

JOBS
โœ“ linux in 3m36s (ID 35010253915)

ANNOTATIONS
! ubuntu-latest pipelines will use ubuntu-24.04 soon. For more details, see https://github.com/actions/runner-images/issues/10636
linux: .github#1


For more information about the job, try: gh run view --job=35010253915
View this run on GitHub: https://github.com/iesahin/xvc.py/actions/runs/12557509836

๐Ÿ‡ I canโ€™t see a coverage report on codecov.io, though.

๐Ÿข It says no coverage report is generated. Letโ€™s test to generate XML files locally.

๐Ÿ‡ The option in the docs seems incorrect. --cov-branch doesnโ€™t produce anything.

๐Ÿข Letโ€™s wait for the run again.

๐Ÿ‡ Should we add a badge to the README?

๐Ÿข It wonโ€™t show much, but yeah, letโ€™s make it.

๐Ÿ‡ The results are in and it shows 100% coverage. This means it doesnโ€™t actually test anything.

๐Ÿข We need Rust coverage for this. Letโ€™s search for it.

๐Ÿ‡ I found this: https://github.com/cjermain/rust-python-coverage. It runs cargo llvm-cov with the project and measures test coverage. But we donโ€™t have any Rust tests.

๐Ÿข It looks like we donโ€™t need Rust tests. cargo llvm-cov can check coverage with the Python as well.

$ cargo llvm-cov show-env --export-prefix
export RUSTFLAGS=" -C instrument-coverage --cfg coverage --cfg trybuild_no_target"
export LLVM_PROFILE_FILE="/home/.../rust-python-coverage/target/rust-python-coverage-%m.profraw"
export CARGO_INCREMENTAL="0"
export CARGO_LLVM_COV_TARGET_DIR="/home/.../rust-python-coverage/target"

๐Ÿข Letโ€™s take a look at what remained for 0.6.13.

๐Ÿ‡ I think there is nothing left. Python must be published when we merged the PR.

๐Ÿข Letโ€™s take a look by searching xvc python.

๐Ÿ‡ The PyPI page is https://pypi.org/project/xvc/ and it still reports the version as 0.6.11. There must be something.

๐Ÿข Now letโ€™s take a look at

https://github.com/iesahin/xvc.py

๐Ÿ‡ The run seems to be OK though.

https://github.com/iesahin/xvc.py/actions/runs/12557780141/job/35010938462

๐Ÿข Maybe the version in pyproject.toml is still 0.6.11 and we forgot to update it?

tmux new-window -c $HOME/github.com/iesahin/xvc.py/ nvim

๐Ÿ‡ There is no version string in ~/github.com/iesahin/xvc.py/pyproject.toml. Itโ€™s dynamic.

๐Ÿข Then, letโ€™s try to publish from local now.

๐Ÿ‡ The iex username requires email verification.

๐Ÿข It looks like I publish xvc through the iesahin account, not iex. Maybe I can add both accounts to the project.

๐Ÿ‡ There seem to be no errors on the PyPI site.

๐Ÿข Updating the token. Letโ€™s add the token to pass to run maturin publish.

๐Ÿ‡ Weโ€™re receiving invalid or non-existent authentication information. Upgraded maturin to see if it fixes the issue.

๐Ÿข We can also double-check the key.

๐Ÿ‡ Uploaded successfully from local. Letโ€™s check the job again.

๐Ÿข The release job was skipped because we didnโ€™t tag after the merge. https://github.com/iesahin/xvc.py/actions/runs/12557780141/job/35011303045 That looks like the reason.

๐Ÿ‡ I should be more careful which is run and which is skipped.

๐Ÿข Maybe we can relax the condition. We do this rarely. Maybe republishing is alright?

๐Ÿ‡ Yep, removed that. The jobs are running now. Letโ€™s watch them to see what happens when we publish some of the packages.

๐Ÿข In the meantime, letโ€™s experiment with searching commands file with fzf-lua.

๐Ÿ‡ It requires more experimentation, but we can start from https://github.com/ibhagwan/fzf-lua/wiki/Advanced#interactive-shell-command.

๐Ÿข The xvc publish jobs failed, btw.

ghrl
completed	failure	Remove if condition from release	publish-to-pypi	v0.6.13	push	12568863716	15m26s	2025-01-01T08:30:03Z

gh run view 12568863716 --log-failed
...
Release	Run actions/download-artifact@v4.1.7	        Please ensure that your artifact is not expired and the artifact was uploaded using a compatible version of toolkit/upload-artifact.
...

๐Ÿ‡ It was using an older version of upload-artifact.

๐Ÿข Rerunning the job and it looks like it runs for both push to main and tag with v0.6.13. We can turn off push to main, I believe.

๐Ÿ‡ There was a missing upload-artifact again. Fixed and updated the tags.

ghrl completed failure update upload-artifacts publish-to-pypi main push 12569060915 14m39s 2025-01-01T08:57:22Z

ghrf 12569060915

๐Ÿข There are conflicts with uploaded artifacts now. We may need to clean up the artifacts manually.

๐Ÿ‡ The conflicts were not about inter-workflow names. The names were conflicting because all files were named wheel. I added the platform and target to the names to avoid conflicts.

๐Ÿข Letโ€™s wait then. Maybe it will work this time. Could you search for a Lua console for Neovim?

๐Ÿ‡ Letโ€™s try this one: return { โ€œyarospace/lua-console.nvimโ€, lazy = true, keys = โ€œ`โ€, opts = {}, }

๐Ÿข I couldnโ€™t make it run but wonโ€™t spend much time ATM. How about the jobs?

ghrl
completed	failure	update upload artifact names	publish-to-pypi	v0.6.13	push	12569298414	13m57s	2025-01-01T09:27:00Z

ghrf 12569298414
Release	Run actions/download-artifact@v4	2025-01-01T09:40:52.9180510Z ##[group]Run actions/download-artifact@v4
Release	Run actions/download-artifact@v4	2025-01-01T09:40:52.9182098Z with:
Release	Run actions/download-artifact@v4	2025-01-01T09:40:52.9182859Z   name: wheels
Release	Run actions/download-artifact@v4	2025-01-01T09:40:52.9183843Z   merge-multiple: false
Release	Run actions/download-artifact@v4	2025-01-01T09:40:52.9184817Z   repository: iesahin/xvc.py
Release	Run actions/download-artifact@v4	2025-01-01T09:40:52.9185820Z   run-id: 12569298414
Release	Run actions/download-artifact@v4	2025-01-01T09:40:52.9186903Z ##[endgroup]
Release	Run actions/download-artifact@v4	2025-01-01T09:40:53.1937617Z Downloading single artifact
Release	Run actions/download-artifact@v4	2025-01-01T09:40:53.4694769Z ##[error]Unable to download artifact(s): Artifact not found for name: wheels
Release	Run actions/download-artifact@v4	        Please ensure that your artifact is not expired and the artifact was uploaded using a compatible version of toolkit/upload-artifact.
Release	Run actions/download-artifact@v4	        For more information, visit the GitHub Artifacts FAQ: https://github.com/actions/toolkit/blob/main/packages/artifact/docs/faq.md

๐Ÿ‡ Now the download doesnโ€™t work.

๐Ÿข Update it to download using patterns.

๐Ÿ‡ Did so and letโ€™s take a look at the jobs again.

ghrl
completed	failure	added pattern to download	.github/workflows/publish.yml	main	push	12569456859	0s	2025-01-01T09:49:19Z

ghrv 12569456859

X main .github/workflows/publish.yml ยท 12569456859
Triggered via push about 3 minutes ago

X This run likely failed because of a workflow file issue.

For more information, see: https://github.com/iesahin/xvc.py/actions/runs/12569456859

๐Ÿข The line with the pattern was reported as broken.

๐Ÿ‡ Removed it and recommitted.

ghrl
โœ… completed	success	remove pattern to download all	publish-to-pypi	v0.6.13	push	12569509435	14m41s	2025-01-01T09:58:49Z

๐Ÿข And now this completes the v0.6.13 release.

๐Ÿ‡ Weโ€™ll see how it will work next time.

๐Ÿข Yep. Letโ€™s move on to the GUI for now. Is that okay with you?

devlog 13

๐Ÿ‡ So, whatโ€™s next?

๐Ÿฒ We can work on completions or the GUI.

๐Ÿข Completions are similar to the Homebrew work. Itโ€™s in another ecosystem and feels boring.

๐Ÿฒ We can try to make it in Rust: โ€œWriting shell completions in Rust.โ€

๐Ÿข There are two solutions: One is clap_complete (https://docs.rs/clap_complete/latest/clap_complete/) and the other is shell_completion (https://github.com/JoshMcguigan/shell_completion). The latter is very new but may be what we need: writing completions only with Rust.

๐Ÿฒ The shell_completion crate is very bare-bones. It doesnโ€™t have anything, actually. https://github.com/JoshMcguigan/shell_completion/issues/1

๐Ÿ‡ The feature that we need is dynamic completion. Weโ€™ll write something similar to ArgValueCompleter for this.

๐ŸฆŠ I think we can just start a new branch.

๐Ÿฒ We have a branch for Homebrew; should we merge it?

๐Ÿข I think, yes, we can merge it. Weโ€™ll fix it if it breaks. Itโ€™s a separate workflow file anyway.

๐ŸฆŠ Created the PR.

ghpl
264	add brew tap	add-brew-tap	OPEN	2025-01-03T05:14:45Z

๐Ÿข Merged it.

๐Ÿ‡ Now letโ€™s create a new branch and add clap-complete to Cargo.toml.

๐Ÿฒ There are some packages that we need to take a look at. thiserror now has v2.

๐Ÿข Upgraded packages and compiled. It works.

๐Ÿฒ Letโ€™s update the version.

๐Ÿข Done. Now we can add clap-complete.

๐ŸฆŠ Added with the unstable-dynamic command. Can we build it again?

๐Ÿข Built it. Now we can add a completions subcommand to Xvc.

๐Ÿฒ Is this the right name for this?

๐Ÿข It will output a shell script that we can source in the shell.

๐Ÿฒ Okay, letโ€™s add the subcommand now.

๐Ÿข The example doesnโ€™t work with the clap builder.

๐Ÿ‡ Letโ€™s search for it.


๐Ÿข I think weโ€™re extending ourselves a bit when trying to add all dynamic features of completion at once. We can just start by adding completion to xvc-test-helper.

๐Ÿ‡ Yep, thatโ€™s a better idea. We can have many more examples, and itโ€™s certainly more straightforward.

๐Ÿข One thing I noticed when working yesterday is that using auto-save was actually preventing many of these swap errors.

๐Ÿฒ It was a bit slow. Can we take a look at a few others?

๐Ÿข https://github.com/okuuva/auto-save.nvim seems a bit more polished.

๐Ÿ‡ Installed it, but havenโ€™t seen an effect yet.

๐Ÿข We may need to restart this.

๐Ÿฒ Now, we can get into the test-helper completions.

๐Ÿข Done. Added these quickly as you thought. Now adding this to main seems much easier.

๐ŸฆŠ That was a nice approach. Letโ€™s dive into adding this to main.

๐Ÿ‡ Should we go with a separate command or just an option?

๐Ÿฒ Our commands have distinct initial letters, allowing them to be used with just those letters. Adding another top-level command for this will make c useless.

๐Ÿข I donโ€™t think thatโ€™s a valid concern. We can create aliases for all commands and completions shouldnโ€™t have to have an alias. But I agree that completions should not be a top-level command. It will be run once for installation at most.

๐ŸฆŠ I agree. Letโ€™s call it --completions. It will be run as xvc --completions zsh and will print out the completions.

๐Ÿข Okay. Letโ€™s do this.

๐ŸฆŠ No errors left. Letโ€™s install this version.

๐Ÿข We get:

error: 'xvc' requires a subcommand but one was not provided
  [subcommands: file, init, pipeline, storage, root, check-ignore, aliases, help]

Usage: xvc [OPTIONS] <COMMAND>

For more information, try '--help'.

๐ŸฆŠ Umm, okay. I think we donโ€™t have an option to create a command like that. Then weโ€™ll have to print completions with a subcommand.

๐Ÿฒ The above discussion is now moot. โŒ

๐Ÿข We can try to push, but probably itโ€™s not worth it.

๐Ÿฒ Letโ€™s not lose time on this. I think we can remove the aliases command and replace it with completions, and add single-letter aliases to subcommands.

๐Ÿข We can have an option in the completions command to print aliases. That will work.

๐ŸฆŠ I donโ€™t think people will use the aliases command if we have single-letter command aliases.

๐Ÿข You may be right. No need to try to maintain that at this time.


๐Ÿข Good morning, and Iโ€™m fed up with these blink.cmp errors, you know.

๐Ÿ‡ Reinstalling blink works. Itโ€™s interesting to rely on such unreliable software.

๐Ÿข Oh, yeah. Itโ€™s interesting. Where were we yesterday?

๐Ÿฒ We decided to rename the aliases command to completions.

๐Ÿ‡ And waiting for rust-analyzer to complete its analysis.

๐Ÿข Uh, yeah. I see.

๐ŸฆŠ The code itself is very short, actually.

    if let Some(shell) = cli_opts.completions {
        let mut cmd = XvcCLI::command();
        generate(shell, &mut cmd, "xvc", &mut io::stdout());
        return Ok(None);
    }

๐Ÿฒ Weโ€™ll use the output! macro instead of writing to io::stdout(), right?

๐Ÿข Yes, we can rely on the usual output system. It will be slower to create an output thread but shouldnโ€™t matter for outputting a shell script.

๐Ÿ‡ It takes a while for rust-analyzer to scan all the directories, it looks like. When I close the project, it just cannot reload it immediately.

๐Ÿข We can use cargo clean from time to time.

๐ŸฆŠ rust-analyzer finished scanning; letโ€™s try to rename AliasesCLI.

๐Ÿฒ Letโ€™s keep these here; maybe weโ€™ll need them in our scripts:

# Standard Xvc command aliases for longer commands.
alias xls='xvc file list'
alias pvc='xvc pipeline'
alias fvc='xvc file'
alias xvcf='xvc file'
alias xvcft='xvc file track'
alias xvcfl='xvc file list'
alias xvcfs='xvc file send'
alias xvcfb='xvc file bring'
alias xvcfh='xvc file hash'
alias xvcfco='xvc file checkout'
alias xvcfr='xvc file recheck'
alias xvcp='xvc pipeline'
alias xvcpr='xvc pipeline run'
alias xvcps='xvc pipeline step'
alias xvcpsn='xvc pipeline step new'
alias xvcpsd='xvc pipeline step dependency'
alias xvcpso='xvc pipeline step output'
alias xvcpi='xvc pipeline import'
alias xvcpe='xvc pipeline export'
alias xvcpl='xvc pipeline list'
alias xvcpn='xvc pipeline new'
alias xvcpu='xvc pipeline update'
alias xvcpd='xvc pipeline dag'
alias xvcs='xvc storage'
alias xvcsn='xvc storage new'
alias xvcsl='xvc storage list'
alias xvcsr='xvc storage remove'

๐ŸฆŠ We can use them when adding single-letter aliases.

๐Ÿ‡ There is a from_env method for Shell; will we support it?

๐ŸฆŠ I think we can support it. Itโ€™s much easier to use if we omit the shell.

๐Ÿฒ We had aliases in xvc-core, but we need to access XvcCLI from completions. The completions module must be moved to xvc.

๐ŸฆŠ We added the clap_complete dependency to the xvc-pipeline and xvc-file crates, but I donโ€™t think they are necessary. Letโ€™s remove them now.

๐Ÿข Now only the test-helper and xvc crates have the clap_complete dependency.

๐Ÿ‡ Are we ready to test?

๐Ÿข Completions are working. ๐ŸŽ‰

๐Ÿฒ Now we need to update the docs and doc tests, I believe.

๐Ÿข There are also tests to update.

๐Ÿ‡ Tests are running now. In the meantime, can we take a look at the blink configuration?

๐Ÿฒ When we removed xvc aliases, we also removed pvc, xls, and other aliases. Maybe we can add these to the docs.

๐Ÿข We can put them in the xvc completions reference for now.


๐Ÿข It takes a while for rust-analyzer to finish analyzing the codebase.

๐ŸฆŠ Added aliases for xvc pipeline commands. Do you think we need to repeat root-level flags in xvc-pipeline?

๐Ÿข No need to divert attention, I believe. Also, I still think there is an easier way to do that.

๐Ÿ‡ Okay. Do you think we should add easier subcommands to step? Like, step new becoming xvc p s n instead of xvc p s n? (Wait, thatโ€™s the same). I mean, more concise.

๐Ÿข I think we can extend these even to the top-level, but shouldnโ€™t make them visible. It will pollute the help text. We can add xvc fl for file list to avoid the space, but hide these from the help text.

๐Ÿฒ Thatโ€™s a good idea, but it will require including sub-crate level modules at the top level. We must test it first.

๐Ÿข Letโ€™s finish up the current changes and release them first.

๐Ÿ‡ By the way, we didnโ€™t add two-letter abbreviations to the xvc storage new subcommands. Do you think we need them?

๐Ÿข When we think about the frequency of these commands, no, I donโ€™t think we need them. Users wonโ€™t add a new storage every day.

๐Ÿ‡ By that logic, we shouldnโ€™t need an n for xvc storage new.

๐Ÿข Actually, yeah. Maybe we should remove even s.

๐ŸฆŠ storage list may be useful.

๐Ÿข s is a very common letter, though. We can have other uses for that letter.

๐Ÿ‡ Updating xvc p s dependency options, but it looks like making dependency options subcommands is a better way.

๐ŸฆŠ We didnโ€™t do it because currently we can supply multiple options with a single command. If we go the subcommand route, weโ€™ll have to write all dependencies one by one. Adding multiple subcommands with clap is a bit tricky.

๐Ÿข Umm. Yeah, I see.

๐Ÿ‡ Maybe we can provide a separate command to add multiple dependencies. Like, xvc p s d add 'lines=myfile.csv::10-20; file=myimage.jpg; glob=dir/image-10*'

๐Ÿฒ That looks like the start of a language. We need a parser for those strings. They will be freeform.

๐ŸฆŠ Another option is to keep the current options and add commands with the same names.

๐Ÿข That will be confusing. The user will have both --param and param, and they will work differently.

๐Ÿ‡ We can have an add command that accepts the current options. Parsing will be done by clap just like now, but for a subcommand of dependency.

๐Ÿข The full command will be something like xvc pipeline step --step-name preprocessing dependency add --params 'params.json::batch_size'

๐Ÿ‡ With shorter commands, itโ€™s like xvc p s -s preprocessing d a --params 'params.json::batch_size', and this doesnโ€™t look like ffmpeg monstrosities.

๐Ÿฒ In any case, this is a backward-incompatible change. This should wait for v0.7 along with ECS changes.

๐Ÿข ECS changes are not user-visible, but these are. Certainly needs a minor version update.

๐Ÿ‡ Then, okay, letโ€™s keep the current ones for this version and think about updating them in a future version.

๐Ÿข Okay. Letโ€™s finish up and weโ€™ll create a separate invisible subcommand to ask questions to the Xvc repo for completions.

๐Ÿ‡ We completed completions for the common command structure. Now, we need a way to show certain info after certain commands. A tab after xvc p r -p should show pipeline names, for example.

๐Ÿข Umm, yeah. And these should be as quick as possible. They shouldnโ€™t check Git or any other things.

๐Ÿ‡ I looked here and there, and the only working example I found is here: https://github.com/clap-rs/clap/blob/master/clap_complete/tests/testsuite/zsh.rs#L248 in clap_complete tests.

๐Ÿข Letโ€™s try to dive in. We can start by cloning the repo, I believe.

devlog 14

๐Ÿข Now, letโ€™s return to clap and its dynamic completions. Last time you said:

๐Ÿ‡ The examples are only found in the tests: https://github.com/clap-rs/clap/blob/master/clap_complete/tests/testsuite/engine.rs#L604

and we can try to create those random strings this way.

๐ŸฆŠ The issue is that we may not be able to use the derive method to add these custom completers. All examples are using the builder API.

๐Ÿข We should be able to obtain the end result from derives and modify them to use these custom completers, but letโ€™s try to use add = ArgValueCompleter first.

๐Ÿ‡ It looks like we have another problem. We need to add a rust-toolchain.toml file to the project, you know, to keep it on the stable channel.

๐Ÿข Ah, yeah, we now use multiple channels. Letโ€™s do that.

๐Ÿ‡ I found an example, actually:

#[derive(Debug, Parser)]
struct Cli {
    #[arg(long, add = ArgValueCompleter::new(custom_completer))]
    custom: Option<String>,
}

๐Ÿข Iโ€™m trying to add this to the --from-ref option of Xvc. Itโ€™s not running anything. The custom completer should return a random string, but it doesnโ€™t.

๐ŸฆŠ Maybe test with a constant first.

๐Ÿข It didnโ€™t work that way either.

๐Ÿ‡ Letโ€™s do a cargo clean.

๐Ÿข Nothing has changed.

๐Ÿ‡ Letโ€™s take a look at the output of xvc completions:

xvc completions
...
'(--skip-git)--from-ref=[Checkout the given Git reference (branch, tag, commit etc.) before performing the Xvc operation. This runs \`git checkout <given-value>\` before running the command]:FROM_REF:_default' \
...

๐Ÿข This is the only place --from-ref is mentioned and, as far as I can see, there is nothing that calls a dynamic command here.

๐Ÿ‡ Umm, right. Thereโ€™s something weird here. Maybe we lack the ArgExt trait or something.

๐Ÿข It should be turned on by clap-complete with its unstable-dynamic feature, but who knows.

๐Ÿ‡ It didnโ€™t work.

๐Ÿข ArgExt is available, but it isnโ€™t being used.

๐Ÿ‡ I think I found the answer in https://jj-vcs.github.io/jj/latest/install-and-setup/#command-line-completion:

source <(COMPLETE=zsh xvc)

is the command we should use.

๐Ÿข It doesnโ€™t produce a completion script, though.

๐Ÿ‡ We have this in jj/cli/src/cli_util.rs:

        if env::var_os("COMPLETE").is_some() {
            return handle_shell_completion(ui, &self.app, &config, &cwd);
        }

๐Ÿข I think the whole handle_shell_completion set is written by them. Iโ€™d like to see what jj with COMPLETE=zsh outputs.

๐Ÿ‡ Building it to see now.

๐Ÿข As expected, it calls a function:

โฏ COMPLETE=zsh target/debug/jj
#compdef jj
function _clap_dynamic_completer_jj() {
    local _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
    local _CLAP_IFS=$'\n'

    local completions=("${(@f)$( \
        _CLAP_IFS="$_CLAP_IFS" \
        _CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
        COMPLETE="zsh" \
        /Users/iex/github.com/etc/jj/target/debug/jj -- ${words} 2>/dev/null \
    )}")

    if [[ -n $completions ]]; then
        _describe 'values' completions
    fi
}

compdef _clap_dynamic_completer_jj jj

๐Ÿ‡ Now we should make it the same; the shell must call xvc to get the completions. Thatโ€™s how all these will work.

๐Ÿข Our goal now is to make a random string output from the --from-ref completion.

๐Ÿ‡ The plan is to make completions work as quickly as possible.

๐Ÿข We have done it. ๐ŸŽ‰๐Ÿฅณ

๐Ÿ‡ Cool. Now we need to fill up all those completion methods.

๐ŸฆŠ Yeah. We can start with basic ones and move from there.

๐Ÿข There will probably be architectural changes as well. We cannot just run xvc functions directly. We need to run internal functions, but not through commands. At that point, when the user hits tab, we donโ€™t know which command to run.

๐Ÿ‡ Maybe itโ€™s time to move to a Git library. โ€œWhat is the best Git library for Rust?โ€

๐Ÿฒ Should we use a Git library?

๐Ÿข It looks like Gitoxide is the default Rust way to interact with Git repositories. We can keep our way of using the Git binary and use this as an experimental way to learn.

๐ŸฆŠ Yep. Thatโ€™s a good idea. We can include it in the lib for the time being and start to use it in completions.

devlog 15

๐Ÿข Should we go into completions directly, or do we have anything to update in the working scripts?

๐Ÿ‡ Starting with completions is better. One question I have is whether the completions work for subcommands as usual, like in the static completions.

๐Ÿข Yes, they work. We need to add:

source <(COMPLETE=zsh xvc)

to .zshrc, though.

๐Ÿ‡ It looks like we can drop the xvc completions command. Weโ€™re just checking the COMPLETE environment variable, and thereโ€™s no need for a separate command in this case.

๐Ÿข Yes, letโ€™s remove that.

๐Ÿ‡ Maybe we can employ a completions module for all completion-related functionality. Itโ€™s a separate thing, you know.

๐Ÿข Which completions do we need?

๐Ÿ‡ We can just mark them with TODOs now.

๐Ÿข Yes, letโ€™s check where we need completions and what.

๐Ÿ‡ I noticed weโ€™re repeating options in XvcFileCLI, and some of the options are missing, e.g., --from-ref and --to-branch.

๐Ÿข Because it can compile into a different binary, and global options donโ€™t work that way. Maybe we can move all these binaries into different files under lib. We can have feature flags to turn off certain features and compile different binaries.

๐ŸฆŠ Letโ€™s search for it; I havenโ€™t seen it before: building different binaries with feature flags using cargo.

๐Ÿข There is no clear-cut solution, but it looks like we can move all binaries to the root with the required-features field.

๐Ÿฒ We can postpone this to another release.

๐Ÿ‡ Yes, letโ€™s not spend time on this now.

๐Ÿข Should we try making pipeline_name a global option? Itโ€™s repeating everywhere.

๐Ÿ‡ That will make it easier to maintain.

๐Ÿข Weโ€™ll have to pass pipeline_name to subcommands, though.

๐Ÿ‡ I think we can have a set of global options that we can pass. For the time being, thatโ€™s only the pipeline_name.

๐Ÿข Okay. Letโ€™s do this.

๐Ÿ‡ A similar option is the step name for pipeline steps.

๐Ÿข Dependencies will need a revamp in the next version anyway. So letโ€™s keep it for now.

๐Ÿฒ Also, the semantics of step-name are different for these commands. step new interprets it as a new name, while step dependency interprets it as an existing name. The first can be renamed to --name, and the second can be --to, as one of its aliases suggests.

๐Ÿข Yes, letโ€™s keep it for now, and weโ€™ll continue to work on others.

๐ŸฆŠ I want to post a comment in the clap discussions.

๐Ÿข Wrote it. Now letโ€™s build it after changing the pipeline_name.

๐Ÿ‡ It compiles. Should we test it?

๐Ÿข I think weโ€™ll test after all this completion work is done.

๐Ÿ‡ We did most of the strum-related completion.

๐Ÿข Didnโ€™t test them yet, though.

๐Ÿ‡ This is Rust. It will work if it compiles, and it compiles.

devlog 16

๐Ÿข Good morning. Yesterday we wrote a proxy server for a job application. It was a pleasing experience, but I wasnโ€™t able to write a dialogue.

๐Ÿ‡ It was one of those nice days. I have that feeling in my head that I used it to its full potential.

๐Ÿข So we can do some light work today.

๐Ÿ‡ Like completions?

๐Ÿข Yep, letโ€™s fill in some missing pieces there.

๐Ÿ‡ Letโ€™s see what those missing pieces are.

๐Ÿข We need to clean up the code comments we copied from jj.

๐ŸฆŠ Cleaned up quite a bit and moved everything to main. Itโ€™s a single function call.

๐Ÿข I think we completed all strum-related completions. Now we need to discuss store completions a bit.

๐Ÿ‡ They canโ€™t all have the same function like we used for strum-based enums. They will look up different parts of components. For XvcPath, it will be the inner RelativePathBuf; for StorageIdentifier, that will be the names of all recorded storage names and GUIDs. The field weโ€™re going to complete will change from case to case.

๐ŸฆŠ They can depend on the same machinery. It will detect .xvc, load a store, and return a field from the component. The first two are the same for all.

๐Ÿข Yep. Letโ€™s write a few TODOs in the code, then.

๐ŸฆŠ One thing to discuss is whether we should load all config and root. It may be a bit time-consuming. Just loading the stores should be enough.

๐Ÿข I think so. ECS doesnโ€™t depend on XvcRoot, and we can load stores without loading XvcRoot. We have convenience functions in XvcRoot, but they are for convenience. There is nothing that prevents us from loading stores without loading the root.

๐ŸฆŠ Letโ€™s take a look at XvcRootโ€™s loading of these.

๐Ÿข Tomorrow, hopefully.

devlog 17

๐Ÿข We are progressing towards adding all store completions. In the meantime, I switched to nushell from zsh. Completions for nushell require a different crate and it doesnโ€™t have dynamic completions yet.

๐Ÿ‡ We can just complete the completions for this version and add JSON output for all commands to use in nushell. In the meantime, dynamic completions support for nushell is likely to be added.

๐Ÿข Umm, yep. Then weโ€™ll continue to run tests with zsh for the current version.

๐Ÿ‡ run-tests is a zsh file, and we will continue to use it. We can switch to nushell eventually as we add more compatibility with it, but letโ€™s not do this now.

๐Ÿข Where were we the last time for completions?

๐Ÿ‡ We added xvc_path_completers. Letโ€™s check the TODO list now.

๐Ÿข We still need tracked_targets completions in a few places.

๐Ÿ‡ Added those.

๐Ÿข Now we have some strum completers. Letโ€™s finish these up as well.

๐Ÿ‡ Ok. They are done as well.

๐Ÿข Now, letโ€™s start adding a storage_identifier completer. It should read all XvcStorage records and list their names.

๐Ÿ‡ Added a storage_identifier completer. Letโ€™s add it to all places where we use storage_identifiers.

๐Ÿข Now, we need completers for pipeline and step names. These are straightforward.

๐Ÿ‡ Added these. Letโ€™s fill up where they are needed.

๐Ÿข I want to skip some of the fine-grained completions in this version. We can have a specific completer for --params options for files and params inside, for example. Or a special completer for directories.

๐Ÿ‡ Letโ€™s not forget these. Reading YAML files and extracting hyperparameter keys for the prompt would be a really good feature for the user.

๐Ÿข I agree. These are good features in general, but we need to ship the current version as soon as possible.

๐Ÿ‡ Letโ€™s check the TODO comments once more.

$ rg 'TODO:' 
...
- โœ… pipeline/src/pipeline/api/update.rs:    /// TODO: Add a repository_dirs completer (11:08)
- โœ… file/src/remove/mod.rs:    /// TODO: Add a storage_identifier completer (11:08)
- โœ… file/src/bring/mod.rs:    /// TODO: Add a storage_identifier completer (11:08)
...

๐ŸฆŠ We can use xvc_path_completer for the tracked directory completer for the time being. For pipeline update, we can use a ValueHint instead. Itโ€™s not necessary to use a custom completion.

๐Ÿข For the xvc file copy destination, we can have a file or a directory that we track and is not available, or we donโ€™t track and is available. Itโ€™s a similar situation to xvc_path_completer, but we also need to check the local paths. It requires some more care.

๐Ÿ‡ Now we can begin to run the tests.

๐Ÿข Is this building?

๐Ÿ‡ It should.

๐Ÿข We also have one bug. xvc shouldnโ€™t print help text when the COMPLETE environment variable is set.

๐Ÿ‡ Umm, yeah, thatโ€™s a blocker.

๐Ÿข Fixed it. We can bump the version and push the changes.

devlog 18

๐Ÿ‡ There is no xvc completions command anymore. Letโ€™s remove the test.

๐Ÿข Instead, we can make it test with environment variables for each of these shells.

๐Ÿ‡ It looks like the test fails with wait_status.

cargo test -p xvc --test test_completions
...
failures:

---- test_completions stdout ----
...
ExitStatus(unix_wait_status(512))

thread 'test_completions' panicked at lib/tests/common/mod.rs:46:5:
Command failed: Command { cmd: "/Users/iex/github.com/iesahin/xvc/target/debug/xvc", stdin: None, timeout: None }
...

๐Ÿข The failure is in the line:

    let mut cmd = Command::cargo_bin("xvc").unwrap();

So it probably waits for user input to complete, but it doesnโ€™t have any and cannot complete it, so it returns an error.

๐ŸฆŠ Letโ€™s comment the earlier test out and check if other tests work.

๐Ÿข Yes, they fail the same. When there are no commands to run, xvc returns an error. Maybe we can change this behavior or handle the error in the Command::cargo_bin line above.

๐Ÿ‡ I checked the code and we donโ€™t handle the โ€œno argumentsโ€ case anywhere. It looks like the behavior to return an error code is inherited from clap.

๐Ÿข Updated the error handling code to report a more descriptive message from the source.

cargo test -p xvc --test test_completions
...
failures:

---- test_completions stdout ----
Output { status: ExitStatus(unix_wait_status(25856)), stdout: "", stderr: "\nthread 'main' panicked at /Users/iex/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_builder-4.5.20/src/builder/debug_asserts.rs:341:13:\nCommand pipeline: command `export` alias `l` is duplicated\nstack backtrace:\n   0: rust_begin_unwind\n             at /rustc/b1a7dfb91106018f47ed9dc9b27aee1977682868/library/std/src/panicking.rs:692:5\n   1: core::panicking::panic_fmt\n             at /rustc/b1a7dfb91106018f47ed9dc9b27aee1977682868/library/core/src/panicking.rs:75:14\n   2: clap_builder::builder::debug_asserts::assert_app\n             at /Users/iex/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_builder-4.5.20/src/builder/debug_asserts.rs:341:13\n   3: clap_builder::builder::command::Command::_build_self\n             at /Users/iex/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_builder-4.5.20/src/builder/command.rs:4173:13\n   4: clap_builder::builder::command::Command::_build_recursive\n             at /Users/iex/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_builder-4.5.20/src/builder/command.rs:4076:9\n   5: clap_builder::builder::command::Command::_build_recursive\n             at /Users/iex/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_builder-4.5.20/src/builder/command.rs:4078:13\n   6: clap_builder::builder::command::Command::build\n             at /Users/iex/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_builder-4.5.20/src/builder/command.rs:4071:9\n   7: clap_complete::env::CompleteEnv<F>::try_complete_\n             at /Users/iex/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_complete-4.5.40/src/env/mod.rs:229:9\n   8: clap_complete::env::CompleteEnv<F>::try_complete\n             at /Users/iex/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/clap_complete-4.5.40/src/env/mod.rs:210:9\n   9: xvc::main\n             at ./src/main.rs:18:30\n  10: core::ops::function::FnOnce::call_once\n             at... [truncated]
ExitStatus(unix_wait_status(25856))

...

error: test failed, to rerun pass `-p xvc --test test_completions`

๐Ÿข The real issue is when we assert the successful run, at this line:

            assert!(output.status.success(), "Command failed: {:?}", prepared);

๐Ÿ‡ If you look at the error message carefully, youโ€™ll see that this is a different error. We added an alias to xvc pipeline export with l, which is a duplicate.

๐Ÿข Oops, yeah.

cargo test -p xvc --test test_completions
...
test test_completions ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.81s

๐Ÿ‡ There are some warnings in compilation. Letโ€™s fix these.

cargo build
   Compiling xvc-storage v0.6.14-alpha.8 (/Users/iex/github.com/iesahin/xvc/storage)
   Compiling xvc-file v0.6.14-alpha.8 (/Users/iex/github.com/iesahin/xvc/file)
   Compiling xvc-pipeline v0.6.14-alpha.8 (/Users/iex/github.com/iesahin/xvc/pipeline)
   Compiling xvc v0.6.14-alpha.8 (/Users/iex/github.com/iesahin/xvc/lib)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.28s

๐Ÿข Finished the build without warnings. Now we can run the whole test suite again.

๐Ÿ‡ Tests are running fine. Updated some error messages and types, and doc tests also pass. There is an issue with the Rsync storage ref.

๐Ÿข It looks like we try to elide output with [...], while the proper format is [..].

๐Ÿ‡ Replaced them with ... that will elide multiple lines.

๐Ÿข Pushed changes to the server.

devlog 19

๐Ÿ‡ Letโ€™s start by checking the GitHub Actions results.

$ ghrl | first
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ conclusion   โ”‚ failure                                                 โ”‚
โ”‚ displayTitle โ”‚ Add CLI completions                                     โ”‚
โ”‚ headBranch   โ”‚ clap-complete-16608                                     โ”‚
โ”‚ url          โ”‚ https://github.com/iesahin/xvc/actions/runs/13007688178 โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

๐Ÿข Although I turned off most of the watches, logs are still so large that itโ€™s not possible to view them from the interface. Downloaded the log archive.

๐ŸฆŠ We can have different GitHub Actions steps for each test. Claude can help write such a repeating set of steps.

๐Ÿ‡ We can at least separate z_test_docs to see if integration tests or that fails.

๐Ÿข Weโ€™ll have to add caching for test artifacts to upload them for coverage. Iโ€™m not sure we really need to add that complexity to the process just to avoid downloading the logs.

๐Ÿ‡ We can actually move all to Xvc. We need a GitHub Action to run an Xvc pipeline.

๐Ÿข Eventually yes, we should move our testing to Xvc itself. Now, the logs show that there are differences in z_test_docs actually.

๐Ÿ‡ When I run the command below, it passes. We may have a different config in GitHub Actions.

$ XVC_TRYCMD_TESTS=storage,file,pipeline,core,start TRYCMD=overwrite rws cargo test --features test-ci -p xvc --test z_test_docs
test z_doc_tests ... ok
...
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 15.34s

๐Ÿข We donโ€™t have an XVC_TRYCMD_TESTS=storage,file,pipeline,core,start definition in GitHub Actions; letโ€™s add it, bump the version, and try again.

$ cargo set-version "0.6.14-alpha.10"
   Upgrading xvc from 0.6.14-alpha.9 to 0.6.14-alpha.10
...

๐ŸฆŠ We could also update run-tests.zsh to get a quick response.

๐Ÿข Yep, letโ€™s do that as well.

๐Ÿ‡ Dev tests pass, but there are differences in the documents still. For some reason, the local run-tests.zsh doesnโ€™t update xvc/book/src/ref/xvc-storage.md. It only has the help text as a reference, but itโ€™s not updated with the aliases, and it breaks the CI. This is weird but a small issue. Fixed it manually.

$ ghrl | first 2
โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ # โ”‚ conclusion โ”‚   displayTitle    โ”‚     headBranch     โ”‚        url         โ”‚
โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 0 โ”‚            โ”‚ Add CLI           โ”‚ clap-complete-1660 โ”‚ https://github.com โ”‚
โ”‚   โ”‚            โ”‚ completions       โ”‚ 8                  โ”‚ /iesahin/xvc/actio โ”‚
โ”‚   โ”‚            โ”‚                   โ”‚                    โ”‚ ns/runs/1302770108 โ”‚
โ”‚   โ”‚            โ”‚                   โ”‚                    โ”‚ 2                  โ”‚
โ”‚ 1 โ”‚ success    โ”‚ Add CLI           โ”‚ clap-complete-1660 โ”‚ https://github.com โ”‚
โ”‚   โ”‚            โ”‚ completions       โ”‚ 8                  โ”‚ /iesahin/xvc/actio โ”‚
โ”‚   โ”‚            โ”‚                   โ”‚                    โ”‚ ns/runs/1302755462 โ”‚
โ”‚   โ”‚            โ”‚                   โ”‚                    โ”‚ 2                  โ”‚
โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

๐Ÿข The earlier one has passed. It looks like weโ€™re ready to merge. Letโ€™s update the CHANGELOG.md for release.

๐Ÿ‡ Setting the release version:

cargo set-version "0.6.14"
   Upgrading xvc from 0.6.14-alpha.10 to 0.6.14
...

๐Ÿข Letโ€™s check the CI

$ ghrl | first 2

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ # โ”‚ conclusion โ”‚   displayTitle    โ”‚     headBranch     โ”‚        url         โ”‚
โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 0 โ”‚ success    โ”‚ Add CLI           โ”‚ clap-complete-1660 โ”‚ https://github.com โ”‚
โ”‚   โ”‚            โ”‚ completions       โ”‚ 8                  โ”‚ /iesahin/xvc/actio โ”‚
โ”‚   โ”‚            โ”‚                   โ”‚                    โ”‚ ns/runs/1302770108 โ”‚
โ”‚   โ”‚            โ”‚                   โ”‚                    โ”‚ 2                  โ”‚
โ”‚ 1 โ”‚ success    โ”‚ Add CLI           โ”‚ clap-complete-1660 โ”‚ https://github.com โ”‚
โ”‚   โ”‚            โ”‚ completions       โ”‚ 8                  โ”‚ /iesahin/xvc/actio โ”‚
โ”‚   โ”‚            โ”‚                   โ”‚                    โ”‚ ns/runs/1302755462 โ”‚
โ”‚   โ”‚            โ”‚                   โ”‚                    โ”‚ 2                  โ”‚
โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

๐Ÿ‡ And merge:

$ ghpM --body $"(open CHANGELOG.md | lines | skip 2 | take 7)" --subject "Add completions" --squash

๐Ÿข Tagged main and pushed. Packages should be built in a few minutes.

devlog 20

๐Ÿข We have an error in the publish action. Letโ€™s fix it and rerun it.

๐Ÿ‡ There are two errors. One resulted from forgetting sudo when installing dependencies. The other was the incorrect name for the OpenSSL library. It should be libssl-dev instead of openssl-dev. Maybe we can link the command list in the docs.

๐Ÿข Oops, yes, we should at least keep that in mind.

๐Ÿ‡ Iโ€™m checking the most popular crates. getrandom retrieves a random number from the system. It looks much lighter than the rand crate.

๐ŸฆŠ We can use once_cell to initialize XvcEntityCounter. We currently use Once for this purpose.

๐Ÿ‡ I donโ€™t think it will provide any better features.

๐ŸฆŠ For that case, yes, no better features. But the interface is something like:

impl<T> OnceCell<T> {
    const fn new() -> OnceCell<T> { ... }
    fn set(&self, value: T) -> Result<(), T> { ... }
    fn get(&self) -> Option<&T> { ... }
}

And this makes, for example, working with XvcRoot much easier. We are passing Arc<RwLock<XvcRootInner>>> everywhere. This is a heavy price when we only use it in a read-only manner. We can prevent most of these, when we use a read lock, by using OnceCell.

๐Ÿ‡ XvcConfig can benefit from this as well. We donโ€™t update the config during runs.

๐Ÿข Why do we want to assign it, though? We currently have a config field in XvcRootInner, and we get a reference to it with the config() method.

๐Ÿ‡ Ok. Letโ€™s skip this for now. No need to worry before measuring the performance impact.

devlog 21

๐Ÿข Now, the next version will have a --json output for xvc file list. We can start working on it or update the Readme file?

๐Ÿ‡ What about adding at least command completions for Nushell?

๐Ÿข Letโ€™s read a bit about clap_complete_nushell.

๐ŸฆŠ There seems to be a nu-complete command. Letโ€™s check its documentation.

๐Ÿ‡ Nothing was found, and Kagi doesnโ€™t help much either.

๐Ÿข There is a completions document for Nushell: https://www.nushell.sh/book/custom_completions.html

๐Ÿ‡ There is a tool called carapace to provide completions across shells.

๐Ÿข Its documentation is thin, and Iโ€™m not sure if it supports dynamic completions out of the box. I believe instead of adding a carapace setup, we can just write a Nushell completion script that will use JSON output from the commands and add some (maybe hidden) utility commands to support it.

๐Ÿ‡ There are a set of example scripts in the Nushell repo: https://github.com/nushell/nu_scripts/tree/main/custom-completions

๐Ÿข The reason I want to write custom completions for Nushell is that it will be an exercise for the scripting language. gh completions are not as scary as a Bash script.

๐Ÿ‡ git completions are a better example for XVC. They simply run git whenever necessary. We can start from a static completions command and update this with dynamic completions manually. It will teach a lot.

๐Ÿข I forked the nu_scripts repo and will add XVC completions script there.

๐Ÿ‡ Then letโ€™s begin by adding Nushell static completions. Shall we add a command for this?

๐ŸฆŠ Reviving the completion command we removed in 0.6.13?

๐Ÿข We shouldnโ€™t list it. We can make a _comp subcommand for the time being and generate and distribute completions in the repository. When clap_complete_nushell has the feature parity to provide dynamic completions, we can remove these commands.

๐Ÿ‡ What will we use this for other than generating completions?

๐Ÿข Maybe dynamic completions can call this as well.

๐Ÿ‡ Added Nushell static completions to be output using xvc _comp generate-nushell. Letโ€™s bump up the version to 0.6.15.

cargo set-version 0.6.15-alpha.1
   Upgrading xvc from 0.6.14 to 0.6.15-alpha.1
...

๐Ÿข I noticed we forgot a line in the CLI command handler that asserts xvc_root_opt.is_some(), and this fails when we run xvc outside of repositories. We need to release this version quickly.

๐Ÿ‡ Oops, now, ok, letโ€™s write a static Nushell generator and just release quickly.

๐ŸฆŠ Generating completions with

xvc comp generate-nushell

๐Ÿข Completion command is run with comp instead of _comp. Should we rename it?

๐Ÿ‡ Renamed it to _comp. Itโ€™s not hidden, but at least we can be sure that it wonโ€™t be misunderstood as a common command.

๐Ÿข Bumping the version again. Now letโ€™s source the generated script and test it.

cargo set-version 0.6.15-alpha.2
   Upgrading xvc from 0.6.15-alpha.1 to 0.6.15-alpha.2
...

๐ŸฆŠ Yep, it works. We now have completions for Nushell.

๐Ÿข Letโ€™s update the completions documentation.

๐Ÿ‡ Done. Now, letโ€™s take a look at CI and see what fails.

ghrl | first
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ conclusion   โ”‚ success                                                 โ”‚
โ”‚ displayTitle โ”‚ Add Nushell completions                                 โ”‚
โ”‚ headBranch   โ”‚ nushell-completions                                     โ”‚
โ”‚ url          โ”‚ https://github.com/iesahin/xvc/actions/runs/13070200765 โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

๐Ÿข It fails because of coverage, not the tests. Codecov says the new code isnโ€™t tested.

๐Ÿ‡ The added xvc _comp command isnโ€™t tested. We can add a test running those lines and testing if the command outputs a completion script.

๐Ÿข We have a test for completions. We can add a test that runs the lines.

๐Ÿ‡ Added a test and bumping up the version.

cargo set-version 0.6.15-alpha.3
   Upgrading xvc from 0.6.15-alpha.2 to 0.6.15-alpha.3
...

๐ŸฆŠ We can add some more coverage while waiting for the tests.

๐Ÿ‡ XvcOutputLine implementation seems to have no tests. Itโ€™s weird because we use these everywhere.

๐Ÿข Iโ€™m not sure we use this particular implementation; we just use XvcOutputLine::Info(s), not XvcOutputLine::info(s) anywhere. We can delete these methods actually.

๐Ÿ‡ Weโ€™ll add JSON output via this particular struct. Can we refactor these to use formatting for JSON, for example? Or use these to output JSON?

๐ŸฆŠ We can add a formatter to XvcOutputLine to output structures.

๐Ÿข The enum is now defined as:

#[derive(Clone, Debug)]
pub enum XvcOutputLine {
    /// The output that we should be reporting to user
    Output(String),
    /// For informational messages
    Info(String),
    /// For debug output to show the internals of Xvc
    Debug(String),
    /// Warnings that are against some usual workflows
    Warn(String),
    /// Errors that interrupts a workflow but may be recoverable
    Error(String),
    /// Panics that interrupts the workflow and ends the program
    /// Note that this doesn't call panic! automatically
    Panic(String),
    /// Progress bar ticks.
    /// Self::Info is also used for Tick(1)
    Tick(usize),
}

Here, these fields can also have a formatter that will render the string in a particular format. For example, the output can be

XvcOutputLine::Output(XvcJsonFormatter, String)

๐Ÿ‡ Iโ€™m not sure this is a good idea. Output already specifies this string as output. We can have a wrapper instead, like,

struct XvcJsonOutput(Format<XvcStructuredOutput>, XvcStructuredOutput)

and we can use the supplied format to render XvcStructuredOutput to an output line with XvcOutputLine::Output. If we donโ€™t provide output as structured, it will be too much error-prone work to convert the current outputs to structured.

๐ŸฆŠ The transition will also be gradual. We may not need structured output for most of the commands. We can start with xvc file list and convert others as we go.

๐Ÿข This is sensible. By the way, coverage still didnโ€™t increase. There may be something going on with Codecov or running the test.

ghrl | first
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ conclusion   โ”‚ success                                                 โ”‚
โ”‚ displayTitle โ”‚ Add Nushell completions                                 โ”‚
โ”‚ headBranch   โ”‚ nushell-completions                                     โ”‚
โ”‚ url          โ”‚ https://github.com/iesahin/xvc/actions/runs/13087431038 โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

๐Ÿ‡ Letโ€™s run the test:

cargo test -p xvc --test test_completions
...
test test_completions ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.80s

๐Ÿข Can we make sure the output is a Nushell script and not an error message?

๐Ÿ‡ Letโ€™s print it out.

๐Ÿข It looks like when the COMPLETE environment variable is set, it never calls _comp subcommand and never calls those lines.

cargo set-version 0.6.15-alpha.4
   Upgrading xvc from 0.6.15-alpha.3 to 0.6.15-alpha.4
...

๐Ÿข Letโ€™s make a release for 0.6.15. Coverage is OK now.

cargo set-version 0.6.15
   Upgrading xvc from 0.6.15-alpha.4 to 0.6.15
...
gh pr merge --squash --body $"(open CHANGELOG.md | lines | skip 2 | take 5)" --subject "Add static nushell completions"

๐Ÿ‡ Merged the PR.

๐Ÿข Releases should appear in a few minutes.

๐Ÿ‡ We need to tag the merge commit for this.

๐Ÿข Oh, yep. AFAIK Lazygit doesnโ€™t have something for git push --tags. Letโ€™s push from the CLI.

git push --tags
You are on the main branch. Skipping CHANGELOG.md check.
To github.com:iesahin/xvc
 * [new tag]         v0.6.15 -> v0.6.15

๐ŸฆŠ These commands, especially tables, are not rendered correctly on the web. We need to change the theme, I think.

devlog 22

๐Ÿข We need to find a theme for the blog. The current one breaks Nushell output tables because the code blocks are too narrow.

๐Ÿ‡ https://www.getzola.org/themes/pico/ is an option, but I donโ€™t like its header.

๐ŸฆŠ Minimal Dark from the same author looks better: https://kuznetsov17.github.io/minimal-dark/notes/note1/

๐Ÿ‡ https://www.getzola.org/themes/no-style-please/ is also an option.

๐ŸฆŠ https://halve-z.netlify.app/posts/information/ looks interesting, but there is too much screen estate for the left bar.

๐Ÿข Letโ€™s start by running the site locally first.

๐Ÿ‡ There are errors in the configuration. Thatโ€™s weird, but letโ€™s fix these.

๐Ÿข Fixed errors. These are probably related to a newer version. Can we take a look at netlify.toml to see if it downloads the same version?

๐Ÿ‡ There are breaking changes in Zola 0.19. Letโ€™s update and push the Netlify config to see the results.

๐Ÿข We need to add language support for Nushell to prevent warnings. Take a look at how to add a syntax file to Zola.

๐Ÿ‡ I added

extra_syntaxes_and_themes = ["syntaxes"]

to the [markdown] section and added a syntaxes/nushell.sublime-syntax file copied from https://github.com/kurokirasama/nushell_sublime_syntax. Iโ€™m getting:

Error: Reason: Error while compiling regex '\b(?x: 7z | ?
...
s-to-gdrive | usage | ver | verify | weather | wget-all | which-cd | wifi-info | wifi-pass | xls2csv | ydx | yt-api | ytcli | ytm | z | zi)\b'
Oniguruma error: target of repeat operator is not specified

๐Ÿข The syntax highlighter may be a bit buggy. Letโ€™s try to fix this if itโ€™s a one-off.

๐Ÿ‡ Found the bug. There is a ? in the regex that causes it to fail. Now it compiles, and Nushell blocks are colored.

๐Ÿข Cool. Letโ€™s fix the other warnings now. shell and console are not recognized, it looks like.

๐Ÿ‡ There is only Bash listed in https://www.getzola.org/documentation/content/syntax-highlighting/.

๐Ÿฆ What do you think about migrating to mdBook? We already maintain mdBook for XVC; what about just moving the site to mdBook?

๐Ÿข I thought about this before, and the only downside is the lack of an RSS feed.

๐ŸฆŠ I found this: https://github.com/theowenyoung/mdbook-rss

๐Ÿข Now, this changes everything. We can even move the nedriy.at site to mdBook in this case.

๐Ÿ‡ Then, letโ€™s start working on this. The site will be a technical book site in this case.

๐Ÿข Does mdBook support Nushell syntax?

๐Ÿ‡ Nushell is not in the listed languages. mdBook uses highlight.js, and in its listed languages, we donโ€™t find Nu either.

๐Ÿข We already added Nu support to Zola, and we can just change the theme. This is a blocker in my opinion.

๐Ÿ‡ I searched for Nushell highlight.js support, and nothing appears. I think we can just postpone until Nu has more support on this front.

๐Ÿข Yes. Letโ€™s first try this change in the non-technical blog, and we can come back to this issue. Now, weโ€™ll update console and shell to bash, I think.

๐Ÿ‡ Replaced shell and console with bash.

๐Ÿข There is a file for ggplot that has warnings from earlier incarnations. We also lack syntax highlighters for Vim and Tmux.

๐Ÿ‡ There is a sublime-syntax file for Tmux at https://raw.githubusercontent.com/gerardroche/sublime-tmux/refs/heads/master/Tmux.sublime-syntax, but do we need it for a single file?

๐Ÿข Letโ€™s set it to plain text.

๐Ÿ‡ Now we only have Mermaid warnings left.

๐Ÿข There should be a diagram at https://emresahin.net/developing-a-gitignore-crate/, but it doesnโ€™t show up. We need a shortcode to show these, like the YouTube shortcode. Now letโ€™s get back to theme selection.

๐Ÿ‡ I tested Karzok, but it doesnโ€™t have category and tags support.

๐Ÿข And I tested https://github.com/micahkepe/radion, but the best so far is the apollo theme. Iโ€™m struggling to modify the index page, though. I forgot that I modified the themeโ€™s index.html file. I have content in /content/_index.md and a modified index.html in /themes/anemone/templates/index.html to show the content, tags, categories, etc. It should be fixed now.

๐Ÿ‡ Ah, cool. Can we clean the recent duplicate pages now?

๐Ÿข Yeah, letโ€™s take a look.

devlog 23

๐Ÿ‡ Letโ€™s turn to discussing the JSON output changes. We can add another option to XvcOutputLine, like XvcOutputLine::Json(T: Serialize), that will output the type using serde_json. It wonโ€™t introduce any other type.

๐Ÿข Yes, but what will T be? Although store structures have Serde implementations, they are not particularly useful for this.

๐Ÿ‡ We can have output types, named like XvcFileListOutput, that will be converted to strings with serialization.

๐ŸฆŠ We do something similar in xvc pipeline export and import commands. We use XvcPipelineSchema and XvcStepSchema just for the import and export commands. Weโ€™ll write similar structs for all JSON output and will use Serde to convert these to strings.

๐Ÿข Unlike the import and export commands, we have optional fields in the output, though. I donโ€™t want content digests to appear in JSON output if they are not required.

๐Ÿ‡ Letโ€™s search for optional fields in Serde.

๐ŸฆŠ There is a crate for optional fields.

๐Ÿข We donโ€™t need another crate for this. Serde has the skip_serializing_if attribute for fields. We can add Option::is_none as a method to these to skip outputting None fields. All those fields, in this case, will be optional.

๐Ÿ‡ This is fine. We already use structs to format the xvc file list output. We can just use them to output JSON.

devlog 24

๐Ÿ‡ Is there a way to implement Default for CLI structs?

๐Ÿข There is a way if we start from the configuration, not just files. The default configuration is a TOML document. We can make it an XvcConfiguration struct and load and store it with confy.

๐Ÿ‡ We have a cascading set of configurations, but maybe we can start from the struct and serialize/deserialize it on demand.

๐Ÿฒ I donโ€™t think we need to update xvc-config at the moment. Itโ€™s working, and we donโ€™t need to alter its inner workings in the near future.

๐Ÿข I agree. We can use the config crate when we need to update and have enough time to work on this.

devlog 25

๐Ÿข I want to update xvc.py to the latest version.

๐Ÿ‡ It should only be needed to update the dependency versions in Cargo.toml, right?

๐Ÿข Letโ€™s start with that.

๐Ÿ‡ We have interface changes regarding aliases; letโ€™s start using uv for building.

๐ŸฆŠ Added requirements to pyproject.toml by running:

uv add -r requirements.txt

devlog 26

๐Ÿข We can add type checking to xvc.pyโ€™s command-line handler. Currently, it builds a command line manually from the given options and parses it with clap. Itโ€™s error-prone.

๐Ÿ‡ What are the options, though? xvc.py is just a wrapper around xvc, and that was the easiest way to get it working. We can list all options manually in the headers as documentation, but it will be harder to maintain.

๐Ÿข Once we start doing it, weโ€™ll find a good way to simplify and shorten it.

๐Ÿ‡ Letโ€™s start to work on xvc file track, then.

๐Ÿข Worked on it a bit, and I decided itโ€™s not worth it at the moment. We need to supply default values for most of the options. Maintaining a separate list of default values may not be feasible; it will be error-prone in a different way.

devlog 27

๐Ÿ‡ Tests are failing again:

ghrl | get url | first
https://github.com/iesahin/xvc/actions/runs/13875177382

๐Ÿข We forgot to update the doc tests. Letโ€™s run them again to update storage remove and file untrack commands.

๐Ÿ‡ There are issues with elision.

ghpl

โ•ญโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ # โ”‚     headRefName      โ”‚       title        โ”‚             url              โ”‚
โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 0 โ”‚ storage-remove-16674 โ”‚ xvc storage remove โ”‚ https://github.com/iesahin/x โ”‚
โ”‚   โ”‚                      โ”‚                    โ”‚ vc/pull/270                  โ”‚
โ•ฐโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

๐Ÿข Tests are passing; we can merge the PR. But our commit hook to check the CHANGELOG doesnโ€™t work. Thatโ€™s weird; we donโ€™t get any errors when the CHANGELOG is not in the push set.

tmux new-window -c ($env.HOME | path join github.com iesahin xvc.py)  nvim 

๐Ÿข Now we can update the Python bindings as well.

๐Ÿ‡ We forgot to bump package versions. We need to create a checklist for releases.

  • โœ… #๐ŸŒป CREATE a release checklist (2025-03-25 17:52)

๐Ÿข Letโ€™s check if the latest version is updated on PyPI.

pypi xvc

๐Ÿ‡ Yes, it is.

devlog 28

๐Ÿข We can start adding an rclone remote as well.

๐Ÿข Created the PR, waiting for the tests.

๐ŸฆŠ Letโ€™s search if there is a crate to manage rclone. Maybe it will be easier that way.

๐Ÿ‡ It will add another dependency though.

๐ŸฆŠ We can always add a feature flag for this.

๐Ÿข There is a librclone crate that can be used to call rclone commands like https://rclone.org/rc/#supported-commands

๐ŸฆŠ We can test it from the command line perhaps.

rclone rc 
2025/03/15 17:41:09 NOTICE: Failed to rc: failed to list: connection failed: Post "http://localhost:5572/rc/list": dial tcp [::1]:5572: connect: connection refused

๐Ÿ‡ It requires the backend to be running in the background.

๐ŸฆŠ There may be examples in the repository.

๐Ÿข There are none. We can search GH for this crate though.

๐ŸฆŠ Itโ€™s also possible to search for dependents in crates.io.

๐Ÿข I think Xvc will be the first dependent of this crate: https://crates.io/crates/librclone/reverse_dependencies

๐Ÿ‡ The following two projects depend on librclone:

  • https://github.com/Sh3mm/WarpDrive/tree/master
  • https://github.com/hwittenborn/celeste

๐Ÿข Letโ€™s clone Celeste. It uses librclone and looks like itโ€™s a user interface for rclone written in Rust.

๐Ÿ‡ The examples are in celeste/src/rclone.rs.

๐Ÿข Cool. Letโ€™s take a look at how commands are run:

    /// Common function for some of the below command.
    fn common(command: &str, remote_name: &str, path: &str) -> Result<(), RcloneError> {
        let resp = run(
            command,
            &json!({
                "fs": get_remote_name(remote_name),
                "remote": util::strip_slashes(path),
            })
            .to_string(),
        );

        match resp {
            Ok(_) => Ok(()),
            Err(json_str) => Err(serde_json::from_str(&json_str).unwrap()),
        }
    }

All commands are run like librclone::rpc(method, input)) and the commands are like:

    /// make a directory on the remote.
    pub fn mkdir(remote_name: &str, path: &str) -> Result<(), RcloneError> {
        common("operations/mkdir", remote_name, path)
    }

๐Ÿ‡ We have all commands in this file that are relevant to Xvc. Letโ€™s list them here:

  • make directory: common("operations/mkdir", remote_name, path)
  • delete file: common("operations/delete", remote_name, path)
  • remove a dir and all of its contents: common("operations/purge", remote_name, path)
  • copy file:
run( "operations/copyfile",
            &json!({
                "srcFs": src_fs,
                "srcRemote": util::strip_slashes(src_remote),
                "dstFs": dst_fs,
                "dstRemote": util::strip_slashes(dst_remote)
            })

and


    /// Copy a file from the local machine to the remote.
    pub fn copy_to_remote(
        local_file: &str,
        remote_name: &str,
        remote_destination: &str,
    ) -> Result<(), RcloneError> {
        copy(
            "/",
            local_file,
            &get_remote_name(remote_name),
            remote_destination,
        )
    }

    /// Copy a file from the remote to the local machine.
    pub fn copy_to_local(
        local_destination: &str,
        remote_name: &str,
        remote_file: &str,
    ) -> Result<(), RcloneError> {
        copy(
            &get_remote_name(remote_name),
            remote_file,
            "/",
            local_destination,
        )
    }

๐Ÿ‡ It looks like thatโ€™s all we need. We can organize the commands differently, but these examples are enough to use librclone. It seems rather straightforward.

devlog 29

๐Ÿข As the new version is updated, we can go back to the project. Whatโ€™s the next step?

๐Ÿ‡ We can continue working on rclone.

๐Ÿข Umm, ok. Letโ€™s try to focus on adding another storage type.

  • โœ… #๐ŸŒป ADD rclone storage type (2025-04-24 06:27)

๐Ÿข I think the first option is to run the commands from the command line. We can just use a modified generic storage type without trying to make it fast.

๐Ÿ‡ Letโ€™s make it run first, you say?

๐Ÿข Yes, letโ€™s make it run first and then we can think about making it run fast.

๐Ÿ‡ Youโ€™re right!

  • โœ… #๐ŸŒป ADD generic rclone tests (2025-04-24 06:27)

Itโ€™s possible to use the alias remote with a local path.

devlog 30

๐Ÿข Letโ€™s discuss how to move Xvc forwardโ€”maybe we can write a post to Reddit and the Rust forum in the meantime. ๐Ÿ‡ I think the next step is rclone remotes. It will allow us to use all remote storages supported by Rclone, which is a nice feature. ๐Ÿข Donโ€™t you think we need to publish the current version to Reddit and the forum? ๐Ÿ‡ We can do that as well. ๐ŸฆŠ Adding rclone remote must be a straightforward task. ๐Ÿข We need to understand rclone paths, but overall, yes. Weโ€™ll just need to get the remote name, like drive://, and a path, like my-xvc-storage, and build paths with these. ๐Ÿ‡ What are the commands? ๐Ÿข We need to learn how to upload files from local to remote and how to download these files. We can also list the files and get files as well. ๐Ÿฒ How about adding a paths.txt to folders in remotes to show which paths the files in 0.jpg belong to? This will change the remote cache structure a bit. We will have a reverse index of files and they will be findable. ๐Ÿข Whatโ€™s the reason for this? ๐Ÿฒ When I upload a file to Drive with only the content hash, I lose track of the actual path. This is not desirable. We can add a file to the directory, called paths.txt, to get the paths for a file. ๐Ÿข This may prove to be a feat, though; adding these XvcPaths to a file requires a lookup. ๐Ÿ‡ Maybe a JSON file? It might be possible to look up a path with a JSON file, and it will be easier to parse. ๐Ÿฒ I donโ€™t think the issue is about parsing, though. We can just have a plain text file that lists the paths. Itโ€™s a text file, which is the most compatible across all storages. ๐Ÿข Storages, you mean. ๐Ÿฒ Ugh, yeah. If I have a file called Alan Watts but I only have the content, this file will be immensely useful. ๐Ÿข This makes XvcCachePath and XvcPath coupled. Architecture-wise, it may not be a good thing, though. ๐Ÿ‡ Also, there may be common storages for multiple repositories. ๐Ÿฒ Umm, thatโ€™s a good point. I donโ€™t think the architecture will be much compromised, though. We already keep the file paths and their cache paths somewhere. ๐Ÿข Cache paths are generated from the content, but any number of paths can point to a single path in the cache. If I have 1 million copies of the same file, will I add all these files to the paths.txt you mentioned? ๐Ÿฒ Thatโ€™s a good point too. We can have a limit, like 1,000 or something, not to make these files too big. ๐Ÿข Instead of this, we can store the output of xvc file list at the storage root and allow looking up the files that way. ๐Ÿฒ It has the same problem, though; if we have a million files, their list will be too large. ๐Ÿข There can be a manual command, like xvc file index --to storage, that will show content hashes and paths of each file. We can also add URLs to files if possible. ๐Ÿฒ No one will use it when itโ€™s manual, though. ๐Ÿข We can add functionality to update this index when we send a file, though.
๐Ÿฒ So, after each send, weโ€™ll update the index for the repository on that storage. Is that correct? ๐Ÿข Not after each send. After each send session, maybe. ๐Ÿ‡ We can have an incremental way of updating the index, like we do in ECS? ๐Ÿข It will be overkill for this functionality and add too much noise to the storage. ๐Ÿฒ Letโ€™s keep this discussion here, but I also want to have an index merge or index cleanup mechanism for the entity generator and the ECS. ๐Ÿข We can have a โ€œmerge indicesโ€ functionality in ECS. That will remove all older entity-generator files and merge all store files. ๐Ÿ‡ Removing older entity files is easy, but what about merging the store files? ๐Ÿข Itโ€™s easy too. Weโ€™ll just load all event logs from the directory, remove all other files, and save the event log to a file. ๐Ÿ‡ Will this be manual or automatic? ๐Ÿข I think the first version can be manual, something like xvc fsck merge-store-files or something like that. We can notify the user if the number of files is > 10,000 or something like that. I donโ€™t think we need to make it automatic unless we measure the impact of these files. There is no point in trying to do it at every command. ๐Ÿ‡ Then weโ€™ll have two new commands for the next version? ๐Ÿข I think we can just add rclone remote now and release it, then make changes in the ECS for this new xvc fsck command. ๐Ÿ‡ Can the name be doctor or something? Or util? Or can we add a top-level merge indices command? ๐Ÿข xvc doctor seems like a better alternative. We can have a diagnose subcommand as well to check for possible inconsistencies. xvc doctor merge-store-files is a better command. ๐Ÿฒ Will we use d for this command? ๐Ÿข No need to add a single-letter command for this, I believe. It shouldnโ€™t be required to run frequently. ๐Ÿ‡ Hmm, ok. What do we need to know for rclone remote? ๐Ÿฒ I noticed we donโ€™t have the xvc storage remove command implemented yet. Maybe we can start from that. ๐Ÿข Hmm, yeap. Letโ€™s start by implementing that first. We can add the rclone command next. ๐Ÿ‡ Will we use a feature flag for rclone? It will run the command only with an external binary. ๐Ÿข Itโ€™s better to have a feature flag. I think we can add a feature flag for rsync remote as well. ๐Ÿ‡ We can use the generic one to update the feature flag. ๐Ÿข I think the only two items of information we need for rclone are the remote name and the remote directory. Will we make these required?

devlog 31

๐Ÿข I think sending Alt keys in Ghostty needs some love. Letโ€™s start with it as an easy task for the day.

๐Ÿ‡ While checking macos-option-as-alt as a potential source of the problem, I spent time adjusting the Ghostty icon. I changed the app icon. This is how I spend my time being extremely productive.

๐Ÿข Weird. The Option as alt key setting is fine.

๐Ÿ‡ Actually, the Alt key works; the reason we cannot make panes larger is that Alt-Right and Alt-Left are not bound to this.

๐Ÿข You may be right, yes. Then, whatโ€™s the key to enlarge a pane?

๐ŸฆŠ List all the keys. Check the keys. You should have done this already.

$ tmux list-keys | rg resize

bind-key    -T prefix       >                      display-menu -T "#[align=centre]#{pane_index} (#{pane_id})" -x P -y P "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}" < { send-keys -X history-top } "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}" > { send-keys -X history-bottom } '' "#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}" C-r { if-shell -F "#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}" "copy-mode -t=" ; send-keys -X -t = search-backward "#{q:mouse_word}" } "#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}" C-y { copy-mode -q ; send-keys -l "#{q:mouse_word}" } "#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}" c { copy-mode -q ; set-buffer "#{q:mouse_word}" } "#{?mouse_line,Copy Line,}" l { copy-mode -q ; set-buffer "#{q:mouse_line}" } '' "#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}" C-h { copy-mode -q ; send-keys -l "#{q:mouse_hyperlink}" } "#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}" h { copy-mode -q ; set-buffer "#{q:mouse_hyperlink}" } '' "Horizontal Split" h { split-window -h } "Vertical Split" v { split-window -v } '' "#{?#{>:#{window_panes},1},,-}Swap Up" u { swap-pane -U } "#{?#{>:#{window_panes},1},,-}Swap Down" d { swap-pane -D } "#{?pane_marked_set,,-}Swap Marked" s { swap-pane } '' Kill X { kill-pane } Respawn R { respawn-pane -k } "#{?pane_marked,Unmark,Mark}" m { select-pane -m } "#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}" z { resize-pane -Z }
bind-key    -T prefix       z                      resize-pane -Z
bind-key -r -T prefix       M-Up                   resize-pane -U 5
bind-key -r -T prefix       M-Down                 resize-pane -D 5
bind-key -r -T prefix       M-Left                 resize-pane -L 5
bind-key -r -T prefix       C-Up                   resize-pane -U
bind-key -r -T prefix       C-Down                 resize-pane -D
bind-key -r -T prefix       C-Left                 resize-pane -L
bind-key -r -T prefix       C-Right                resize-pane -R
bind-key    -T root         MouseDown3Pane         if-shell -F -t = "#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}" { select-pane -t = ; send-keys -M } { display-menu -T "#[align=centre]#{pane_index} (#{pane_id})" -t = -x M -y M "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}" < { send-keys -X history-top } "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}" > { send-keys -X history-bottom } '' "#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}" C-r { if-shell -F "#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}" "copy-mode -t=" ; send-keys -X -t = search-backward "#{q:mouse_word}" } "#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}" C-y { copy-mode -q ; send-keys -l "#{q:mouse_word}" } "#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}" c { copy-mode -q ; set-buffer "#{q:mouse_word}" } "#{?mouse_line,Copy Line,}" l { copy-mode -q ; set-buffer "#{q:mouse_line}" } '' "#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}" C-h { copy-mode -q ; send-keys -l "#{q:mouse_hyperlink}" } "#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}" h { copy-mode -q ; set-buffer "#{q:mouse_hyperlink}" } '' "Horizontal Split" h { split-window -h } "Vertical Split" v { split-window -v } '' "#{?#{>:#{window_panes},1},,-}Swap Up" u { swap-pane -U } "#{?#{>:#{window_panes},1},,-}Swap Down" d { swap-pane -D } "#{?pane_marked_set,,-}Swap Marked" s { swap-pane } '' Kill X { kill-pane } Respawn R { respawn-pane -k } "#{?pane_marked,Unmark,Mark}" m { select-pane -m } "#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}" z { resize-pane -Z } }
bind-key    -T root         MouseDrag1Border       resize-pane -M
bind-key    -T root         M-MouseDown3Pane       display-menu -T "#[align=centre]#{pane_index} (#{pane_id})" -t = -x M -y M "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}" < { send-keys -X history-top } "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}" > { send-keys -X history-bottom } '' "#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}" C-r { if-shell -F "#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}" "copy-mode -t=" ; send-keys -X -t = search-backward "#{q:mouse_word}" } "#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}" C-y { copy-mode -q ; send-keys -l "#{q:mouse_word}" } "#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}" c { copy-mode -q ; set-buffer "#{q:mouse_word}" } "#{?mouse_line,Copy Line,}" l { copy-mode -q ; set-buffer "#{q:mouse_line}" } '' "#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}" C-h { copy-mode -q ; send-keys -l "#{q:mouse_hyperlink}" } "#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}" h { copy-mode -q ; set-buffer "#{q:mouse_hyperlink}" } '' "Horizontal Split" h { split-window -h } "Vertical Split" v { split-window -v } '' "#{?#{>:#{window_panes},1},,-}Swap Up" u { swap-pane -U } "#{?#{>:#{window_panes},1},,-}Swap Down" d { swap-pane -D } "#{?pane_marked_set,,-}Swap Marked" s { swap-pane } '' Kill X { kill-pane } Respawn R { respawn-pane -k } "#{?pane_marked,Unmark,Mark}" m { select-pane -m } "#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}" z { resize-pane -Z }

๐Ÿ‡ resize-pane has keys, but they conflict with C-Right, etc., which are macOS display selection keys. I defined M-S-Right, etc., to resize and M-C-Right, etc., to swap the panes. Defining new keys with M-Sโ€ฆ

bind-key -n "M-S-Right" resize-pane -R 10
bind-key -n "M-S-Left" resize-pane -L 10
bind-key -n "M-S-Up" resize-pane -U 10
bind-key -n "M-S-Down" resize-pane -D 10

๐Ÿข We can set keys to swap windows too, maybe to M-C-PageDown, etc. M-PageDown now moves between windows.

๐Ÿ‡ Letโ€™s take a look at the command options:

     swap-window [-d] [-s src-window] [-t dst-window]
                   (alias: swapw)
             This is similar to link-window, except the source and destination windows are swapped.  It is an error if no window exists at src-window.  If -d is given, the new window
             does not become the current window.

             If -s is omitted and a marked pane is present (see select-pane -m), the window containing the marked pane is used rather than the current window.

๐Ÿข The use case will be limited, though.

๐Ÿ‡ Umm, right. Letโ€™s stop procrastinating here.

๐Ÿข It was productive procrastination, though. Now I can resize my tmux panes.

devlog 32

This dialog is about converting ColQwen2 to the ONNX format.

๐Ÿข Now, I have two classes. This one is a copy of the ONNX Patcher thatโ€™s used to convert ColQwen2 to ONNX format. But this one is valid only for the underlying model, that is Qwen2VLForConditionalGeneration, and ColQwen2 also has some modifications to call it.

๐Ÿ‡ What are these modifications?

๐Ÿข The forward method looks something like this:

ColQwen2

    def forward(self, *args, **kwargs) -> torch.Tensor:
        kwargs.pop("output_hidden_states", None)

        # Handle the custom "pixel_values" input obtained with `ColQwen2Processor` through unpadding
        if "pixel_values" in kwargs:
            offsets = kwargs["image_grid_thw"][:, 1] * kwargs["image_grid_thw"][:, 2]  # (batch_size,)
            kwargs["pixel_values"] = torch.cat(
                [pixel_sequence[:offset] for pixel_sequence, offset in zip(kwargs["pixel_values"], offsets)],
                dim=0,
            )

        position_ids, rope_deltas = self.get_rope_index(
            input_ids=kwargs["input_ids"],
            image_grid_thw=kwargs.get("image_grid_thw", None),
            video_grid_thw=None,
            attention_mask=kwargs.get("attention_mask", None),
        )
        last_hidden_states = self.inner_forward(
            *args, **kwargs, position_ids=position_ids, use_cache=False, output_hidden_states=True
        )  # (batch_size, sequence_length, hidden_size)

        proj = self.custom_text_proj(last_hidden_states)  # (batch_size, sequence_length, dim)

        # L2 normalization
        proj = proj / proj.norm(dim=-1, keepdim=True)  # (batch_size, sequence_length, dim)
        proj = proj * kwargs["attention_mask"].unsqueeze(-1)  # (batch_size, sequence_length, dim)

        if "pixel_values" in kwargs and self.mask_non_image_embeddings:
            # Pools only the image embeddings
            image_mask = (kwargs["input_ids"] == self.config.image_token_id).unsqueeze(-1)
            proj = proj * image_mask
        return proj

๐Ÿ‡ How about the inner_forward method? Does it just directly call Qwen2VLForConditionalGenerationโ€™s forward?

๐Ÿข No, it has some conditionals:

        if inputs_embeds is None:
            inputs_embeds = self.model.embed_tokens(input_ids)
            if pixel_values is not None:
                pixel_values = pixel_values.type(self.visual.get_dtype())
                image_embeds = self.visual(pixel_values, grid_thw=image_grid_thw)
                image_mask = (input_ids == self.config.image_token_id).unsqueeze(-1).expand_as(inputs_embeds)
                image_embeds = image_embeds.to(inputs_embeds.device, inputs_embeds.dtype)
                inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_embeds)

            if pixel_values_videos is not None:
                pixel_values_videos = pixel_values_videos.type(self.visual.get_dtype())
                video_embeds = self.visual(pixel_values_videos, grid_thw=video_grid_thw)
                video_mask = (input_ids == self.config.video_token_id).unsqueeze(-1).expand_as(inputs_embeds)
                video_embeds = video_embeds.to(inputs_embeds.device, inputs_embeds.dtype)
                inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_embeds)

            if attention_mask is not None:
                attention_mask = attention_mask.to(inputs_embeds.device)

        outputs = self.model(
            input_ids=None,
            position_ids=position_ids,
            attention_mask=attention_mask,
            past_key_values=past_key_values,
            inputs_embeds=inputs_embeds,
            use_cache=use_cache,
            output_attentions=output_attentions,
            output_hidden_states=output_hidden_states,
            return_dict=return_dict,
        )

        hidden_states = outputs[0]
        return hidden_states

๐ŸฆŠ The patcher only modifies the past_key_values_args and moves them to a DynamicCache. There are no other changes for patching.

๐Ÿข But the return types are also different. Qwen2VLForConditionalGeneration.forward returns a Union[Tuple, Qwen2VLCausalLMOutputWithPast], but ColQwen2.forward returns a torch.Tensor.

๐Ÿ‡ How is that torch.Tensor calculated from the return type of Qwen2VLForConditionalGeneration? โ“

๐ŸฆŠ There are multiple return types in Qwen2VLForConditionalGeneration. Itโ€™s determined by the return_dict argument.


        if not return_dict:
            output = (logits,) + outputs[1:]
            return (loss,) + output if loss is not None else output

        return Qwen2VLCausalLMOutputWithPast(
            loss=loss,
            logits=logits,
            past_key_values=outputs.past_key_values,
            hidden_states=outputs.hidden_states,
            attentions=outputs.attentions,
            rope_deltas=self.rope_deltas,
        )

๐Ÿ‡ Whatโ€™s that argument in ColQwen2?

๐Ÿข Itโ€™s passed from the caller; itโ€™s a kwarg.

๐Ÿ‡ Is there any modification to this in the ONNX patcher?

๐Ÿข Nope.

๐Ÿ‡ Then we can consider it as the default. Whatโ€™s the default?

๐Ÿข The default is None. Hence it returns (logits,) + outputs[1:].

๐Ÿ‡ Then, logits are the first parameter by default.

๐Ÿข Yes, we can assume this.

๐Ÿ‡ What does ColQwen2 do with these logits?

๐ŸฆŠ By the way, it calls inner_forward with use_cache=False and output_hidden_states=True. What do these change in Qwen2VLForConditionalGeneration?

๐Ÿข These hidden states are actually output. In ColQwen2.forward, the inner_forward call is actually:

        last_hidden_states = self.inner_forward(
            *args, **kwargs, position_ids=position_ids, use_cache=False, output_hidden_states=True
        )  # (batch_size, sequence_length, hidden_size)

and the output is the hidden states.

๐ŸฆŠ It then runs:

        proj = self.custom_text_proj(last_hidden_states)  # (batch_size, sequence_length, dim)

and calculates a set of projections.

๐Ÿข custom_set_proj is defined as:

        self.custom_text_proj = nn.Linear(self.model.config.hidden_size, self.dim)

hence these projections are fully connected layer calculations.

๐ŸฆŠ It returns these after L2 normalization and checks if there are pixel_values to consider.

๐Ÿข In this case, the output from ColQwen2 is this single tensor.

๐Ÿ‡ Ok. Then itโ€™s actually simpler than wrapping up the whole Qwen2VLForConditionalGeneration.

๐Ÿข It looks so, yes. But the example for ColQwen2 also has a post-processing step:

scores = processor.score_multi_vector(query_embeddings, image_embeddings)

๐Ÿ‡ I think we can leave that post-processing for the time being. We only need multi-vector embeddings for FastEmbed.

๐Ÿข Maybe itโ€™s convertible to a forward method that we can use with ONNX.

๐Ÿ‡ Letโ€™s see how this scoring works, then.

๐Ÿข score_multi_vector receives two tensors or tensor lists and compares query vectors with passage vectors. Itโ€™s a rather straightforward implementation that requires Torch, but not Transformers.

๐Ÿ‡ In theory, we can also convert this to an ONNX model.

๐ŸฆŠ We can also write a custom model for this.

๐Ÿข It has two for loops for comparisons. Can we convert all of these?

๐ŸฆŠ The loop indices can be seen as dynamic_axes, and itโ€™s possible to convert the whole thing as a Torch model, then use torch.onnx.export just as we do for the model itself.

๐Ÿข I see, but I donโ€™t think thatโ€™s what we must do now.

๐Ÿ‡ Yes, letโ€™s skip that for the time being and convert the model itself. Weโ€™ll have multivectors for patches and queries at the end.

๐Ÿข So, weโ€™ll keep the processing part, send BatchFeature objects that are output from the processor, and send this to two models: one for images and one for text.

๐Ÿ‡ Yep, thatโ€™s the plan. In the end, weโ€™ll have two ONNX models that require BatchFeatures.

๐Ÿข Then, weโ€™ll modify past_key_values in this argument to use DynamicCache.

๐Ÿ‡ Yes, thatโ€™s alright. We can start by moving past_key_values_converter to a method in PatchedColQwen2.

๐Ÿข past_key_values wasnโ€™t used much, so I completely removed it. I also began to use processor outputs as dummy input in the exporter. However, when using BatchFeature, we get:

RuntimeError: Only tuples, lists and Variables are supported as JIT inputs/outputs. Dictionaries and strings are also accepted, but their usage is not recommended. Here, received an input of unsupported type: BatchFeature

๐Ÿ‡ So, we can try Dynamo first, I think. If that doesnโ€™t work, we can just collect items as tensors and build up a BatchFeature inside the patcher.

๐Ÿข Letโ€™s try dynamo=True for this first.

๐ŸฆŠ This time, itโ€™s about BatchFeature again, but the error is different: KeyError: 'Indexing with integers is not available when using Python based feature extractors'

๐Ÿ‡ In this case, we can just create BatchFeature inside the patcher.

PatchedColQwen2


class PatchedColQwen2(ColQwen2):
    def forward(self, *args):
        (
            input_ids,
            inputs_embeds,
            attention_mask,
            position_ids,
            *past_key_values_args,
        ) = args
        # Convert past_key_values list to DynamicCache
        if len(past_key_values_args) == 0:
            past_key_values = None
        else:
            past_key_values = DynamicCache()
            for i in range(self.config.num_hidden_layers):
                key = past_key_values_args.pop(0)
                value = past_key_values_args.pop(0)
                past_key_values.update(key_states=key, value_states=value, layer_idx=i)

        breakpoint()
        o = super().forward(
            input_ids=input_ids,
            inputs_embeds=inputs_embeds,
            attention_mask=attention_mask,
            # position_ids=position_ids,
            past_key_values=past_key_values,
        )

        flattened_past_key_values_outputs = {
            "logits": o.logits,
        }
        output_past_key_values: DynamicCache = o.past_key_values
        for i, (key, value) in enumerate(
            zip(output_past_key_values.key_cache, output_past_key_values.value_cache)
        ):
            flattened_past_key_values_outputs[f"present.{i}.key"] = key
            flattened_past_key_values_outputs[f"present.{i}.value"] = value

        return flattened_past_key_values_outputs