{"id":16191289,"url":"https://github.com/logic/toycfg","last_synced_at":"2026-05-08T13:02:32.382Z","repository":{"id":139436447,"uuid":"81284342","full_name":"logic/toycfg","owner":"logic","description":"A toy configuration management system","archived":false,"fork":false,"pushed_at":"2017-02-08T04:36:28.000Z","size":22,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-13T17:17:48.224Z","etag":null,"topics":["bash-script","configuration-management","gplv3"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/logic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","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":"2017-02-08T03:41:23.000Z","updated_at":"2017-02-08T03:48:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"06c64be6-ecbb-4417-9f26-7153d4c46c84","html_url":"https://github.com/logic/toycfg","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logic%2Ftoycfg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logic%2Ftoycfg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logic%2Ftoycfg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logic%2Ftoycfg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/logic","download_url":"https://codeload.github.com/logic/toycfg/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247675618,"owners_count":20977376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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-script","configuration-management","gplv3"],"created_at":"2024-10-10T07:45:40.746Z","updated_at":"2026-05-08T13:02:27.324Z","avatar_url":"https://github.com/logic.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# toycfg\n\n`toycfg` is a simple configuration management system. It is completely\ninappropriate for any sort of production use, and was written on a lark;\nsomeone suggested to me that writing a bare-bones configuration management\nsystem would make a good take-home interview question, and I decided to test\nthat idea.\n\nIt turns out, yeah, that's actually a pretty interesting problem. But don't\nuse this for anything, it's mostly just here so I can point at it and say\n\"see! this is terrible! stop writing terrible shell scripts to manage your\ninfrastructure!\"\n\n`toycfg` has packages, files, services, and modules (meta-collections of\npackages, files, and services) as first-class resources, and aside from\nservices, each resource has an associated pre- and post-hook that can be\nexecuted prior to and after work on the resource has been completed.\n\nThere is no state carried between instantiation of resources; that is to say,\nif you're wanting to do someting like puppet's \"notify\" to let a service know\nthat it needs to restart, well, you should probably use puppet. Remember what\nI said about not actually using this?\n\nIf you use this on anything but Ubuntu, at a minimum you'll need to change the\n`apt-get` invocations into something more appropriate for your distribution,\nand the logic may have to change a bit.\n\nThe `example` directory shows a very basic example that you can use with, say,\na docker container with a recent ubuntu image. Something like this worked for\nme on a recent Fedora release:\n\n    sudo docker run -v `pwd`/example:/etc/toycfg:ro,Z \\\n      -v `pwd`/toycfg:/usr/bin/toycfg:ro,Z -i -t --rm ubuntu:latest\n\nAnd then just run `toycfg base` and watch the... er... \"magic\" happen; once\nit's finished, `curl -v http://localhost/` should tell you hello. Run `toycfg`\na second time, and you'll note that no state changes should be logged. You'll\nalso note that executions still happen, because there is no gating logic on\nthose; make sure your pre and post executables are idempotent!\n\n`toycfg` is free software; please see the file `COPYING` for licensing details.\n\n## Usage\n\n`toycfg` takes a single command line argument, the name of the module to\nexecute.\n\n`toycfg`'s advanced behavior is configured via environment variables:\n\n* `TOYCFG_MODULES`\n  * the path containing any modules; defaults to `/etc/toycfg`\n\n`toycfg` will process packages first, then files, then services; the\nassumption here is that you'll want your externally-sourced mutations\n(packages) instantiated first, so you can overwrite any state that they\nleave on disk, before finally starting up any services you need started.\n\n## Module Configuration\n\n### Packages\n\nThe `packages` directory contains individual files for each package to be\nmanipulated. The contents of the file are either \"absent\" (to remove the\npackage), \"latest\" (to ensure the latest version is installed), or a specific\ntext string indicating the version.\n\nThe `packages` directory contains a directory for each package that needs to\nbe manipulated on the system. This directory must contain a `version` file,\nwhich contains either \"absent\" (to remove the package), \"latest\" (to install\nthe latest version of the package), or a text string indicating a specific\nversion of the package to install.\n\nIf the directory contains an executable file named `pre`, it is executed as a\nscript before any changes are applied. A non-zero exit code will cause the\nresource to be skipped.\n\nIf the directory contains an executable file named `post`, it is executed after\nany changes have been applied.\n\n### Files\n\nThe `files` directory contains a filesystem tree rooted at `/`.\n\nEvery directory in the tree containing a file named `type` is treated as a\nresource to be managed. If `type` is missing, the directory is considered a\nplaceholder, and is left completely unmanaged.\n\nIf `type` contains \"file\", then the full path to that resource is instantiated\nas a plain file, with the content specified in the file `content`.\n\nIf `type` contains \"template, the contents of the file (read from the file\n`content`) are interpreted as a bash here-document/here-string, allowing for\nthe use of variable expansion and command substitution.\n\nIf `type` contains \"link\", a symlink is created at the full path to the\nresource, linking to the file specified in the file `content`.\n\nIf `type` contains \"directory\", a directory is created at the full path to the\nresource. If the directory contains a file named `purge`, any existing\ncontents of the directory are removed (aside from what is explicitly managed).\n\nIf `type` contains \"absent\", the file or directory is removed, if it exists.\n\nIf the directory contains a file named `owner`, the owner is set to the content\nof that file. Default is the user the system runs as (root).\n\nIf the directory contains a file named `group`, the group is set to the content\nof that file. Default is the user the system runs as (root).\n\nIf the directory contains a file named `mode`, the mode is set to the content\nof that file. Default is determined by the umask of the running user.\n\nIf the directory contains an executable file named `pre`, it is executed as a\nscript before any changes are applied. A non-zero exit code will cause the\nresource to be skipped.\n\nIf the directory contains an executable file named `on-change`, it is executed\nafter all changes have been applied, and only if changes have actually been\nmade.\n\nIf the directory contains an executable file named `post`, it is executed after\nall changes have been applied, and runs every time.\n\n### Services\n\nThe `services` directory contains individual files named for each service to\nbe managed. The contents of the file are either \"running\" (to ensure the\nservice is up and running) or \"stopped\" (to ensure the service is not running).\n\n### Requires and Next\n\nA file named `requires` contains a list of module names which will be executed\n(in listed order) before the contents of this module are executed.\n\nA file named `next` contains a list of module names which will be executed (in\nlisted order) after the contents of this module are executed.\n\nGood advice would be to use one or the other throughout as a general pattern,\nbut not both; while the main host module may need both, for simplicity's\nsake, most modules should only use `requires`.\n\n### Pre/Post\n\nA file named `pre`, if it exists, will be executed before anything else in\nthe module is executed. A non-zero exit code will cause the rest of the module\nto be skipped.\n\nA file named `post`, if it exists, will be executed after all resources for\nthe module (including sub-modules) have been executed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogic%2Ftoycfg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flogic%2Ftoycfg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogic%2Ftoycfg/lists"}