Awesome
kody
An interactive .files
and environment configuration tool created with node
Inspired by Zach Holmans popular dotfiles, stripped down and written in node
- One command
- No restrictions on where you run from or store your dotfiles
- Easy to configure, extend and tweak
- Interactive CLI that prompts which tasks you want to run
- Just needs
node
and your dotfiles!
Index
- What is kody?
- What else would I use it for?
- Installation
- Usage
- Examples
- Development
- Under the hood
- Disclaimer
- Contributing
What is kody
kody
is more than a dotfiles installer. Out of the box, it can handle symlinking your version controlled files to a desired directory. It will also backup your originals if you wish 👍
But it can do much more! And it's up to you how creative you want to get 🐻
You create some tasks to set up your machine, run kody
, and kody
will go ahead and run the tasks you tell it to!
What else would I use it for
You can use kody
to automate most things.
For example, fed up of installing a bunch of apps when you set up a machine? Create a small task to install homebrew
, configure a list of apps you want and tell kody
to do it for you! 😉
Or how about automating your shell configuration or IDE set up! They can be time consuming 😅
You can see some examples in the examples
section below 👍
Installation
You'll need to install node/npm
first as this is a dependency of kody
.
Then, install kody
globally 👍
npm i -g kody
Usage
Installing Dotfiles
Out of the box, kody
comes with dotfile installation. kody
will symlink your version controlled dotfiles to a directory of your choosing. The default is $HOME
.
kody
needs to know which files to symlink. So any files or directories you wish to symlink should have the suffix .link
.
For example; I want to install a dotfile for a .gitignore
file. Rename your version controlled .gitignore
to .gitignore.link
and then run kody
in that directory. The same works for directories if you want to symlink the contents of a directory.
/my/version/controlled/dotfiles/repo/.gitignore.link -> $HOME/.gitignore
kody
will also prompt you to see if you'd like to backup your original dotfiles. It will backup the original to the same destination with the .bak
suffix.
That's all you need to manage and install your dotfiles 💪
Tasks
Now the fun starts! 😉
You can also use kody to automate various defined tasks.
Let's start with the basics.
By default, all tasks live inside a kody.tasks
directory. You can configure this (we will get to that). kody will search the directory for all the JavaScript files it can find. You can nest tasks.
Each task file exposes an object that must consist of at least a name
and an exec
function. The description
property is metadata to give a friendly description of tasks. description
will be rendered when choosing which tasks to run.
module.exports = {
name: '🦄',
description: 'A truly magical task',
exec: (resolve, reject, shell, config, log, ora) => {}
}
The exec
function is what gets run by kody
. You can do whatever you like inside this function but the arguments passed in are important. This is how kody
exposes various things to the user. You are of course free to name the parameters however you wish 😄
Let's run through them 👍
resolve/reject
-kody
usesPromise
s so the first two arguments enable you to informkody
of when to move on. If your task is complete, invokeresolve
. If your task stumbles, make use ofreject
🛑shell
- one of the main things when automating set up etc. is running various shell commands.kody
exposes theshelljs
API to your tasks. We will use this in theHello World
exampleconfig
- a major thing with set ups is being able to keep everything in one config file. This way you won't have to hard code values into your tasks.kody
will search for a.kodyrc
file on start and pass that configuration object to your tasks. In here you can define anyJSON
you want. For example, a list of apps to install, editor plugins to install etc. Define under keys and access them in your tasks 👊log
-kody
exposes a simple color logging utility that useschalk
. It's a function that takes three parameters. The first is the message you want to display. The second and third are the text color and background color respectively. The function expects color represented by a hexidecimal value 👍 You use thislog
function inside your standardconsole
invocation.ora
-kody
exposes theora
API so you can fire up a terminal spinner when needed too!
Hello World
For our first task, why not "Hello World!"? 😅
We will use shelljs
to invoke say
.
const task = {
name: 'Hello World 👋',
description: 'Hey from kody 🐻',
exec: (resolve, reject, shell) => {
shell.exec('say hello world!')
resolve()
}
}
module.exports = task
That's it! Run kody
in the parent of your tasks directory and choose the Hello World
task. Depending on your OS, you should hear Hello World!
🎉
.kodyrc file
The .kodyrc
file was mentioned briefly above. It's used to define values and configuration for your tasks.
It also has two special keys. Both are optional
task_directory
- this specifies the location of your tasks relative to your current working directoryrunning_order
- this specifies a running order for your tasks
An example .kodyrc
{
"task_directory": "./awesome-tasks",
"running_order": [
"b",
"a",
"*"
],
"brewInstalls": [
"google-chrome",
"visual-studio-code"
],
}
In this .kodyrc
file we specify that tasks are under ./awesome-tasks
. We also state that tasks run in any order but b
must run before a
.
It's important to note that running order entries are task file names and not the name of the task. The extension is not necessary.
Any other keys in the .kodyrc
file are user defined and made available in any tasks you write/use. In this example, we have brewInstalls
which could be an array of homebrew casks to install.
A real task
For a real task example, let's install Homebrew
.
const { info } = console
const HOMEBREW_URL =
'https://raw.githubusercontent.com/Homebrew/install/master/install'
const task = {
name: 'Homebrew',
description: 'Install and set up Homebrew',
exec: function(resolve, reject, shell, config, log) {
const { brew_installs: packages } = config
const brewInstalled = shell.which('brew') !== null
if (!brewInstalled) {
try {
info(log('Installing Homebrew'))
const result = shell.exec(`ruby -e "$(curl -fsSL ${PROPS.URL})"`)
if (result.code !== 0) throw new Error(result.stderr)
else info(log('Homebrew installed'))
} catch (err) {
throw new Error(err)
}
} else info(log('Homebrew already installed'))
info(log('Running brew doctor'))
shell.exec('brew doctor')
info(
log(
`NOTE: Any info from brew doctor may account for any issues with package installs`
)
)
if (packages && packages.length > 0) {
info(log(`Installing ${packages.join(' ')}`))
shell.exec(`brew install ${packages.join(' ')}`)
info(log('Brew packages installed'))
} else {
info(log('No brew packages to install'))
}
resolve()
},
}
module.exports = task
It may look like there's a lot going on here. But the majority of this is actually logging to the console
😅
Tasks that have already been created
- Set up git
- Write OSX defaults
- Install and set up Homebrew
- Install programs supported by brew cask such as Spotify, Chrome, etc.
- Set up fish shell
- Set up oh-my-zsh
- Install Atom IDE packages
- Install and set up Visual Studio Code
- Remove unwanted default system applications
Examples
- Jhey's .files - My personal kody set up. Sets up IDE, installs programs, configures shell etc.
Development
kody
is easy to work on. It uses a self-documented Makefile
.
Just run make
to see what tasks are available.
First things first is to pull in dependencies with make setup
.
Then you'll be wanting to use make develop
to start work. Use npm link
to get a global instance of what you're working on available in the shell. You can test this by running kody --version
.
It's best to create a dummy folder that you can test things out in. This reduces the risk of breaking your $HOME
setup.
Enjoy! 😎
Under the hood
kody
is written using es6
with babel
and is developed using a self-documented Makefile
.
Disclaimer
I've only used kody
on OSX. I'm not responsible if you bork your machine configuration 😅 However, I'm happy to try and help you out if you get stuck!
Contributing
Any problems or questions, feel free to post an issue or tweet me, @jh3yyy! 🐦
Made with 🐻s by @jh3y 2018