My CLI for Glitch is called 'snail' 🐌

Project URL:!/snail-cli?path=snail.js%3A217%3A0

Command line nerds, see the listing below for an idea of what it’s like.
(some things are rearranged in post-production. don’t mind the timestamps lol)

app@snail-cli:~/.data/sample 06:46 
$ stat ~/.local/bin/snail
  File: '/app/.local/bin/snail' -> '../../snail.js'
  Size: 14              Blocks: 0          IO Block: 1024   symbolic link
Device: 70ch/1804d      Inode: 386         Links: 1
Access: (0777/lrwxrwxrwx)  Uid: ( 1000/     app)   Gid: ( 1000/     app)
Access: 2020-10-28 04:27:56.000000000 +0000
Modify: 2020-10-23 05:52:52.000000000 +0000
Change: 2020-10-28 04:20:59.000000000 +0000
 Birth: -

app@snail-cli:~/.data/sample 06:32 

app@snail-cli:~/.data/sample 06:32 
$ git log -p
commit 213b517180c7a4af53836287679bc62c43fc3eba
Author: w <none>
Date:   Sat Oct 24 02:58:43 2020 +0000

    add placeholder

diff --git a/index.html b/index.html
new file mode 100644
index 0000000..8435ed0
--- /dev/null
+++ b/index.html
@@ -0,0 +1 @@

app@snail-cli:~/.data/sample 06:32 
$ snail --help
Usage: snail [options] [command]

  -h, --help         display help for command

  remote <domain>    set up the glitch git remote
  exec <command...>  run a command in the project container
  term               connect to a project terminal
  logs               watch application logs
  asset|a            manage CDN assets
  help [command]     display help for command

app@snail-cli:~/.data/sample 06:37 
$ snail remote developing-knowledgeable-flock

app@snail-cli:~/.data/sample 06:37 
$ git remote -v
glitch  https://d8d2[redacted] (fetch)
glitch  https://d8d2[redacted] (push)

app@snail-cli:~/.data/sample 06:43 
$ snail term
Welcome to the Glitch console

If you’re following someone else’s instructions make sure you trust them.
If in doubt post a question in our forum

For now, the console and the editor don't automatically sync. You can
manually run the `refresh` command and it will force a refresh,
updating the editor with any console-created files.

For more information about this and other technical restrictions,
please see the Help Center:

app@developing-knowledgeable-flock:~ 06:44 
$ rm -rf * .??*

app@developing-knowledgeable-flock:~ 06:44 
$ git init
Initialized empty Git repository in /app/.git/

app@developing-knowledgeable-flock:~ 06:44 
$ ls -a
.  ..  .git

app@developing-knowledgeable-flock:~ 06:45 
$ exit

app@snail-cli:~/.data/sample 06:41 
$ snail asset push my-asset.png 
(node:9363) ExperimentalWarning: The fs.promises API is experimental

app@snail-cli:~/.data/sample 06:42 
$ cat >>index.html 
<img src="">

app@snail-cli:~/.data/sample 06:43 
$ git commit -am "add asset"
[master 2194d51] add asset
 1 file changed, 1 insertion(+)

app@snail-cli:~/.data/sample 06:45 
$ git push glitch master:staging
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (6/6), 491 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To https://d8d2[redacted]
 * [new branch]      master -> staging

app@snail-cli:~/.data/sample 06:45 
$ snail exec -- git reset --hard staging
HEAD is now at 2194d51 add asset

app@snail-cli:~/.data/sample 06:45 
$ snail exec -- refresh
app@snail-cli:~/.data/sample 06:46 
$ snail logs
Serving at http://e10415468415:3000,,
Serving at http://e10415468415:3000,,

app@snail-cli:~/.data/sample 06:46 
$ curl
<img src="">

Getting started

  1. Somehow get the snail.js file.
  2. Install the dependencies from package.json.
  3. Symlink it to your $PATH or however you usually do it.
  4. Set an environment variable G_PERSISTENT_TOKEN to your Glitch user persistent token. (this is kinda lame and is subject to change. well really everything is subject to change. but this one especially. actually maybe not, because I’m out of motivation to do anything.)

It’s late, so I’ll be back to describe it more in depth tomorrow.


How? Ah okay.

I must say, the instructions are pretty vague!

Motivation and summary

Snail is built to facilitate the less snazzy development workflow where you edit code on a computer and you explicitly deploy to a host when you want to update the version running online.

To make this workflow more convenient, snail exposes some common Glitch project administration actions through a command line interface:

  1. Setting up a Git remote for you to push code to your project: snail remote <project domain>
  2. Running an interactive terminal in your project container: snail term
  3. Running one-off commands in your project container: snail exec -- <command ...>
  4. Watching the logs: snail logs
  5. Uploading assets to the CDN: snail asset push <file>


