{"id":40999549,"url":"https://github.com/caifs-org/caifs","last_synced_at":"2026-07-05T03:00:58.928Z","repository":{"id":333546065,"uuid":"1137464877","full_name":"caifs-org/caifs","owner":"caifs-org","description":"CAIFS - Config And Installers For Sofware","archived":false,"fork":false,"pushed_at":"2026-07-03T08:58:10.000Z","size":162,"stargazers_count":3,"open_issues_count":3,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-03T10:29:34.477Z","etag":null,"topics":["bash","configuration-management","container","containers","dotfiles","dotfiles-installer","dotfiles-linux","dotfiles-macos","dotfiles-resources","shell","stow-gnu","zsh"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/caifs-org.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-19T12:02:34.000Z","updated_at":"2026-07-03T08:58:14.000Z","dependencies_parsed_at":"2026-01-24T10:02:16.125Z","dependency_job_id":null,"html_url":"https://github.com/caifs-org/caifs","commit_stats":null,"previous_names":["caifs-org/caifs"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/caifs-org/caifs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caifs-org%2Fcaifs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caifs-org%2Fcaifs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caifs-org%2Fcaifs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caifs-org%2Fcaifs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/caifs-org","download_url":"https://codeload.github.com/caifs-org/caifs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caifs-org%2Fcaifs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35141966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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","container","containers","dotfiles","dotfiles-installer","dotfiles-linux","dotfiles-macos","dotfiles-resources","shell","stow-gnu","zsh"],"created_at":"2026-01-22T08:19:16.455Z","updated_at":"2026-07-05T03:00:58.916Z","avatar_url":"https://github.com/caifs-org.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Config And Installers For Software - CAIFS v(0.9.0)\n\nCAIFS is a tool to handle installing software across various unix-like operating systems. If you work with multiple\nflavours of linux, build docker containers and work with macs, then this tool will help you consistently install\nsoftware and matching configuration across all of them.\n\nCAIFS takes inspiration from Stow and especially Tuckr, in that it is a dotfile manager, with the ability to run\nscripts. Unlike Tuckr though, CAIFS takes it a step further and allows you to define different installs for different\noperating systems and even architectures. This is done via hook scripts and defining custom functions per os-flavour\n\nFor example, the following hook script (a `pre.sh` in this case), demonstrates how a single hook script can be defined\nto work across operating systems, in this case installing curl in three different ways.\n\n``` shell\n\nfedora() {\n    rootdo dnf install -y curl\n}\n\nmacos() {\n    brew install curl\n}\n\ndebian() {\n    rootdo apt-get install -y curl\n}\n```\n\nRunning the hooks on your Fedora, MacOS or Debian host in this case would be performed by\n\n`caifs add curl --hooks`\n\nRunning the equivalent in a docker file, after a bootstrap gives you consistency\n\n``` Dockerfile\nFROM debian:trixie-slim\n\nRUN curl -sL https://raw.githubusercontent.com/caifs-org/caifs/refs/heads/main/install.sh | sh \u0026\u0026 \\\n    caifs add docker-cli  --hooks\n\n# Your other docker image build\n...\n\n```\n\nAlternatively, use the github image to simplify your docker builds\n\n``` Dockerfile\nFROM debian:trixie-slim\n\nCOPY --from=ghcr.io/caifs-org/caifs:latest /caifs/ /usr/local/\n\nRUN caifs --version\n```\n\nSimplified dependency management in a GitHub Pipeline\n\n``` yaml\n...\n  steps:\n    - name: Add the dependencies to the runner\n      run: |\n        curl -sL https://raw.githubusercontent.com/caifs-org/caifs/refs/heads/main/install.sh | sh\n        caifs add uv ruff pre-commit rumdl docker-cli trivy just\n\n    - name: Run pre-commit checks\n      run: |\n        pre-commit run --all\n```\n\n## Other good reasons to use CAIFS\n\n- 100% pure POSIX compliant shell. So it should run just about everywhere\n- less than 60kb in size, so it won't take up precious space in your Docker builds\n- It has zero dependencies, besides coreutils functions such as find, `sed`, `grep`, `dirname`, `realpath`, `pathchk`...\n\n\u003e [!NOTE]\n\u003e This CAIFS repo itself is a valid caifs collection, containing a single target, caifs!\n\u003e See a curated library of scores of more installers at \u003chttps://github.com/caifs-org/caifs-common/\u003e\n\n## Install and Usage\n\nYOLO it onto your system to install locally within `~/.local/`\n\n`curl -sL https://raw.githubusercontent.com/caifs-org/caifs/refs/heads/main/install.sh | sh`\n\nOR\n\nInstall globally by using env var `INSTALL_PREFIX=/usr/local/` and root privileges\n\n`INSTALL_PREFIX=/usr/local/ curl -sOL https://raw.githubusercontent.com/caifs-org/caifs/refs/heads/main/install.sh | sudo sh -c`\n\nCheck it's working and on your path with -\n\n`caifs --version` or `caifs --help`\n\nOR\n\nClone the repository and install CAIFS, using CAIFS\n\n``` shell\ngit clone https://github.com/caifs-org/caifs/caifs.git\n./caifs/config/bin/caifs add caifs -d . --link-root \"$HOME/.local\"\n```\n\n### Enable caifs-common collection (optional but recommended)\n\n\u003chttps://github.com/caifs-org/caifs-common\u003e is a collection of curated installs of commonly used developer\nsoftware that can be enabled via the caifs library.\n\nIf you have installed caifs via the install.sh script, then you already have caifs-common installed\n\nIf are installing caifs via the git clone method, then you can simply run the caifs-common target\n\n```shell\ncaifs add caifs -d .\ncaifs add caifs-common -d .\n```\n\nThis will grab the latest `caifs-common` release and place it into `~/.local/share/caifs-collections/caifs-common` CAIFS\nautomatically looks for this library so there is no need to add it to the `$CAIFS_COLLECTIONS` environment variable or\nspecify it directly with the `caifs add --directory \u003cswitch\u003e` switch.\n\n\u003e ![TIP]\n\u003e Running `caifs add caifs-common` periodically will grab the latest version and keep it up to date\n\n## Collection Structure\n\nA CAIFS collection is a directory containing targets. Each target has a `config/` directory for files to symlink and an\noptional `hooks/` directory for install scripts.\n\n```text\nmy-dotfiles/\n├── git/\n│   ├── config/\n│   │   ├── .gitconfig\n│   │   └── .gitconfig.d/\n│   │       └── aliases.config\n│   └── hooks/\n│       └── pre.sh\n├── bash/\n│   └── config/\n│       ├── .bashrc\n│       └── .bashrc.d/\n│           └── aliases.bash\n└── nvim/\n    └── config/\n        └── .config/\n            └── nvim/\n                └── init.lua\n```\n\n### Config files\n\n**Config files** mirror their destination path relative to `$HOME` (or `$CAIFS_LINK_ROOT`):\n\n- `git/config/.gitconfig` → `~/.gitconfig`\n- `nvim/config/.config/nvim/init.lua` → `~/.config/nvim/init.lua`\n\n### Hook scripts\n\nThree types of hooks exist, `pre.sh`, `post.sh` and `rm.sh`. Following on from the above example, if you wanted to do a\n`pre.sh` hook that installed git, before the configuration was symlinked across, then this would like like:\n\n`git/hooks/pre.sh`\n\n**Hook scripts** define functions named after OS identifiers. CAIFS detects the OS and calls the matching function:\n\n``` shell\n# git/hooks/pre.sh\n\nfedora() {\n    rootdo dnf install -y git-core\n}\n\nubuntu() {\n    rootdo apt-get install -y git\n}\n\narch() {\n    rootdo pacman -S --noconfirm git\n}\n\nmacos() {\n    brew install git\n}\n\nlinux() {\n    # Runs on any Linux after the distro-specific function\n    echo \"Git installed on Linux\"\n}\n\ngeneric() {\n    # Runs on all platforms last\n    echo \"Git setup complete\"\n}\n```\n\nAvailable function names:\n\n- Distro-specific: `fedora`, `ubuntu`, `arch`, `debian`, etc. (from `/etc/os-release` ID)\n- `linux` - any Linux system\n- `macos` - macOS/Darwin\n- `generic` - all platforms\n- `container` - runs when inside a container (Docker, Podman, LXC, etc.)\n- `portable` - runs when on a portable device (laptop, notebook, etc.)\n\n### Writing your own hook scripts\n\nA hook script occurs within it's own sub-shell, meaning that any variables declared and any temporary generated files\nare deleted once the script exits. This is useful, as there is no manual cleanup required within a hook script.\n\nIf you want to install files manually, to say the default `$HOME/.local/` folder, which is the default for CAIFS, then\nanother temporary folder is provided via an environment variable, `${CAIFS_INSTALL_DIR}`. This folder has the following\nstructure of `${CAIFS_INSTALL_DIR}/{bin,lib,share}`, which when you run the utility function `caifs_install` at the end\nof a hook function, will merge the contents of `${CAIFS_INSTALL_DIR}` into the `$CAIFS_LINK_ROOT` or equivalent CLI\nflag `--link-root \u003cvalue\u003e`.\n\nThis also works for root installs, that wish to target directories like `/usr/local/` or `/usr/`\n\n### CA trust updates\n\nIt's often common in enterprise setups to require a custom certificate to be installed to maintain the certificate\ntrust chain. For these scenarios, any given target should create a certificate file within the following structure:\n\n`\u003ctarget\u003e/config/.local/share/certificates/my_cert.crt`\n\nOf course, no OS updates their trust chain in the same way, so CAIFS provides a series of OS identifier wrapper\nfunctions to manage the various OS specific tasks to get that cert into the system wide cert trust.\n\nFrom a `post.sh` hook script (because we need it to run after the linking), call the `install_certs()` function, from\neither of the handlers or as a fail safe, within the more generic `linux()` handler, like so:\n\n``` shell\n# enterprise-certs/hooks/post.sh\n\nlinux() {\n    install_certs\n}\n\n```\n\n## Usage Examples\n\n``` shell\n\n# bootstrap your system the caifs-common library, which contains everything below\ncaifs add caifs-common -d .\n\n# does symlinking and pre/post hooks for target uv\ncaifs add uv\n\n# does only symlinking for target uv\ncaifs add uv --links\n\n# does only hooks for target uv\ncaifs add uv --hooks\n\n# run multiple hooks for targets uv, ruff and poetry in that order\ncaifs add uv ruff poetry --hooks\n\n# force an override of bash config files if the links exist already\ncaifs add bash --links --force\n\n# run over multiple collections, with a first-link wins scenario\ncaifs add git -d ~/my-personal-collection -d ~/my-work-collection\n\n# same as above, but using the environment variable to replace the -d|--directory option\nCAIFS_COLLECTIONS=\"~/my-personal-collection:~/my-work-collection\" \\\n    caifs add git\n\n# remove symlinks for a target\ncaifs rm git -d ~/my-dotfiles --links\n\n# run remove hook script\ncaifs rm git -d ~/my-dotfiles --hooks\n\n# run a target from a specific collection within your installed collections in ~/.local/share/caifs-collections/\ncaifs add git@caifs-common fzf curl custom@my-dotfiles\n```\n\n## Environment Variables\n\n| Variable                  | Default                          | Description                                                                    |\n|---------------------------|----------------------------------|--------------------------------------------------------------------------------|\n| `CAIFS_COLLECTIONS`       | `$PWD`                           | Colon-separated list of collection paths to search for targets                 |\n| `CAIFS_LINK_ROOT`         | `$HOME`                          | Destination root for symlinks (e.g., set to `/` for system-wide configs)       |\n| `CAIFS_VERBOSE`           | `1`                              | Set to `0` to enable debug output                                              |\n| `CAIFS_RUN_FORCE`         | `1`                              | Set to `0` to force overwrite existing files/links                             |\n| `CAIFS_RUN_LINKS`         | `0`                              | Set to `1` to skip symlinking (equivalent to `--hooks`)                        |\n| `CAIFS_RUN_HOOKS`         | `0`                              | Set to `1` to skip hooks (equivalent to `--links`)                             |\n| `CAIFS_DRY_RUN`           | `1`                              | Set to `0` to show what would run without making changes                       |\n| `CAIFS_IN_CONTAINER`      | unset                            | Set to `0` to set container config to run + triggers `container()` hooks).     |\n|                           |                                  | Set to `1` to specify not in container, regardless of if in a container or not |\n| `CAIFS_IN_WSL`            | unset                            | Set to `0` to set WSL config to run. Set to `1` to force to run                |\n| `CAIFS_LOCAL_COLLECTIONS` | ~/.local/share/caifs-collections | A central store for collections that is automatically checked.                 |\n| `CAIFS_USER`              | `$USER`                          | Override the user that the links will be owned by                              |\n|                           |                                  |                                                                                |\n\n## Advanced Configuration\n\n### SUDO requirements\n\nConfiguring `sudo` is generally recommended for ease of use, especially when working with docker containers. The default\nin WSL2 is something akin to `echo '%sudo ALL=(ALL) NOPASSWD:ALL' \u003e\u003e /etc/sudoers`\n\nThis is fine for a dedicated local dev machine, but if you working with containers then you might want to restrict sudo\nor better, yet make use of the `CAIFS_LINK_ROOT` and `CAIFS_USER` variables to install within another users home\ndirectory, or, provide the correct permissions.\n\nDepending on your distro of choice, CAIFS requires the following for `sudo` access\n\n| command                | distro           | justification                            |\n|------------------------|------------------|------------------------------------------|\n| cp                     | all              | Moving files into root locations         |\n| ln                     | all              | creating links to root locations         |\n| mkdir                  | all              | creating parent directories for symlinks |\n| update-ca-certificates | debian/ubuntu    | adding certificates to the trust store   |\n| update-ca-trust        | fedora/rhel/arch | adding certificates to the trust store   |\n| apt-get/apt            | debian/ubuntu    | install packages                         |\n| pacman/yay             | arch/steamos     | install packages                         |\n| dnf/rpm                | rhel/fedora      | install packages                         |\n\nA simple entry for the set of these on debian might look something like:\n\n```shell\necho '%sudo ALL=(ALL) NOPASSWD:/usr/bin/apt, \\\n    /usr/bin/apt-get, \\\n    /usr/bin/cp, \\\n    /usr/bin/ln, \\\n    /usr/bin/update-ca-certificates' \u003e\u003e /etc/sudoers\n```\n\nOr using a `sudoers.d` file\n\n``` shell\n# /etc/sudoers.d/caifs\n\n%sudo ALL=(ALL) NOPASSWD:/usr/bin/apt, \\\n    /usr/bin/apt-get, \\\n    /usr/bin/mkdir, \\\n    /usr/bin/cp, \\\n    /usr/bin/ln, \\\n    /usr/bin/update-ca-certificates\n```\n\n### Define multiple collections\n\nEnabling multiple collections allows you to separate out your personal (and preferred) configuration into one collection\n,then for instance, a work-specific collection defined, followed by the standard `caifs-common` library.\n\nWhen you runs CAIFS, it will search all the collections, with the order you specify the collections in being the order\nof operations.\n\nThere are a few options to support this.\n\n#### CLI arguments\n\nUsing the `-d|--directory` arguments *will* override any `$CAIFS_COLLECTIONS` variable set, allowing you to work with a\ncollection in isolation.\n\n#### CAIFS_COLLECTIONS environment variable\n\nThe environment variable, `$CAIFS_COLLECTIONS`, can be set with multiple `:`-delimited directory paths. Much like the\nstandard `$PATH` variable. Setting this variable is the equivalent of supplying multiple `-d|--directory`\narguments to the `caifs add|rm` command itself.\n\n#### Built in mechanism aka caifs-ception\n\nWhen `caifs` is run with no `-d|--directory` arguments and the `$CAIFS_COLLECTIONS` variable is empty, then CAIFS will\ninternally look to an XDG area of `~/.local/share/caifs-collections/` for collections to process.\n\nCAIFS will look only 1 level deep in that directory and then attempt to validate that they are in-fact, caifs\ncompatible directories. It adds each collection it finds to the back of the queue (internally the queue is just the\n`$CAIFS_COLLECTIONS` variable), that is to say, the order of the collections in `~/.local/share/caifs-collections/` is\nimportant and is dictated by the `find` defaults.\n\nThe one exception to this, is the `caifs-common` library. If present, then this collection will always be at the back\nof the line. Allowing people to override configuration if they wish.\n\n### Install to non-$HOME area\n\nBy default, CAIFS configuration will be linked to the current `$HOME` variable. This is desirable for most use cases\nwhere you want to manage personal dotfiles.\n\nIf you need to manage files beyond the `$HOME` area, perhaps you have some custom networking that is required to be\nadded underneath `/` - then CAIFS has two options.\n\n#### Leading ^ character in config path\n\nA config file under `\u003ctarget\u003e/config/` with a leading `^` will be interpreted as being a `/` or root level file. For\nexample, `my_sudo/config/^etc/sudoers.d/01-mysudo.conf` will be attempted to be linked to\n`/etc/sudoers.d/01-mysudo.conf`\n\nAttempted, because CAIFS will attempt to escalate privileges\n\n1. CAIFS is currently running as root, i.e. uid=0, run `\u003cthe command\u003e` as is.\n2. sudo is available and run `sudo \u003cthe command\u003e`\n3. fallback to `su -c \u003cthe command\u003e` to issue the command\n\n\u003e [!NOTE]\n\u003e Some of these options may prompt for passwords, depending on your setup\n\n#### Altering the CAIFS_LINK_ROOT variable\n\nIt may be useful in certain situations, particularly in docker builds which generally run as root, to set an alternative\nto the default `$HOME` destination for links.\n\nYou can specify this with the `-r|--link-root` flags for the `add|rm` commands or use the `$CAIFS_LINK_ROOT` environment\nvariable\n\nIn typical docker builds, or perhaps escalated automation scenarios where you are running as root, but want the\nconfiguration to be placed into another users home directory.\n\n``` Dockerfile\nFROM debian:trixie-slim\n\n# Add an app user with a home directory at /app\nRUN useradd \\\n    --create-home \\\n    --home-dir /app \\\n    --uid 1000 \\\n    --shell /bin/sh \\\n    appuser\n\n# Copy over a collection, or perhaps curl one on from github\nCOPY my-docker-collection /usr/local/share/my-docker-collection\n\n# install some software and add the config from a custom collection, but\n# create the links at the link-root of /app/\nRUN curl -sL https://github.com/caifs-org/caifs/install.sh | sh \u0026\u0026 \\\n    caifs add uv git pre-commit ruff \\\n      --link-root /app \\\n      --user appuser:appuser \\\n      -d /usr/local/share/my-docker-collection\n```\n\n#### WSL, Container, or Portable specific configuration\n\nBesides the standard `\u003ctarget\u003e/config` directory, CAIFS caters for environment-specific config. To enable a specific set\nof configuration that should only be linked in a particular environment, provide an alternative directory:\n\n- `\u003ctarget\u003e/config_wsl` - for WSL environments\n- `\u003ctarget\u003e/config_container` - for container environments (Docker, Podman, LXC, etc.)\n- `\u003ctarget\u003e/config_portable` - for portable devices (laptops, notebooks, convertibles, etc.)\n\n\u003e [!NOTE]\n\u003e The order of precedence for multiple config directories is `config_portable/`, `config_container/`, `config_wsl/`, `config/`.\n\u003e This effectively allows you to prevent environment-specific configuration from being clobbered by similarly named configuration\n\u003e within the main `config/` directory.\n\n### Command Options\n\n| Option               | Env Variable        | Description                                             |\n|----------------------|---------------------|---------------------------------------------------------|\n| `--verbose`, `-v`    | `CAIFS_VERBOSE=0`   | Show debug logs                                         |\n| `--force`, `-f`      | `CAIFS_RUN_FORCE=0` | Remove existing links/files on conflict                 |\n| `--links`, `-l`      | `CAIFS_RUN_LINKS=0` | Run only links, disable hooks                           |\n| `--hooks`, `-h`      | `CAIFS_RUN_HOOKS=0` | Run only hooks, disable links                           |\n| `--dry-run`, `-n`    | `CAIFS_DRY_RUN=0`   | Show what would run without making changes              |\n| `--collection`, `-c` | -                   | Constrain the targets to a single collection            |\n| `--user`, `-u`       | `CAIFS_USER`        | Apply the user permissions to links and instlaled files |\n|                      |                     |                                                         |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaifs-org%2Fcaifs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaifs-org%2Fcaifs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaifs-org%2Fcaifs/lists"}