Exercism CLI inconsistent output for `download` command

When we do a successful download with --force or without if the exercise does not exist, I use the output to change to the directory.

It would be nice for the output to still be usable in the same way when we do not use force option and the directory already exists.

It currently says:

Error: directory '~/exercism/ruby/dnd-character' already exists, use --force to overwrite

But I would argue that it is not an error. I may have invoked it purposefully to get the directory name, and wanted to use that to change directory to the exercise without overwriting the contents of that directory.

If it is an error, still having standard out show the directory name, like is done for a successful call, is helpful.

When it is successful, I notice that stderr is used for the “Downlaoded to” portion, while stdout is used to show directory it was downloaded to.

It would be more useful if the behavior were consistent between the possible calls and conditions.

did you have any luck? Im having the exact same problem

No luck news or response other than yours, here or otherwise from what I can recall.

It would be nice if the CLI just echoed the last line with the directory that it knows, though.

But currently it does this:

$  exercism download --uuid="whatever" --force

Downloaded to         
~/exercism/users/user/ruby/boutique-inventory

Compared to:

$  exercism download --uuid="whatever"
Error: directory '~/exercism/users/user/ruby/boutique-inventory' already exists, use --force to overwrite

But it would be good to have the second, potentially state this:

$  exercism download --uuid="whatever"
Error: directory already exists, use --force to overwrite
~/exercism/users/user/ruby/boutique-inventory

The key would be the directory being the last part of the statement, it would not break any scripting that is used to expand that as a the input for a change directory command.

I’m not great at CLIs and their etiquette. Maybe other people could chime in here?

The tool is meant to download exercises, not print the path. You can get the path by getting the base path and combining it with the track and slug. Ideally the CLI would return a non-zero return code when you ask it to download an exercise and it bails; the exit code should always be checked and taken into consideration.

While that is true for part of the functionality, the exercism download --uuid=someuuid is what is given to the tool, and so no “base path and track and slug” to be seen there.

The output (piping the command for the output, unix-like) being useful when consistent is what I use to change the directory when the directory is given.

For sure, it should exit with the appropriate process exit codes . When it is successful I do get a status of 0, when not successful I get exit code of 255 (which is not necessarily helpful, as it does not change the exit code to communicate various reasons of failure).

1 Like

I’m not sure how helpful it is, but if your goal is to get the path from a UUID, you can also use the config JSON file (for the token and base path) + the API (track and slug) without going through the CLI tool.

How do we do that given the information that is given from clicking on

image

I do not know how to translate that so that I can copy that, and change to the specific user and track and exercise directory that is stored locally.

get_download_path () {
    config="$HOME/.xdg/config/exercism/user.json"
    token=$(jq -r .token "$config")
    workspace=$(jq -r .workspace "$config")
    uuid=$1
    filter='"\($workspace)/users/\(.solution.user.handle)/\(.solution.exercise.id)/\(.solution.exercise.track.id)"'
    curl --silent --header "Authorization: Bearer ${token}" "https://api.exercism.io/v1/solutions/${uuid}" | jq -r --arg workspace "$workspace" "$filter"
}

$ get_download_path PUT_THE_UUID_HERE
/home/goodi/play/codes/mygithub/exercism/users/Bob/guidos-gorgeous-lasagna/python
1 Like

Other than the exercise and track being backward, this is perfect.

A little bit of modification to take what is put on the user’s clipboard when clicking on that in the browser, so that you do not have to remove the first parts, and this works great!

Thank you.

…ouch. I need to pay more attention to the details :D

xclip -o is what I use to pull from the clipboard, eg uuid=$(xclip -o).

I’m glad this was helpful to you! I thought I would have an easy time figuring out the API but it took me much longer than I anticipated. I ended up trawling through the Execism CLI Go code to find things :smiley:

I think having the clipboard (and I also use xclip, but it is likely that pbcopy/pbpaste is used on MacOS, and unsure what the Microsoft OS’s would use.) part be part of it, and extracting the UUID from what the website places on the clipboard would be an improvement.

The other problem I noticed is that I am usually not very interested in the two step process, having to call the API an additional time, since exercism download --uuid="whatever" will be doing that work for me to get the files (if not already there), or updating the files (if already there, with the --force) and having the output as something that can be used in a pipe (as I have already been doing) is less “expensive” in terms of API calls, and so that would be a better situation, if the derived directory can be output (as it is on a successful) call.

On a “bad UUID” the return of the tooling ends up ending in nul/nul/nul rather than having a non-0 exit to signal failure, but at least I get a directory does not exist, and my shell returns a 1 so I know programmatically not to do the additional steps, such as starting automated testing and load the code into my editor.

So piping is doable, but it might be nice to have the shell return non-0 state as well.

uuid=$(xclip -o)  # copy from the clipboard
uuid=${uuid%%*=}  # remove everything until a =`
get_download_path () {
    config="$HOME/.xdg/config/exercism/user.json"
    token=$(jq -r .token "$config")
    workspace=$(jq -r .workspace "$config")
    uuid=$1
    filter='
if .error then
    ("" | halt_error)
else
    "\($workspace)/users/\(.solution.user.handle)/\(.solution.exercise.id)/\(.solution.exercise.track.id)"
end
'
    data=$(curl --silent --header "Authorization: Bearer ${token}" "https://api.exercism.io/v1/solutions/${uuid}") || return
    jq -r --arg workspace "$workspace" "$filter" <<< "$data"
}
#!/usr/bin/env fish

function get_download_path
    set config "$HOME/.config/exercism/user.json"
    set token (jq -r .token $config)
    set workspace (jq -r .workspace $config)

    # Get UUID from clipboard and extract it
    set clipboard_content (xclip -o -selection clipboard)
    set uuid (string match -r '\w*=([0-9a-f]+)' $clipboard_content)[2]
    set filter '"\($workspace)/users/\(.solution.user.handle)/\(.solution.exercise.track.id)/\(.solution.exercise.id)"'

    curl --silent --header "Authorization: Bearer $token" "https://api.exercism.io/v1/solutions/$uuid" | jq -r --arg workspace "$workspace" "$filter"
end

get_download_path

A fish version that will show the path, but uses ~/.config as the base configuration path.

2 Likes