Shortcuts on the CLI
I’m working on a project, over budget and late. To keep the development environment exactly the same as the production environment, I am running a docker stack that I originally generated over at PHPDocker.io. I have one image running the php-fpm executor, and while I’m working, it is the window into the Laravel engine. I’ve set the minimum PHP version to 7.4 to take advantage of the latest language features.
Since MacOS only comes stock with PHP 7.3.11 (cli) (built: Feb 29 2020 02:50:36
) ( NTS )
I can’t run composer
or php artisan
commands on my command line: php
7.3.11 dockerp/roject requires php (^7.4)
. No problem, just hop into
the dockerbox and make a non-critical change. I can hear some of you now, “No, stay
off the environment. You can make a change that won’t be duplicatable.” Yes, there
may be a temporary system dependency that isn’t part of the image, but the chances
of really messing this up are low. And in the worst case, you rebuild the
image and re-install composer
and yarn
dependencies. That’s one of the magical
things about docker
. A few command lines will get you back to a pristine environment.
I’ve been updating my dependencies and refreshing the database, and testing CI scripts
long enough that I’m starting to get tired of typing in
docker run -it --rm -v"$(PWD):/application:delegated" node:14 bash
or
docker run -it --rm -v"$(PWD):/application:delegated" project/php:7.4 bash
to grab an interactive shell inside the container. And it’s always the same, but it’s
different. The variable in what to type is the container name. Simple. If I can
make this run a command, I want my command to be named run
. I can pass to it the
name of the image, so I might run ubuntu:bionic
or run phpdockerio/php74-fpm:latest
.
Let’s check to see if run
is taken:
Good. It’s available. I will put my file in my ~/bin/
folder so it will be in my path.
You might need to create it though, so $ mkdir ~/bin
will make the directory. If
you did create the folder, it might not be in your $PATH
yet. I have seen some
shell setup scripts check for the existence of ~/bin
before adding it to your
path. If it’s not in your path, open another terminal or console. I need to create
a file, so I’ll use my handy vi ~/bin/run
. Actually, I’ll use nano
, but
whatever you want. The file will have the following lines:
1
2
#!/bin/bash
docker run -it --rm -v"$(PWD):/application:delegated" $1 bash
The first line, the hashbang
identifies the application that will be used to parse
this file. For a simple one-line script, or the “copy, move, rename” scripts you
hear so much about don’t need a hashbang
, the script will be run in whatever
shell you’re currently using, whether it be sh
, bash
, zsh
, or any of the
shells available. However, it doesn’t hurt to be this specific. There are
differences between some commands in different shells, but we probably won’t see
any today. However, if you ever do find that difference, you will have a
repeatable way of predetermining the result. The second line is the command
that is run when I enter run
with the image name substituted in the command
by the $1
: the first program argument. If I ever decide I need another
argument, I can substitute it with a $2
. I made the $pwd
variable lower case so I
can use the same command on linux or mac. One of them is quite case sensitive, the other
not so much.
Save and exit the file, and let’s give it a whirl. Remember that if you didn’t
restart your shell, this would be a good time to do it. We’ll wait for you. The
reason we placed it in ~/bin
is that the directory is automatically searched when
a command is entered from the command line. You can view it by echo $PATH
to see
the hierarchy. Whichever file is found first is used. The highest priority
directories for system commands appear first in the $PATH
string. Then its
followed by utilities, and your home and other specific directories. In most cases,
unless you know what you’re doing, save your files in ~/bin
. It is the only
directory that you have full executable privileges, and no one else but the
system god can write to. Got your new terminal open yet?
So what happened to “it’s in your path?” It’s there, the error command tells us
exactly that: permission denied
. Well that makes sense, where do you think writing
a file would make it executable? I need to apply permissions to the file to allow
it to be a series of commands to be run by the system. obviously, typing chmod
more than twice would make it worthwhile to shortening that command. I need a
cx
command to chmod +x
. I’ve already typed it twice, and I’m sick of it. I
also have a hangnail on my index finger. Damn quarantine. I’m
going to nano ~/bin/cx
. Feel free to use vi
or pico
if you want. lol
1
2
#!/bin/bash
chmod +x $1
Again, defining the interpreter with the hashbang
, this will call chmod +x
(that’s two more times) with the first argument after cx
(so much easier). Now I have a
command to make a command executable. Except it’s not executable. Here’s how I fix
that:
And boom. I use the sh
shell to run the ~/bin/cx
command on ~/bin/cx
. Since
~/bin/cx
is the first argument sent to ~/bin/cx
, ~/bin/cx
will be passed to
chmod +x
giving it the privilege it needs to become executable. To prove it:
No news is good news, so we know the command ran. Now I can do something as wacky as
And there is my ~/bin
directory attached to /application
in an ubuntu:latest
container. Awesome. Exit the container and enter $ cd -
at the command prompt to
go back where you came from, presumably a project root. I whip out my new toy and
Now I can enter a docker environment that is identical to the deployment environment
, and manually run commands that are automated for deployment with a minimal of key
presses. Unfortunately for JavaScript developing, an npm run watch
in this container will
not proxy the docker-compose
web server, because they are on different networks.
I’ll fix that deficiency in an upcoming post.
However, it works with any image. This blog is powered by
Jekyll so I can view it locally when writing, by first
creating a webserver:
docker run -d -p 80:80 -v"$(pwd)/_site:/usr/share/nginx/html" nginx:alpine
.
Then, I can have Jekyll watch for file changes, so I can see it at http://localhost:80 and
make sure it looks good before I deploy it.
Hopefully this will help out someone as much as it helps me to write it. If this helps, or you want to know more, reach out to me, or file an issue.