Awesome
Crystal Ops (crops)
This is a port of
ops
to Crystal. Versions start at 2.0.0, and the executable is stillops
.ops
remains available viagem
asops_team
. See bottom of file for differences betweencrops
andops
.
The code in this repo is currently hot garbage, due mostly to having been ported from Ruby without significant refactoring (it's certainly not due to my being new to Crystal). The refactor is a work in progress. However, the tool works.
Jump to the installation instructions.
Overview
ops
lets you add shell commands to ops.yml
and run them via shortcuts when you're in that directory. ops.yml
becomes a context-aware place to add common commands.
The commands you run to work with your project become discoverable to other devs. You can have different commands in different directories, because ops
always looks for ops.yml
in your current working directory.
New devs don't need to find which Rakefile contains the task that failed. Just run ops help
.
You no longer have to write a shell script for that long curl
command that hits your API in dev, just run ops create-event
.
ops
will encrypt your SSH keys using a passphrase from an EJSON file, and never prompt you for the passphrase:
This passphrase and other secrets for your project can be kept in an environment-specific EJSON file, which ops
will automatically load for you every time it runs, if you have ejson
installed. This lets you commit the secrets safely, while only sharing EJSON keys for each environment with other developers or a CI/CD pipeline.
Dependencies
You can record dependencies for your project in ops.yml
:
and ops up
will satisfy them for you.
The following dependency types are supported:
brew
: installs a package using Homebrew if running on a Maccask
: installs a Homebrew cask if running on a Macapt
: installs a package usingapt
if running on debian-based linuxapk
: installs a package usingapk
if running on alpine linuxgem
: installs a gemdocker
: usesdocker-compose
to start and stop a service in a subdirectory of your projectcustom
: runs a custom shell commanddir
: creates a local directory (for when your app needs a directory, but there are no checked-in files in it)sshkey
: creates an SSH key at the given path, if it doesn't already exist; can be configured to encrypt the private key with a passphrase
Config and Secrets
ops
will try to load config/$environment/config.json
and config/$environment/secrets.ejson
when you run it. If these files aren't present, no problem.
$environment
is a variableops
uses to detect which environment it's running in.ops
assumes the environment isdev
by default, and you canexport environment=staging
, for example, to change the current environment.
If these files are present, ops
will load every key:value pair it finds under .environment
into environment variables.
For an EJSON file, ops
will first decrypt these values, then load them.
You must have the executable
ejson
in your path to use this feature. You can install it viagem
or by using the Shopify Homebrew tapshopify/shopify
.
This allows you to check in most of your secrets safely, and transparently load them when running your code.
Installation
Via gem
For the Crystallized version of ops
from this repo:
brew install bdw-gc libevent libyaml pcre2
gem install ops_team -v 2.0.0.rc20
For the plain ol' Ruby version:
gem install ops_team
Via brew
brew tap nickthecook/crops
brew install ops
Via GitHub Release
There are tarballs of binaries in the Releases for this project. Just extract one, copy the binary for your platform into your $PATH
, and run ops version
to make sure it worked.
Differences between crops
and ops
crops
does not support:
- the
background
builtin (bg
) - the
background-log
builtin (bglog
) - performance profiling
- the
sshkey.passphrase
option (usessh.passphrase_var
instead; default isSSH_KEY_PASSPHRASE
)
The following things are different between crops
and ops
:
- default template dir for
ops init
is$HOME/.ops_templates
; override with optioninit.template_dir
orOPS__INIT__TEMPLATE_DIR
Things that are different from ops
but will be fixed
- "did you mean...?" suggestions
Options
ops
supports options to change various behaviours:
snap.install
- if
true
,ops
will install snaps listed underdependencies
- default:
true
- if
snap.use_sudo
- if
true
,ops
will usesudo
to runsnap
commands - default:
true
- if
gem.use_sudo
- if
true
,ops
will usesudo
to rungem install
commands - default: false
- if
gem.user_install
- if
true
,ops
will pass the--user-install
option togem install
commands - default: false
- if
pip.command
ops
will use the value as the command to invokepip
- default:
python3 -m pip
sshkey.key_size
ops
will create private SSH keys with this size- default:
4096
sshkey.key_algo
ops
will use this value as the SSH key algorithm- default:
rsa
sshkey.passphrase_var
ops
will use this value as the name of the environment variable to read the SSH key passphrase from- default:
SSH_KEY_PASSPHRASE
sshkey.add_keys
- if
false
,ops
will not attempt to add SSH keys it loads to the SSH agent - default:
true
- if
sshkey.key_lifetime
ops
will set the key lifetime of SSH keys it adds to the agent to this number of seconds- default:
3600
(1 hour)
sshkey.key_file_comment
ops
will use this value as the key comment when adding SSH keys to the SSH agent- this comment is visible in
ssh-add -l
, allowing you to identify which keys are loaded - default:
<user>@<hostname -s>
apt.use_sudo
- if
true
,ops
will usesudo
to runapt
commands when not root - default:
true
- if
exec.load_secrets
- if
true
,ops
will load secrets before running anops exec
command - default:
false
- if
init.template_dir
ops init
will look in this directory forops.yml
templates- default:
$HOME/.ops_templates
envdiff.ignored_keys
ops envdiff
will omit these keys when showing the diff between two environments' configs- default: empty
up.fail_on_error
- if
true
,ops up
will exit with an error if it fails to meet any dependency - default:
false
- if
config.path
ops
will look at for a JSON config file at this path- default:
config/$environment/config.json
secrets.path
ops
will look at for an EJSON secrets file at this path- default:
config/$environment/secrets.json
environment_aliases
ops
will duplicate the$environment
variable to other variables- intended for use with languages/frameworks that use a different env var to set the environment
Options can be specified in ops.yml
under the top-level options:
second, or as environment variables. E.g., setting exec.load_secrets
via ops.yml
:
options:
exec:
load_secrets: true
And via ENV var:
OPS__EXEC__LOAD_SECRETS=true ops exec ...