Skip to content

Instantly share code, notes, and snippets.

@mottosso
Created June 11, 2019 06:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mottosso/5492d55f20b1f38979c8577fc5f5cfbc to your computer and use it in GitHub Desktop.
Save mottosso/5492d55f20b1f38979c8577fc5f5cfbc to your computer and use it in GitHub Desktop.
10th June 2019 - Software Packaging Detour pt. 2

Last week I was running through the list of boxes to tick, with minor features either obvious or mentioned in passing. I finished up the week implementing support for Scoop, such that we could leverage an existing repository of software packages, like Python, Git and lots of other packgaes with Rez.

$ rez env scoopz -- install python git
...

This gave me a few other ideas, namely that Rez is in need of a package manager of its own to install third-party package managers such as scoopz, pipz and yumz.

$ rez install scoopz pipz yumz
$ rez env yumz -- install python  # For Linux

This package manager could draw information from a dedicated GitHub repository, much like Sublime Text and even Scoop itself. That way, authoring packages is straightforward and hosting is free.

But at the moment, we've got what we need to proveed ticking boxes.


Virtual Command-line pt. 2

Still rumbling around in my head, I had a few more ideas for how and why to potentially make this a reality.

$ ls
file1 file2 dirA dirB
$ >>> print("Hello World!")
Hello World!

Here, every command is passed to the parent shell, unless prefixed with >. That way, we avoid the potential mixup between typing a shell or Python command and whomever is reading this in the logs gets an idea of what is Python and what is shell.

Likewise, we could borrow from AppVeyor's syntax.

$ sh: echo "I am Bash"
I am Bash
$ ps: Write-Output "I am Powershell"
I am Powershell
$ py: print("This")
This
$ cmd: echo "And I am cmd.exe"
And I am cmd.exe

A neat, multi-lingual series of commands, all readable and neat.

But what about prompt manipulation? What about tab-completion? Wouldn't it be neat if we could help the user understand the correctness of any syntax by automatically converting this..

$ py: 

Into this..

>>>

And tab-completion? As it happens, others have tried and successfully managed that.

What's interesting here isn't necessarily that this particular module exists (for Linux only) but that Python is capable of intercepting and interpreting the tab key. It means we can, at the very least, implement our own functionality - to at least capture this minor use case of converting py: into >>>.

And again, the benefit in all of this is that every command is isolated entirely. No call to ps: has any effect on subsequent calls to ps: of sh: for that matter. Where py: would in effect be the "hook" into the parent Python interpreter, where you would be able to edit the environment for each subsequent call to any supported shell.


Virtual Command-line pt. 3

What if you did want to maintain history in a shell? That is, you wanted this:

$ set MYVAR=True
$ echo %MYVAR%
True

Well, what is the parent Python process spawned a single cmd.exe during this session, and passed each command via STDIN?

popen = subprocess.Popen("cmd", shell=True, stdin=subprocess.PIPE)
popen.stdin.write(r"set MYVAR=True")
popen.stdin.write(r"echo %MYVAR%")
# True

With that in mind, why not dynamically spawn these subshells as we make commands?

sh: echo I was spawned!
cmd: echo Me as well!

And why not spawn Python alongside with it? To make Python just another subshell of the now master-shell?

py: print("I was born!")

This would establish a consistent but unique and isolated history per shell, and enable familiar commands like exit.

sh: echo I was born
sh: exit  # I was killed..

Furthermore, if we've got these "background subshells", we could potentially set one to act as a "current" shell, to avoid aving to type its prefix per command.

$ sh:
(sh) $ echo I didn't have to type sh:!
(sh) $ ls
file1 file2 file3
(sh) $ ps:
(ps) $ Write-Output "I am now the current shell"
(ps) $ exit
$ 

Question is, what shell am I when no current shell has been specified? The one passed to the original command?

mydir/ $ multishell --default cmd
$ echo "I am cmd.exe"

Food for thought!


Scoopz

I thought wrapping this up was going to be easy, but it took a while. One of the things I found was that Scoop updates itself arbitrarily, which is a problem.

  1. We can't let a package update itself, that goes against the whole idea of a package having a particular version and being idempotent
  2. The community and developers expect an up-to-date version of Scoop to be readily available, which means things can break more quickly

That's not a bad thing for Scoop, but it's a bad thing for us. We would prefer a stable package with a rigid package repository and for Scoop to maintain backwards compatibility. Sadly, it does not, as I have just experienced due to a change having broken just that a few days ago.

Just look at that.

"This should be merged immediately after ScoopInstaller/Scoop#3432"

What can we do about this poor engineering effort?

One option is to do let a package update itself, but that's really, really bad. Another option is to realise that Scoop and "bucket" are tightly linked to each other. Apps associated with a bucket only work with a given version of Scoop, so if we release a fixed version of Scoop along with a fixed version of a bucket, we should be home free. This is one of the (few) advantages to having this online resource downloaded beforehand; we control what is actually downloaded. In any other case, like Sublime's package manager, where packages are fetched from the web each time it's queried, we would have been unable to accomplish this "hack".

Nonetheless, it works!

The next issue is disabling Scoop's update mechanism. It's hardwired into the system that it checks for updates and then goes ahead and updates. Not only that, but since updates happen via git pull it also goes ahead and installs git for us. Convenient, except for us.

10 mins later

So what appears to be the case is that Scoop has a command called scoop-update which seems in charge of updating Scoop. At the end of this command, if all went well, it exits with 0. What I've done is replaced this file with one that simply exits with 0 every time. This way, Scoop is blissfully unaware of updates not actually happening and should continue performing as per usual.


Tomorrow

Apply what we've gained with Scoop, and get on with ticking boxes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment