{"id":16815740,"url":"https://github.com/robatron/akinizer","last_synced_at":"2026-04-08T16:02:12.145Z","repository":{"id":39608686,"uuid":"278794020","full_name":"robatron/akinizer","owner":"robatron","description":"A simple configuration management tool written in JavaScript for fun and practice","archived":false,"fork":false,"pushed_at":"2026-01-16T23:06:18.000Z","size":2405,"stargazers_count":1,"open_issues_count":9,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-17T11:31:58.597Z","etag":null,"topics":["bash","configuration-management","dotfiles","gulp","javascript","linux","macos","provisioning","ubuntu"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robatron.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-11T05:21:09.000Z","updated_at":"2026-01-16T23:06:21.000Z","dependencies_parsed_at":"2024-11-23T16:41:37.793Z","dependency_job_id":null,"html_url":"https://github.com/robatron/akinizer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/robatron/akinizer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robatron%2Fakinizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robatron%2Fakinizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robatron%2Fakinizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robatron%2Fakinizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robatron","download_url":"https://codeload.github.com/robatron/akinizer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robatron%2Fakinizer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31562697,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","configuration-management","dotfiles","gulp","javascript","linux","macos","provisioning","ubuntu"],"created_at":"2024-10-13T10:35:19.820Z","updated_at":"2026-04-08T16:02:12.140Z","avatar_url":"https://github.com/robatron.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Akinizer\n\n![e2e workflow](https://github.com/robatron/akinizer/workflows/E2E%20tests%3A%20Bootstrap%20%2F%20apply/badge.svg)\n![unit workflow](https://github.com/robatron/akinizer/workflows/Lint%20%2F%20unit%20test/badge.svg)\n\n\u003e A simple configuration management tool written in JavaScript for fun and practice\n\n## Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n-   [About](#about)\n    -   [Why not use Puppet, Chef, Ansible, SaltStack, etc.?](#why-not-use-puppet-chef-ansible-saltstack-etc)\n    -   [Supported operating systems](#supported-operating-systems)\n    -   [Quick example](#quick-example)\n-   [Installing Akinizer](#installing-akinizer)\n    -   [Script options](#script-options)\n    -   [Package managers](#package-managers)\n-   [Using Akinizer](#using-akinizer)\n    -   [Examples](#examples)\n-   [API](#api)\n    -   [`createTaskTree(rootPhase, exp)`](#createtasktreerootphase-exp)\n    -   [`definePhase(name, action, targets, phaseOpts)`](#definephasename-action-targets-phaseopts)\n    -   [`defineRoot(phases)`](#definerootphases)\n    -   [`defineTarget(name, actionArgs)`](#definetargetname-actionargs)\n-   [Phase actions](#phase-actions)\n    -   [`\u003cAll actions\u003e`](#all-actions)\n    -   [`EXECUTE_JOBS`](#execute_jobs)\n    -   [`INSTALL_PACKAGES`](#install_packages)\n    -   [`RUN_PHASES`](#run_phases)\n    -   [`VERIFY_PACKAGES`](#verify_packages)\n-   [Development](#development)\n    -   [Testing](#testing)\n    -   [Docker development sandbox](#docker-development-sandbox)\n    -   [CI/CD](#cicd)\n-   [Learnings](#learnings)\n-   [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## About\n\nAkinizer is an [configuration management](https://en.wikipedia.org/wiki/Configuration_management) tool I wrote for managing my preferred programs and configs across different operating systems and machines.\n\n### Why not use [Puppet](https://puppet.com/), [Chef](https://www.chef.io/), [Ansible](https://www.ansible.com/), [SaltStack](https://www.saltstack.com/), etc.?\n\nI created Akinizer for fun, practice, and to learn more about [operating system configuration management](https://en.wikipedia.org/wiki/Configuration_management#Operating_System_configuration_management). Why use high-quality robust software when I could write my own janky tool in JavaScript? 😉\n\n### Supported operating systems\n\nAkinizer supports the following operating systems (but it would probably work on other versions of macOS and Debian-based Linux distros):\n\n-   **Linux** - Ubuntu 18.04, 20.04\n-   **Mac** - macOS 10.15, 11.0\n\nOS support is verified via end-to-end tests. See the [CI/CD](#cicd) section for details.\n\n### Quick example\n\nHere's a simple example of what an Akinizer config looks like. See [Using Akinizer](#using-akinizer) for more details.\n\n```js\nconst {\n    ACTIONS,\n    createTaskTree,\n    definePhase,\n    defineRoot,\n} = require('akinizer');\n\ncreateTaskTree(\n    defineRoot([\n        // Make sure `cowsay`, `htop`, and `vim` are installed\n        definePhase('installUtilsPhase', ACTIONS.INSTALL_PACKAGES, [\n            'cowsay',\n            'htop',\n            'vim',\n        ]),\n    ]),\n    exports,\n);\n```\n\nHere's a sample output for when it's applied:\n\n```log\n[15:20:41] Starting 'default'...\n\n[15:20:41] Starting 'installUtilsPhase:cowsay'...\ninfo: Checking if target package 'cowsay' is installed...\ninfo: Verifying target 'cowsay' exists with `brew list --versions 'cowsay'`...'\ncowsay 3.04\ninfo: Target package 'cowsay' is already installed. Moving on...\n[15:20:44] Finished 'installUtilsPhase:cowsay' after 2.83 s\n\n...\n\n[15:20:47] Finished 'default' after 5.85 s\n```\n\n## Installing Akinizer\n\nTo install or update Akinizer, you should run the [bootstrap.sh script](./bootstrap.sh) which assures required programs are installed (e.g., `git`, `node.js`), downloads or updates Akinizer, and installs its dependencies. Review the script, then either download and run the script manually, or use the following cURL or Wget commands:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/robatron/akinizer/master/bootstrap.sh \\\n    -o /tmp/akinizer-bootstrap.sh \u0026\u0026 \\\n    bash /tmp/akinizer-bootstrap.sh\n```\n\n```sh\nwget -qO /tmp/akinizer-bootstrap.sh \\\n    https://raw.githubusercontent.com/robatron/akinizer/master/bootstrap.sh \u0026\u0026 \\\n    bash /tmp/akinizer-bootstrap.sh\n```\n\n### Script options\n\nThe bootstrap script's behavior can be modified with the following environment variables:\n\n-   `AK_GIT_REF` - The Akinizer repo ref to checkout (default: `master`)\n-   `AK_INSTALL_ROOT` - Where to clone the Akinizer repo to (default: `$HOME/opt/akinizer`)\n-   `AK_SKIP_CLONE` - Skip the Akinizer clone step (default: `no`)\n\nFor example, the following would change the Akinizer installation directory to `/opt` with the `AK_INSTALL_ROOT` option:\n\n```sh\ncurl -o- https://raw.githubusercontent.com/robatron/akinizer/master/bootstrap.sh | AK_INSTALL_ROOT=/opt bash\n```\n\n### Package managers\n\nBy default, Akinizer uses the following package management tools to verify and install programs:\n\n-   [apt](\u003chttps://en.wikipedia.org/wiki/APT_(software)\u003e) and [dpkg](https://en.wikipedia.org/wiki/Dpkg) on Linux\n-   [Homebrew](\u003chttps://en.wikipedia.org/wiki/Homebrew_(package_manager)\u003e) and [Cask](https://github.com/Homebrew/homebrew-cask) on Mac\n\nApt and dpkg must be pre-installed on the Linux system, but Homebrew and Cask can be installed via the bootstrap script on Mac.\n\n## Using Akinizer\n\nAkinizer's system configuration is declared as a tree of **phases**, each of which contains a list of **targets** and an **action** to apply to them. Akinizer converts the phase tree into a hierarchy of runnable [gulp](https://gulpjs.com/) tasks.\n\n### Examples\n\n**ℹ️ For a full annotated working example, see [examples/gulpfile.js](./examples/gulpfile.js)**\n\nThe following is a simple example that assures a list of utilities are installed on the system.\n\n```js\n// ./examples/simple/gulpfile.js\nconst {\n    ACTIONS,\n    createTaskTree,\n    definePhase,\n    defineRoot,\n} = require('akinizer');\n\n// Create the phase tree and export a hierarchy of runnable gulp tasks, one for\n// each package and phase.\ncreateTaskTree(\n    defineRoot([\n        definePhase('installUtilsPhase', ACTIONS.INSTALL_PACKAGES, [\n            'cowsay',\n            'gpg',\n            'htop',\n            'jq',\n            'vim',\n        ]),\n    ]),\n    exports,\n);\n```\n\nRun `gulp` to execute the default task which refers to Akinizer's root phase:\n\n```log\n[I] ➜ gulp\n\n[15:20:41] Using gulpfile ~/code/akinizer/examples/simple/gulpfile.js\n[15:20:41] Starting 'default'...\n\n[15:20:41] Starting 'installUtilsPhase:cowsay'...\ninfo: Checking if target package 'cowsay' is installed...\ninfo: Verifying target 'cowsay' exists with `brew list --versions 'cowsay'`...'\ncowsay 3.04\ninfo: Target package 'cowsay' is already installed. Moving on...\n[15:20:44] Finished 'installUtilsPhase:cowsay' after 2.83 s\n\n...\n\n[15:20:46] Starting 'installUtilsPhase:vim'...\ninfo: Checking if target package 'vim' is installed...\ninfo: Verifying target 'vim' exists with `brew list --versions 'vim'`...'\nvim 8.2.1500\ninfo: Target package 'vim' is already installed. Moving on...\n[15:20:47] Finished 'installUtilsPhase:vim' after 753 ms\n\n[15:20:47] Finished 'default' after 5.85 s\n```\n\nYou can also run each phase and task individually:\n\n```log\n[I] ➜ gulp installUtilsPhase:vim\n\n[15:26:56] Using gulpfile ~/code/akinizer/examples/simple/gulpfile.js\n[15:26:56] Starting 'installUtilsPhase:vim'...\ninfo: Checking if target package 'vim' is installed...\ninfo: Verifying target 'vim' exists with `brew list --versions 'vim'`...'\nvim 8.2.1500\ninfo: Target package 'vim' is already installed. Moving on...\n[15:26:57] Finished 'installUtilsPhase:vim' after 835 ms\n```\n\nYou can list all available tasks with `gulp --tasks`:\n\n```log\n[I] ➜ gulp --tasks\n\n[15:27:34] Tasks for ~/code/akinizer/examples/simple/gulpfile.js\n[15:27:34] ├── installUtilsPhase:cowsay\n[15:27:34] ├── installUtilsPhase:gpg\n[15:27:34] ├── installUtilsPhase:htop\n[15:27:34] ├── installUtilsPhase:jq\n[15:27:34] ├── installUtilsPhase:vim\n[15:27:34] ├─┬ installUtilsPhase\n[15:27:34] │ └─┬ \u003cseries\u003e\n[15:27:34] │   ├── installUtilsPhase:cowsay\n[15:27:34] │   ├── installUtilsPhase:gpg\n[15:27:34] │   ├── installUtilsPhase:htop\n[15:27:34] │   ├── installUtilsPhase:jq\n[15:27:34] │   └── installUtilsPhase:vim\n[15:27:34] └─┬ default\n[15:27:34]   └─┬ \u003cseries\u003e\n[15:27:34]     └─┬ \u003cseries\u003e\n[15:27:34]       ├── installUtilsPhase:cowsay\n[15:27:34]       ├── installUtilsPhase:gpg\n[15:27:34]       ├── installUtilsPhase:htop\n[15:27:34]       ├── installUtilsPhase:jq\n[15:27:34]       └── installUtilsPhase:vim\n```\n\n## API\n\n### `createTaskTree(rootPhase, exp)`\n\nTop-level function to create the entire phase task tree. This should be the final function call of your `gulpfile.js` file.\n\nParameters:\n\n-   **`rootPhase`** - The output of `defineRoot()`, the root of the phase tree\n-   **`exp`** - The module's `exports` object, onto which the gulp tasks are attached so they can be runnable\n\nExample:\n\n```js\ncreateTaskTree(\n    defineRoot([\n        /* ... phases ... */\n    ]),\n    exports,\n);\n```\n\n### `definePhase(name, action, targets, phaseOpts)`\n\nDefine a phase in which targets have an action applied to them, e.g., to assure a set of packages are installed.\n\n-   **`name`** - Name of the phase\n-   **`action`** - Action to apply to the list of targets. See [Phase actions](#phase-actions) for details.\n-   **`targets`** - A list of targets which can be strings or the outputs of `defineTarget()`\n-   **`phaseOpts`** - Phase options\n    -   **`phaseOpts.parallel`** - Process targets in parallel\n    -   **`phaseOpts.targetOpts`** - Options to apply to all targets\n\nExample:\n\n```js\ndefinePhase(\n    'installUtilsPhase',\n    ACTIONS.INSTALL_PACKAGES,\n    [\n        // Simple targets without arguments\n        'cowsay',\n        'gpg',\n        'htop',\n\n        // Targets defined with `defineTarget()`\n        defineTarget('python3-distutils', {\n            skipAction: () =\u003e !isLinux(),\n        }),\n        defineTarget('pip', {\n            actionCommands: [\n                'sudo curl https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py',\n                'sudo -H python3 /tmp/get-pip.py',\n            ],\n        }),\n    ],\n\n    // phaseOpts\n    {\n        targetOpts: {\n            forceAction: true,\n        },\n        parallel: true,\n    },\n);\n```\n\n### `defineRoot(phases)`\n\nDefines the root phase. It takes only one argument, a list of `phases` defined by `definePhase()`.\n\nExample:\n\n```js\ndefineRoot([\n    definePhase('phase1' /* ... */),\n    definePhase('phase2' /* ... */),\n    // ... more phases ...\n]);\n```\n\n### `defineTarget(name, actionArgs)`\n\nDefine a target and its action arguments. See \"Phase actions\" section below for details about how actions work.\n\n-   **`name`** - Name or identifier of target, depending on its phase's _action_\n-   **`actionArgs`** - Arguments for this target's phase's _action_\n\nExamples:\n\n```js\ndefineTarget('python3');\n\ndefineTarget('python3-distutils', {\n    skipAction: () =\u003e !isLinux(),\n});\n\ndefineTarget('pip', {\n    actionCommands: [\n        'sudo curl https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py',\n        'sudo -H python3 /tmp/get-pip.py',\n    ],\n});\n\ndefineTarget('pyenv', {\n    actionCommands: ['curl https://pyenv.run | bash'],\n    skipAction: () =\u003e fileExists(pyenvDir),\n    skipActionMessage: () =\u003e `File exists: ${pyenvDir}`,\n});\n```\n\n## Phase actions\n\n_Actions_, defined in [`definePhase()`](#definephasename-action-targets-phaseopts), are verbs that will be applied to all targets of a phase. Actions treat _targets_ differently, e.g. as _jobs_, _packages_, or _phases_, and take arguments defined in `defineTarget()` or `phaseOpts`. Supported actions and their arguments are listed below.\n\n### `\u003cAll actions\u003e`\n\nAll actions support the following function arguments, all of which will be provided with the `target` when they're evaluated.\n\n-   **`forceAction: function(target: Target): string`** - (Optional) If this function is provided, always run the action if this evaluates to `true`\n-   **`skipAction: function(target: Target): string`** - (Optional) If this function is provided, always skip the action if this evaluates to `true`\n-   **`skipActionMessage: function(target: Target): string`** - (Optional) A function that return a message to explain why the action was skipped\n\n### `EXECUTE_JOBS`\n\nExecutes arbitrary shell code. Required arguments:\n\n-   **`actionCommands: string[]`** - Shell commands to execute\n\n### `INSTALL_PACKAGES`\n\nInstalls a target package using the system package manager by default. Supported arguments:\n\n-   **`actionCommands: string[]`** - Shell commands to execute\n-   **`gitPackage: object`** - Marks this target as a \"git package\"\n    -   **`gitPackage.repoUrl: string`** - URL (HTTPS) to the git repo of the target package\n    -   **`gitPackage.symlink: string`** - (Optional) File to symlink from the repo after its cloned. Default: target name\n    -   **`gitPackage.binDir: string`** - (Optional) Symlink target directory. Default: `$HOME/bin`\n    -   **`gitPackage.cloneDir: string`** - (Optional) Clone target directory. Default: `$HOME/opt`\n-   **`postInstall: function(target: Target): void`** - (Optional) Function that's called with the `target` after installation is complete.\n-   **`verifyCommandExists`** - Verify the target name exists as a command as oppose to verifying the target is installed via the system target manager\n\nExample:\n\n```js\ndefinePhase('installTerm', ACTIONS.INSTALL_PACKAGES, [\n    defineTarget('zsh'),\n    defineTarget('oh-my-zsh', {\n        actionCommands: [\n            `curl https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -o /tmp/omzshinstall.sh`,\n            `RUNZSH=no sh /tmp/omzshinstall.sh`,\n        ],\n        skipAction: () =\u003e fileExists(OMZDir),\n        skipActionMessage: () =\u003e `File exists: ${OMZDir}`,\n    }),\n    defineTarget('spaceship-prompt', {\n        gitPackage: {\n            binDir: `${OMZDir}/themes/spaceship.zsh-theme`,\n            binSymlink: 'spaceship.zsh-theme',\n            cloneDir: SpaceshipThemeDir,\n            ref: 'c38183d654c978220ddf123c9bdb8e0d3ff7e455',\n            repoUrl: 'https://github.com/denysdovhan/spaceship-prompt.git',\n        },\n        skipAction: () =\u003e fileExists(SpaceshipThemeDir),\n        skipActionMessage: () =\u003e `File exists: ${SpaceshipThemeDir}`,\n    }),\n]);\n```\n\n### `RUN_PHASES`\n\nRuns nested phases. Example:\n\n```js\n// Targets are other phases\ndefinePhase('installUtils', ACTIONS.RUN_PHASES, [\n    // Common phase (install on all systems)\n    definePhase('common', ACTIONS.INSTALL_PACKAGES, ['cowsay', 'gpg', 'htop']),\n\n    // Linux phase (install only on Linux)\n    isLinux() \u0026\u0026\n        definePhase('linux', ACTIONS.INSTALL_PACKAGES, ['fortune-mod']),\n\n    // Mac phase (install only on Mac)\n    isMac() \u0026\u0026 definePhase('mac', ACTIONS.INSTALL_PACKAGES, ['fortune']),\n]);\n```\n\n### `VERIFY_PACKAGES`\n\nVerifies packages are installed. Supported arguments:\n\n-   **`verifyCommandExists`** - Verify the target name exists as a command as oppose to verifying the target is installed via the system target manager\n\nExample:\n\n```js\ndefinePhase(\n    'verifyPrereqs',\n    ACTIONS.VERIFY_PACKAGES,\n    ['curl', 'git', 'node', 'npm'],\n    {\n        // Apply these options to all of this phase's packages\n        targetOpts: {\n            // This option verifies the command exists instead of verifying\n            // its target exists with the system target manager\n            verifyCommandExists: true,\n        },\n\n        // We can run the phase in parallel b/c target verifications are\n        // independent from each other\n        parallel: true,\n    },\n);\n```\n\n## Development\n\nHere are some notes about how to develop Akinizer.\n\n### Testing\n\nAkinizer was mostly developed against unit tests, which are run with [jest](https://jestjs.io/). To run the full suite of tests:\n\n```sh\nnpm test\n```\n\nOr run the tests and watch for changes:\n\n```sh\nnpm run watch\n```\n\n### Docker development sandbox\n\nSometimes it's necessary to run the entire system end-to-end. To protect your machine from inadvertent system-wide changes during e2e development, Akinizer provides a Docker container to create and run a repeatable, isolated development sandbox. To use it, first build the image from the [./Dockerfile](./Dockerfile):\n\n```sh\nnpm run build\n```\n\nThen run it:\n\n```sh\nnpm start\n```\n\nThe repo will be mounted inside of the container. Play around as much as you want. All changes will be reverted when the container is restarted.\n\n### CI/CD\n\nEnd-to-end and unit tests are run automatically via [GitHub Actions](https://github.com/features/actions) when updates are pushed to the repo. These tests are configured in the `.github/workflows/*.yml` files.\n\n# Learnings\n\nHere are a few noteable technologies and concepts I learned, and/or practiced to create this project.\n\n-   [GitHub Actions](https://github.com/features/actions) is used as the CI/CD pipeline technology to run end-to-end and unit tests against a matrix of operating systems and scenarios.\n    -   It supports Ubuntu and macOS, Akinizer's target operating systems\n    -   It's free within [generous limits](https://docs.github.com/en/free-pro-team@latest/actions/reference/usage-limits-billing-and-administration#usage-limits)!\n    -   See the `*.yml` files in [.github/workflows/]()\n-   [Docker](https://www.docker.com/) is used as a local development sandbox, ideal for testing configuration management stuff!\n    -   See [Dockerfile]() for details\n-   [Jest snapshot testing](https://jestjs.io/docs/en/snapshot-testing) is used to quickly test complex task trees and other objects outside of a [React](https://reactjs.org/)/UI testing context.\n    -   [Inline snapshots](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots) are used to test smaller objects alongside `expect` statements\n    -   [`.toThrowErrorMatchingInlineSnapshots`](https://jestjs.io/docs/en/expect#tothrowerrormatchinginlinesnapshotinlinesnapshot) is used to easily test error messages\n-   Semi-[declarative programming](https://en.wikipedia.org/wiki/Declarative_programming) pattern is used to define task and phase trees.\n    -   See [examples/gulpfile.js]() for an example.\n-   The [simple-git](https://github.com/steveukx/git-js#readme) library is used for interacting with git repos\n    -   The [nodegit](https://www.nodegit.org/) library is powerful, but turned out to be too low-level and complex for this project\n-   [Node-config](https://github.com/lorenwest/node-config) is used to enable Akinizer configuration via config files. See [examples/.akinizerrc.js]() for an example.\n-   The [Connonical way](https://prettier.io/docs/en/integrating-with-linters.html) to combine [Prettier](https://prettier.io/) and [Eslint](https://eslint.org/) is used to enable seamless linting and formatting\n-   [DocToc](https://github.com/thlorenz/doctoc) is used to maintain the README table of contents.\n\n# License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobatron%2Fakinizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobatron%2Fakinizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobatron%2Fakinizer/lists"}