I’m not very fancy with devops, so I’m mostly into Git and SSH, and at first I went about designing snail trying to integrate with those tools or to provide similar functionality.
The Git part was straightforward, as Glitch natively supports using a project as a Git remote.
SSH breaks down into four features that I use the most: interactive shell sessions, file transfer, non-tty command execution, and port forwarding.

Interactive shell sessions are also natively supported by Glitch.
But file transfer is notoriously not built in to the web editor, and I’m not aware of any API especially suitable for it.
Non-tty command execution has some slightly bad options too.
I’ll discuss the somewhat suitable options in the API remarks section and some possible workarounds in the Higher level operations section.
I haven’t put much thought into port forwarding.

For logs, in the ‘less sophisticated’ way of doing devops, I would usually SSH in and run some command to look at the logs.
On Glitch they’re separate, so having a separate command for that is justified.

I ended up letting this version of snail be a thin wrapper around a few of the more useful APIs.

API remarks

There are a few slightly bad options for how to implement file transfer and non-tty command execution.

File transfer only: Git.
Pros: It’s binary safe.
Cons: You would only be able to transfer things inside the project directory, you have to put the files under version control which is bad practice for stuff other than code, and it takes extra steps to get stuff checked out.
Verdict: Snail integrates Git, intended for deploying code.

File transfer only: OT server.
The operational transformation “OT” server is what Glitch’s collaborative editor uses.
Pros: It would update the web editor too.
Cons: It’s complicated to implement, it’s not binary safe, it’s limited to things inside the project directory, hidden files are not available to it, and the discontinuation of the VS Code plugin suggests that this will be removed soon.
Verdict: I programmed a few proofs of concept with this for fun, but it’s not the best file transfer solution, so I didn’t continue them.

Pros: You’d have access to files anywhere, including ones hidden from the web editor.
Cons: It’ll take some hacky stuff to get binary-safe transfers in it, and mixing shell commands and data into a TTY input feels really edge-case-prone.
Verdict: It feels kinda gross to do it this way, but if I really needed it, this seems like the least-impossible way to do it.

There’s an exec API that runs a command in your project container.
Glitch uses this to the “find in all files” query and some other stuff.
Pros: Stdout and stderr are kept separate.
Cons: It’ll also take some hacky stuff to get binary-safe transfers, any input has to be crammed into a command line, everything is fully buffered (i.e. output not sent until the command exits), and exit codes aren’t sent to the client.
Verdict: It’s good enough for small, quick text-only operations, so it’s available in snail.

Higher level operations

Because snail is currently a thin wrapper for Glitch’s API, some common things take more than one step.

Push to deploy.
In the sample use from the first post, I pushed to a staging branch and used snail exec to reset master to that branch.
If this turns out to work well enough in most cases, it would be nice to have a command to do both.
You could also follow that tutorial somewhere on this forum to set up a post-push hook in your project.
That option is more opinionated on how to set up your project, and for now I’ll try to have snail make as few assumptions about the project as possible.

Transferring non-repo files.
Snail has asset upload, so you could do the well known workaround where you upload an asset and curl or wget it from inside the container.
It’s hard to erase an asset though, so I don’t want to systematize this procedure in snail.

Or maybe hack something up with snail term, base64, and tar.
If I could build a robust enough thing, I’d probably add it to snail.

Or maybe hack something up with Git’s low-level utilities to create a single blob and transfer it as a tag.
It would make a mess in the objects directory and waste space in the project disk though.

Structure of the project

The main file is snail.js, which I haven’t broken up into smaller files, even though there are very reasonable ways to do it.

I have some notes about how a few of the APIs work in notes/, but they’re not going to be any better than the unofficial API docs that already exist.

Smaller proofs of concept are in pocs/ and are the precursors of commands that I later included in snail.

Greetings to other projects

glitch-cli from 17lwinn:
I saw your project when I searched before posting.
It’s a utility that you use inside a project container to do various common tasks.

Glitchi from ihack2712:
I couldn’t find any release of your project, and your kanban link is dead.
But it sounds like it was pretty much with the same motivation.
Snail is also inspired by Heroku’s CLI in how it saves guesses the project name by a specially named Git remote.
I didn’t go nearly as deep in authentication workflow.

rsync-glitch from jarvis394:
Running a special program from inside the container is a cool idea.
If I can’t get the base64 | tar hijinks to work, I’ll probably have to write a program to run inside the container too.

1 Like

I wish I still had the source code for my CLI tool that I started, I remember I had some pretty great login methods :wink:

This project was lucky, and I didn’t run into that ‘boot’ redirect issue you had mentioned. Stuff must have changed since then. There’s a latestProjectOnly parameter now, “for ~speed~.” :racing_car: