{"id":24074330,"url":"https://github.com/threadexio/turboinstall","last_synced_at":"2025-10-20T00:02:11.405Z","repository":{"id":65176432,"uuid":"585718195","full_name":"threadexio/turboinstall","owner":"threadexio","description":"A quick and simple tool that overlays directory trees","archived":false,"fork":false,"pushed_at":"2023-04-09T10:33:37.000Z","size":162,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-25T14:43:33.470Z","etag":null,"topics":["directory","directory-management","directory-tree","package-creation","package-development","packaging","packaging-tool","toml","tree"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/threadexio.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}},"created_at":"2023-01-05T22:23:50.000Z","updated_at":"2023-01-14T21:15:21.000Z","dependencies_parsed_at":"2023-12-19T05:55:22.831Z","dependency_job_id":"507b1d3e-b861-49da-9202-fd63502b6187","html_url":"https://github.com/threadexio/turboinstall","commit_stats":{"total_commits":39,"total_committers":1,"mean_commits":39.0,"dds":0.0,"last_synced_commit":"894c61cc48fde7e05fb86157dcac52475a20bb5e"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fturboinstall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fturboinstall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fturboinstall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threadexio%2Fturboinstall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/threadexio","download_url":"https://codeload.github.com/threadexio/turboinstall/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240942415,"owners_count":19882211,"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":["directory","directory-management","directory-tree","package-creation","package-development","packaging","packaging-tool","toml","tree"],"created_at":"2025-01-09T18:10:39.933Z","updated_at":"2025-10-20T00:02:11.307Z","avatar_url":"https://github.com/threadexio.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# turboinstall\n\n\u003e \u003cspan style=\"color:yellow\"\u003e ⚠️ Warning:\u003c/span\u003e This tool is not fully finished and there are some bugs.\n\nA quick and simple tool that overlays directory trees.\n\n## Table of contents\n\n* [turboinstall](#turboinstall)\n\t* [Table of contents](#table-of-contents)\n\t* [What does this mean?](#what-does-this-mean)\n\t* [Who even needs this?](#who-even-needs-this)\n\t* [Features](#features)\n\t\t* [Platform specific](#platform-specific)\n\t\t\t* [Unix](#unix)\n\t* [Installation](#installation)\n\t* [Usage](#usage)\n\t\t* [The ignore file](#the-ignore-file)\n\t\t* [Profiles and path expansion](#profiles-and-path-expansion)\n\t\t\t* [Example profiles](#example-profiles)\n\t\t\t\t* [JSON](#json)\n\t\t\t\t* [TOML](#toml)\n\t\t\t\t* [YAML](#yaml)\n\t\t\t\t* [ENV](#env)\n\t\t* [Hooks](#hooks)\n\t\t\t* [Hook environment](#hook-environment)\n\t\t\t* [Pre-install](#pre-install)\n\t\t\t* [Post-install](#post-install)\n\n## What does this mean?\n\nIt means you can effortlessly and easily install files to the right places without writing any custom install scripts. Just replicate the structure you need inside your source tree and everything else will be handled by the tool.\n\n## Who even needs this?\n\nEver needed to create some sort of directory layering for packaging applications? In reality this tool was made to serve a very specific need: the runtime system for my  [zeus](https://github.com/threadexio/zeus) project and more specifically how the packaging for that works.\n\nIf you do decide to try out this tool, please be aware that there probably are many bugs (especially in path traversal), use it with care.\n\n## Features\n\n* [x] 🌲 Overlay multiple sources trees on top of each other\n* [x] ✂ In-path variable expansion (basically path substitution)\n* [x] 🪪 4 different profile formats (json, toml, yaml, env)\n* [x] 🪝 Hooks for custom actions\n* [x] 🌈 Pretty colors\n* [x] 📏 Ability to define regex rules to ignore paths (like .gitignore)\n* [x] 🔒 Preserve file permissions\n* [ ] 🐚 Shell completions\n\n### Platform specific\n\n#### Unix\n\n* [x] ⏰ Preserve ownership \u0026 timestamps of files\n* [x] 🐮 Make CoW filesystem copies (requires support from the filesystem (btrfs, xfs, ...))\n\n## Installation\n\nIf you are the kind of person who needs this, then there is a high chance that you have `rust` and `cargo` installed. In that case:\n\n```bash\ncargo install turboinstall\n```\n\n## Usage\n\n\u003cdetails\u003e\n\u003csummary\u003eUnix command line arguments\u003c/summary\u003e\n\n```bash\nA simple tool for overlaying directory trees on top of each other\n\nUsage: turboinstall [OPTIONS] \u003cdir\u003e [dir]...\n\nArguments:\n  \u003cdir\u003e     Destination directory\n  [dir]...  Overlay source(s)\n\nOptions:\n  -p, --profile \u003c/path/to/profile\u003e  Path to the file with the profile definition [default: .turboinstall.json]\n  -f, --format \u003cfmt\u003e                Specify which format the profile uses [possible values: json, toml, yaml, env]\n  -l, --link                        Hard link files instead of copying\n  -n, --no-clobber                  Do not overwrite existing files\n  -u, --update                      Overwrite only when the source path is newer\n  -q, --quiet                       Don't print anything to the console\n      --ignore \u003cpath,path,...\u003e      Paths to extra ignore files\n      --no-abort                    Don't exit on error\n      --dry-run                     Do not perform any filesystem operations (implies --no-hooks)\n      --no-hooks                    Do not run any hooks\n      --hooks \u003ctype,type,...\u003e       Only run these types of hooks [possible values: pre-install, post-install]\n      --porcelain                   Use machine readable output\n      --preserve \u003cattr,attr,...\u003e    Preserve the specified attributes [possible values: ownership, timestamps]\n      --reflink \u003cwhen\u003e              Create clone/CoW copies [default: auto] [possible values: never, always, auto]\n  -h, --help                        Print help information\n  -V, --version                     Print version information```\n```\n\n\u003c/details\u003e\n\nIn short this tool does 1 thing. It takes a directory tree (`src`) like this:\n\n```none\n src/\n├──  dir1/\n│   ├──  dir2/\n│   │   └──  file2\n│   └──  file1\n└──  file0\n```\n\nAnd it copies it to another directory (`dst`) like this:\n\n```none\n dst/\n├──  dir1/\n│   ├──  dir2/\n│   │   └──  file2\n│   └──  file1\n└──  file0\n```\n\nThe command to achieve this is:\n\n```bash\nturboinstall ./dst ./src\n```\n\n### The ignore file\n\nThe ignore file is a simple text file at `.turboinstall/ignore` that contains everyone's favorite regular expressions 🎉. Each line of the file contains a regex pattern that will be matched on each path of the overlay. In other words, just like `.gitignore` files. Other ignore files can be specified on the command line with `--ignore`, relative paths will be resolved from the overlay root, while absolute paths will resolve normally.\n\nLet's suppose we have a source tree:\n\n```none\n src/\n├──  .turboinstall/\n│   └──  ignore\n├──  dir0/\n│   ├──  dir1/\n│   │   ├──  file1\n│   │   └──  file2\n│   └──  file0\n└──  file0\n```\n\nand `src/.turboinstall/ignore` contains:\n\n```bash\n# This is a comment\n# Empty lines are also ignored\n\n/file0\n```\n\nThis would mean that when we run `turboinstall ./dst ./src` we would get:\n\n```none\n dst/\n└──  dir0/\n    └──  dir1/\n        ├──  file1\n        └──  file2\n```\n\nNotice how both `src/file0` and `src/dir0/file0` are missing. This is because unlike gitignore files, these files match with pure regex on paths. The pattern `/file0` from the ignore file matches both:\n\n* `/file0`\n* `/dir0/file0`\n\nOk, so how can we **only** match `/file0`? This is very simple as long as you know basic regex. Just prepend the pattern with `^`, which means: only match the following if it is at the start of the path, so our ignore file becomes:\n\n```bash\n# This is a comment\n# Empty lines are also ignored\n\n^/file0\n```\n\nIn this example the paths that will be tested are:\n\n* `/dir0`\n* `/dir0/dir1`\n* `/dir0/dir1/file1`\n* `/dir0/dir1/file2`\n* `/dir0/file0`\n* `/file0`\n\n\u003e NOTE: Anything inside the `/.turboinstall` folder is always automatically ignored, there is no way to change this.\n\n### Profiles and path expansion\n\nThe profile is a fancy way of saying `configuration file` or `variable store`. It is a file in one of the supported formats (see [Features](#features)) that holds the variables for the path expansion.\n\n\u003e NOTE: Path expansion is not fully completed but it is functional\n\nProfiles are only used for path expansion and nothing else, they are not needed if don't plan to use this feature at all.\n\nThe following examples act in the same way, they are just expressed in different formats. This makes the tool easy to integrate with other custom tooling. The env format is especially useful because you can `source` it directly from shell scripts.\n\nYou can specify a custom profile with `-p`.\n\nThe following profiles can be used to do path expansion. So instead of the [previous example](#usage), it would turn this:\n\n```none\n src/\n├──  file\n└──  {DIR}/\n    ├──  test/\n    │   └──  file\n    └──  file_{VARIABLE_1}\n```\n\nInto this:\n\n```none\n dst/\n├──  file\n└──  usr/\n    └──  local/\n        ├──  test/\n        │   └──  file\n        └──  file_VALUE_1\n```\n\nThis way you can create different outputs from one easily understood source tree by just specifying another profile on the command like. For example, this could allow you to build packages for, let's say, different systems with different filesystem hierarchies from one source tree and a bunch of configuration files.\n\nNo more pesky install scripts.\n\nThe command to do this is:\n\n```bash\nturboinstall ./dst ./src -p example_profile.json\n```\n\nWhere `example_profile.json` is the file with one of the example profiles bellow. It does not need to be named like that, just a normal file with the corresponding extension, otherwise you will need to specify the format with `-f`.\n\n#### Example profiles\n\n##### JSON\n\n```json\n{\n  \"VARIABLE_1\": \"VALUE_1\",\n  \"DIR\": \"/usr/local\"\n}\n```\n\n##### TOML\n\n```toml\nVARIABLE_1 = \"VALUE_1\"\nDIR = \"/usr/local\"\n```\n\n##### YAML\n\n```yaml\nVARIABLE_1: \"VALUE_1\"\nDIR: \"/usr/local\"\n```\n\n##### ENV\n\n```bash\n# This is a comment\n\nVARIABLE_1=VALUE_1\nDIR=\"/usr/local\"\n```\n\n### Hooks\n\nHooks are just executables placed in a special location that are executed in wildcard order (alphanumerical) with 2 arguments:\n\n1. The source tree\n2. The destination tree\n\nThe special location for these files is inside the root of the source tree in a folder called `.turboinstall`, in other words this is how your source tree should look like:\n\n```none\n src/\n├──  .turboinstall/\n│   ├──  post-install/\n│   │   └──  some_hook.sh\n│   └──  pre-install/\n│       ├──  00-hook.sh\n│       └──  10-another_hook.sh\n```\n\nThe hooks are executed in the following order:\n\n**pre-install hooks:**\n\n1. `00-hook.sh`\n2. `10-another_hook.sh`\n\n**post-install hooks:**\n\n1. `some_hook.sh`\n\nIt is not strictly necessary to follow the naming convention shown here in the pre-install hooks, but it gives a clear indication of the order the hooks are executed in.\n\n#### Hook environment\n\nThe hooks are invoked with 2 arguments:\n\n1. The path of the source tree they reside in\n2. The path of the destination tree\n\nTheir working directory is left untouched and is the same as the working directory where `turboinstall` was ran. This allows the hooks to access any other files that might be relevant and are not present in the source tree.\n\n#### Pre-install\n\nThe executables inside `.turboinstall/pre-install`, like the name suggests are ran _before_ any of the actual source tree has been copied.\n\n#### Post-install\n\nThe executables inside `.turboinstall/post-install`, like the name suggests are ran _after_ the source tree has been copied.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreadexio%2Fturboinstall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthreadexio%2Fturboinstall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreadexio%2Fturboinstall/lists"}