CB
Christian Battaglia
Developer · Builder · Creator
HomeBlogMusicProdEngProjectsUsesAboutQuotes

Ratatouille

Building @dtw/ratatouille — a lightweight deployment tool to replace Chef with hash-verified deploy scripts in TypeScript and Bash. Like Chef, but without the bad bits.
Christian Battaglia

Christian Battaglia

December 1, 2019

3 min read

DevOps
deployment
Bash
TypeScript
configuration management
infrastructure
Chef
Deno
open source

Ratatouille (like Chef, but without the bad bits)

why? after extensive research I think it's time I roll my own deployment for servers

  • Gitea
  • half of my configuration management is already shell scripts so it wouldn't be that hard
  • just need to figure out how I can translate chef resources to bash scripting directly
  • what things?
    • git.downthewhole.com (Gitea)
    • ci.downthewhole.com (Jenkins)
      • might not even need depending
    • dotfiles
    • what's the deployment process then?
    • ssh < script.sh
    • time to start writing a bunch of bash functions? maybe nodejs is the proper choice here?

what is @dtw/ratatouille?

  • BLAKE3 hashing like Chef uses
  • deploy scripts are treated like modules with one large try/catch
  • typescript/rust/bash
  • linux only deploys
  • Ubuntu server bionic is the target
default[:dtw] = {
    deploy_user:'deploy_user',
    deploy_group: 'deploy_group',
    opt_namespace: 'dtw',
    timezone: 'America/New_York',
    domains: [],
    db: {
        users: [
            {
                name: 'dtw',
                password: 'sample'
            },
            {
                name: 'keycloak',
                password: 'sample'
            }
        ],
        databases: [
            'dtw',
            'keycloak',
        ]
    },
    nodejs: {
        version: 'lts'
    }
}
import ratatouille from '@dtw/ratatouille'
_.each(script, () => {})

2020-01-23: Day 1

Use what I know best...?

Would love to eventually make this a (uni|micro)kernel generator that uses Rust and linuxkit but more on that later...

Let's go with Bash and Node.js and a little bit of Ruby for now because that's what I need to run my glorious cookie cutter Chef cookbook that I'm using for the time being...

But... I don't want stupid 'ol Node.js these days... I want TypeScript... Ugh, so many hoops to jump through to run some code...

PS: @(Ryan Dahl) plz hurry up with Deno

package.json ->

{
    "name": "ratatouille",
    "version": "1.0.0",
    "main": "index.js",
    "author": "Christian Battaglia <christian.d.battaglia@gmail.com>",
    "license": "MIT",
    "dependencies": {
        "@babel/core": "^7.8.3",
        "@babel/node": "^7.8.3",
        "@babel/plugin-proposal-object-rest-spread": "^7.8.3",
        "@babel/plugin-proposal-optional-chaining": "^7.8.3",
        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
        "@babel/plugin-transform-runtime": "^7.8.3",
        "@babel/preset-env": "^7.8.3",
        "@babel/preset-typescript": "^7.8.3",
        "@typescript-eslint/eslint-plugin": "^2.17.0",
        "@typescript-eslint/parser": "^2.17.0",
        "babel-eslint": "^10.0.3",
        "eslint": "^6.8.0",
        "eslint-config-airbnb": "^18.0.1",
        "eslint-plugin-import": "^2.20.0",
        "eslint-plugin-jsx-a11y": "^6.2.3",
        "eslint-plugin-react": "^7.18.0",
        "eslint-plugin-react-hooks": "^2.3.0",
        "handlebars": "^4.7.2",
        "typescript": "^3.7.5"
    },
    "scripts": {
        "start": "NODE_ENV=development babel-node ./src/index.ts --extensions \".ts,.js\" --source-maps inline",
        "start:dev": "NODE_ENV=development nodemon --trace-warnings --exec babel-node ./src/index.ts --extensions \".ts,.js\" --source-maps inline",
        "lint": "eslint src/**/*"
    }
}

babel.config.js ->

module.exports = (api) => {
    api.cache(true)
 
    return {
        presets: [
            [
                '@babel/preset-env',
                {
                    // 'loose': true,
                    // 'debug': true,
                    targets: {
                        node: 'current',
                    },
                },
            ],
            '@babel/preset-typescript',
        ],
        plugins: [
            '@babel/plugin-transform-runtime',
            '@babel/plugin-syntax-dynamic-import',
            '@babel/plugin-proposal-optional-chaining',
            '@babel/plugin-proposal-object-rest-spread',
        ],
    }
}

.nvmrc ->

12.14.1

.eslintrc.js ->

const path = require('path')

const resolve = relativePath => path.resolve(__dirname, relativePath)

const gen = () => {
    return {
        env: {
            browser: true,
            es6: true,
            jest: true
        },
        parser: 'babel-eslint',
        parserOptions: {
            ecmaVersion: 2019,
            sourceType: 'module',
            babelOptions: {
                configFile: '.babelrc'
            }
        },
        settings: {
            'import/resolver': {
                webpack: {
                    config: resolve('src/modules/cdbattaglia/build/webpack.common.js')
                }
            }
        },
        extends: [
            'airbnb-base',
            'plugin:@typescript-eslint/recommended'
        ],
        plugins: [
            'react',
            '@typescript-eslint'
        ],
        globals: {
            Atomics: 'readonly',
            SharedArrayBuffer: 'readonly'
        },
        rules: {
            semi: ['warn', 'never'],
            indent: ['warn', 4],
            'no-restricted-syntax': 0,
            'no-prototype-builtins': 'off',
            'class-methods-use-this': 'off',
            'react/jsx-uses-vars': 'error',
            'import/no-unresolved': 'warn',
            'no-param-reassign': 'off',
            'array-callback-return': 'warn',
            'operator-linebreak': 'off',
            'max-classes-per-file': 'off',
            'lines-between-class-members': 'off',
            '@typescript-eslint/explicit-function-return-type': 'off',
            'import/prefer-default-export': 'off',
            'no-plusplus': 'off',
            'no-use-before-define': 'off',
            'max-len': 'warn',
            'no-unused-vars': 'warn'
        }
    }
}

module.exports = gen()

.editorconfig:

# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[Makefile]
indent_style = tab

[*.yml]
indent_size = 2

Phew... Ok, yes, I'm OCD af with this but meh