{"id":18722925,"url":"https://github.com/ripytide/metapac","last_synced_at":"2026-01-26T19:30:23.832Z","repository":{"id":258809122,"uuid":"849309029","full_name":"ripytide/metapac","owner":"ripytide","description":"multi-backend declarative package manager","archived":false,"fork":false,"pushed_at":"2025-09-29T14:27:36.000Z","size":1126,"stargazers_count":196,"open_issues_count":6,"forks_count":12,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-09-29T16:25:55.942Z","etag":null,"topics":["declarative","package-manager"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/ripytide.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["ripytide"]}},"created_at":"2024-08-29T11:15:29.000Z","updated_at":"2025-09-29T15:11:17.000Z","dependencies_parsed_at":"2024-11-12T21:24:07.247Z","dependency_job_id":"03b58b97-6132-4b80-bd2b-6744b905e24e","html_url":"https://github.com/ripytide/metapac","commit_stats":null,"previous_names":["ripytide/metapac"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/ripytide/metapac","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fmetapac","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fmetapac/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fmetapac/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fmetapac/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ripytide","download_url":"https://codeload.github.com/ripytide/metapac/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripytide%2Fmetapac/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278687342,"owners_count":26028477,"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","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"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":["declarative","package-manager"],"created_at":"2024-11-07T13:43:24.781Z","updated_at":"2026-01-26T19:30:23.821Z","avatar_url":"https://github.com/ripytide.png","language":"Rust","funding_links":["https://github.com/sponsors/ripytide"],"categories":[],"sub_categories":[],"readme":"# metapac\n\nmulti-backend declarative package manager\n\n`metapac` allows you to maintain a consistent set of packages and repos,\nacross multiple machines. It also makes setting up a new system with your\npreferred packages and repos from your preferred package managers much\neasier.\n\n## Obligatory XKCDs\n\n[\u003cimg src=\"https://imgs.xkcd.com/comics/standards_2x.png\" title=\"How Standards Proliferate\" height=\"300\"/\u003e](https://xkcd.com/927/)\n[\u003cimg src=\"https://imgs.xkcd.com/comics/universal_install_script_2x.png\" title=\"Universal Install Script\" height=\"300\"/\u003e](https://xkcd.com/1654/)\n\n## Installation\n\n### With Cargo\n\n```shell\ncargo install metapac\n```\n\n### With Arch User Repository\n\n```shell\nparu -S metapac\n```\n\n## Meta\n\n`metapac` is a meta package manager, that means it does not directly\nimplement the functionality to install packages on your system, instead it\nprovides a standardised interface for installing packages from other\npackage managers. See the [Supported Backends](#supported-backends) section\nfor a list of the currently supported backend package managers.\n\n## Declarative\n\n`metapac` is also a declarative package manager, that means that you\ndeclare in `.toml` group files the packages and repos you would like\ninstalled on your system and then run one of the `metapac` commands which\nread these group files and then operate on your system to do some function\nsuch as install packages and repos in your group files that are not present\non your system yet (`metapac sync`), or remove packages and repos present\non your system but not in your group files (`metapac clean`).\n\nThe group files are then stored with your other system configuration files\nand so can be tracked with version control.\n\n## Usage\n\n### Enable backends\n\nBy default all backends are disabled. Enable the backends you want\n`metapac` to manage in the config file. See the [`Config`](#config) section\nfor more details.\n\n### Migrating a default system into `metapac`\n\nRun `metapac unmanaged` and save the output into a group file in\n`metapac`'s `groups/` folder, see the [`Group Files`](#group-files)\nsection for the exact location of this folder on your operating system.\n\nFor example, on linux:\n\n```console\nmkdir -p ~/.config/metapac/groups\nmetapac unmanaged \u003e ~/.config/metapac/groups/all.toml\n```\n\nNow `metapac` won't try to remove any of your explicitly installed packages\nwhen you run `metapac clean`.\n\n\u003e [!CAUTION]\n\u003e If you run `metapac clean` without first configuring your group files\n\u003e with the packages you want installed then `metapac` will attempt to\n\u003e remove all of your packages and repos from your enabled backends.\n\u003e\n\u003e `metapac clean` will always show you which packages and repos it intends\n\u003e to remove and ask for confirmation, so make sure to double check that the\n\u003e expected packages and repos are being removed before confirming.\n\n### Adding a new package/repo\n\nUse a text editor to add the package/repo to an existing group file or\ncreate a new group file and add the package/repo to it. See the [`Group\nFiles`](#group-files) section for the group file syntax.\n\nYou can then run `metapac sync` and `metapac` will install the package if\nit is not already present on your system.\n\n### Removing a package/repo\n\nDo the opposite of [`Adding a new package`](#adding-a-new-packagerepo). But\ninstead use `metapac clean` after editing your group files to remove all\npackages/repos on your system not in your group files.\n\n### Hooks\n\nHooks are commands that you can add per-package or per-repo in your group\nfiles. They get run by `metapac` at various stages in some of `metapac`'s\ncommands.\n\nOne of the main use-cases for hooks is to allow you to declaratively\nmaintain your enabled `systemd` services alongside each package in your\ngroup files. See the [`Group Files`](#group-files) section for some\nexamples.\n\n- `before_install`: Run before a package/repo is installed. Only applies to the\n  `metapac sync` command.\n- `after_install`: Run after a package/repo is installed. Only applies to the\n  `metapac sync` command.\n- `before_sync`: Run before installing any packages/repos, regardless of\n  whether the package/repo was already installed or not. Only applies to the\n  `metapac sync` command.\n- `after_sync`: Run after installing all packages/repos, regardless of\n  whether the package/repo was already installed or not. Only applies to the\n  `metapac sync` command.\n\nRepo/package hooks are run before/after installing all repos/packages, not\nbetween each repo/package.\n\n### Enable more logs for debugging\n\nYou can enable additional log levels by setting the `RUST_LOG` environment\nvariable, this can be useful to see which commands are being run on the\nbackends by `metapac`. For example, `RUST_LOG=trace metapac unmanaged`. See\n\u003chttps://docs.rs/env_logger\u003e for more information.\n\n### Advanced usage\n\nFor more advanced usage read through the remaining sections, especially the\n[`Config`](#config) section. You can also run `metapac --help` to get a\nlist of all of the available commands.\n\n## Supported Backends\n\nAt the moment, these are the supported backends. Pull requests and issues\nfor additional backends are always welcome!\n\n| Backend               |\n| --------------------- |\n| [`apt`](#apt)         |\n| [`arch`](#arch)       |\n| [`brew`](#brew)       |\n| [`bun`](#bun)         |\n| [`cargo`](#cargo)     |\n| [`dnf`](#dnf)         |\n| [`flatpak`](#flatpak) |\n| [`mas`](#mas)         |\n| [`mise`](#mise)       |\n| [`npm`](#npm)         |\n| [`pipx`](#pipx)       |\n| [`pnpm`](#pnpm)       |\n| [`scoop`](#scoop)     |\n| [`snap`](#snap)       |\n| [`uv`](#uv)           |\n| [`vscode`](#vscode)   |\n| [`winget`](#winget)   |\n| [`xbps`](#xbps)       |\n| [`yarn`](#yarn)       |\n| [`zypper`](#zypper)   |\n\n### apt\n\nStandard usage.\n\n### arch\n\n#### Package Groups\n\nArch has two special types of packages called meta packages and package\ngroups. (See\n\u003chttps://wiki.archlinux.org/title/Meta_package_and_package_group\u003e).\n`metapac` only supports meta packages in group files since they are \"real\"\npackages whereas groups are not \"real\". This is because meta packages are\nnormal PKGBUILD files with no content of themselves but which have several\ndependencies, whereas package groups are special cases that don't have a\ncorresponding PKGBUILD file. For example, running `pacman -Si nerd-fonts`\nreturns \"error: package 'nerd-fonts' was not found\".\n\nIf you still want the behavior of a meta package you have two options.\n\nFirstly, consider creating your own meta package with the same packages as\nthe group. Consider also publishing this package to the AUR so other users\ncan also benefit from it. Convention has it that meta packages end in\n`-meta`, for example, the meta package version of `nerd-fonts` might be\ncalled `nerd-fonts-meta` (Although `nerd-fonts-meta` does not yet exist at\nthe time of writing, 2025-09-03).\n\nAlternatively, you could create a new group file using the packages from\nthe package group, which you can get from the command: `pacman -Sgq\n\u003cgroup_name\u003e`.\n\n#### Yay Bug\n\nDue to a bug in `yay`: \u003chttps://github.com/Jguer/yay/issues/2288\u003e,\n`metapac` will sometimes keep trying to install some packages when doing\n`metapac sync`. To fix this, either switch to `paru` or use `pacman` to\nmark the packages in question as installed explicitly using `pacman\n--database --asexplicit \u003cpackages...\u003e`.\n\nReported in #152.\n\n### brew\n\nStandard usage.\n\n### bun\n\nStandard usage.\n\n### cargo\n\nStandard usage.\n\n### dnf\n\nStandard usage.\n\n### flatpak\n\nStandard usage.\n\n### mas\n\nStandard usage.\n\n### mise\n\nStandard usage.\n\n### npm\n\nIf on linux you might need to first run `npm config set prefix ~/.local`.\n\n### pipx\n\nStandard usage.\n\n### pnpm\n\nYou might need to first run `pnpm setup`.\n\n### scoop\n\n`scoop` doesn't differentiate between implicit and explicit packages.\nTherefore, you will need to list all packages and their dependencies in\nyour group files. See\n\u003chttps://github.com/ScoopInstaller/Scoop/issues/4276\u003e.\n\n### snap\n\nStandard usage.\n\n### uv\n\nStandard usage.\n\n### vscode\n\nStandard usage.\n\n### winget\n\nStandard usage.\n\n### xbps\n\nStandard usage.\n\n### yarn\n\nStandard usage.\n\n### zypper\n\nStandard usage.\n\n## Config\n\n```toml\n# metapac's config.toml file (like this one) should be placed in the following location\n# dependent on the operating system as specified in the `dirs` crate:\n# | Platform | Value                                                 | Example                                                      |\n# | -------- | ----------------------------------------------------- | ------------------------------------------------------------ |\n# | Linux    | $XDG_CONFIG_HOME or $HOME/.config/metapac/config.toml | /home/alice/.config/metapac/config.toml                      |\n# | macOS    | $HOME/Library/Application Support/metapac/config.toml | /Users/Alice/Library/Application Support/metapac/config.toml |\n# | Windows  | {FOLDERID_RoamingAppData}\\metapac\\config.toml         | C:\\Users\\Alice\\AppData\\Roaming\\metapac\\config.toml           |\n\n# Backends to enable. These will be merged with any hostname-specific backends\n# from the [hostname_enabled_backends] config table.\n# Default: []\nenabled_backends = [\"arch\", \"cargo\"]\n\n# If this is `false` all toml files recursively found in the groups folder\n# will be used as group files.\n# If this is `true` then the [hostname_groups] config table will be used to\n# decide which group files to use per hostname.\n# Default: false\nhostname_groups_enabled = false\n\n# Backends to enable per hostname. These will be merged with the base\n# `enabled_backends` config.\n# Default: None\n[hostname_enabled_backends]\npc = [\"winget\", \"cargo\"]\nlaptop = [\"arch\", \"cargo\"]\nserver = [\"apt\"]\n\n# Which group files will be used per hostname. Subject to `hostname_groups_enabled`.\n# Relative paths are relative to the groups folder.\n# Default: None\n[hostname_groups]\npc = [\"relative_group\", \"/etc/absolute_group\"]\nlaptop = [\"relative_group\"]\nserver = [\"relative_group\"]\n\n[arch]\n# Since pacman, pamac, paru, pikaur and yay all operate on the same package database\n# they are mutually exclusive and so you must pick which one you want\n# metapac to use.\n# Must be one of: [\"pacman\", \"pamac\", \"paru\", \"pikaur\", \"yay\"]\n# Default: \"pacman\"\npackage_manager = \"paru\"\n\n[brew]\n# If this is `true` then brew packages default to using the `--quarantine`\n# option.\n# If this is `false` then brew packages default to using the `--no-quarantine`\n# option.\n# Default: true\nquarantine = true\n\n[cargo]\n# Whether to default to installing cargo packages with the `--locked` option.\n# Default: false\nlocked = false\n\n# Whether to use `cargo-binstall` instead of `cargo install` for installing packages.\n# When `true`, metapac will use `cargo binstall --no-confirm` instead of `cargo install`.\n# This can be faster for installing packages as it downloads pre-built binaries.\n# Default: false\nbinstall = false\n\n[vscode]\n# Since VSCode and VSCodium both operate on the same package database\n# they are mutually exclusive and so you must pick which one you want\n# metapac to use.\n# Must be one of: [\"code\", \"codium\"]\n# Default: \"code\"\nvariant = \"code\"\n\n[zypper]\n# Since OpenSUSE Leap and Tumbleweed should be updated with different commands\n# (see https://en.opensuse.org/System_Updates for more details), you can set how\n# metapac updates system packages.\n# If this is `false` then the system is updated with the subcommand `update` (`up`).\n# If this is `true` then the system is updated with the subcommand `dist-upgrade` (`dup`).\n# Default: false\ndistribution_upgrade = false\n```\n\n## Group Files\n\n```toml\n# metapac's group files (like this one) should be placed in the following location\n# dependent on the operating system as specified in the `dirs` crate:\n# | Platform | Value                                             | Example                                                  |\n# | -------- | ------------------------------------------------- | -------------------------------------------------------- |\n# | Linux    | $XDG_CONFIG_HOME or $HOME/.config/metapac/groups/ | /home/alice/.config/metapac/groups/                      |\n# | macOS    | $HOME/Library/Application Support/metapac/groups/ | /Users/Alice/Library/Application Support/metapac/groups/ |\n# | Windows  | {FOLDERID_RoamingAppData}\\metapac\\groups\\         | C:\\Users\\Alice\\AppData\\Roaming\\metapac\\groups\\           |\n#\n# The packages for each backend in group files can come in two formats, short-form\n# and long-form:\n#\n# short-form syntax is simply a string of the name of the package.\n#\n# long-form syntax is a table which contains several fields which can\n# optionally be set to specify install options on a per-package basis.\n# The \"package\" field in the table specifies the name of the package.\n#\n# For example, the following two packages are equivalent:\n# arch = [\n#  \"metapac\",\n#  { package = \"metapac\" }\n# ]\n\napt = { packages = [\"package1\", { name = \"package2\" }] }\narch = {\n  packages = [\n    \"package1\",\n    { name = \"package2\" },\n    {\n      name = \"syncthing\",\n      hooks = {\n        after_sync = [\n          \"sudo\",\n          \"systemctl\",\n          \"enable\",\n          \"--now\",\n          \"syncthing@ripytide\",\n        ]\n      }\n    },\n    {\n      name = \"openssh\",\n      hooks = {\n        after_sync = [\n          \"sudo\",\n          \"systemctl\",\n          \"enable\",\n          \"--now\",\n          \"sshd\",\n        ]\n      }\n    },\n    {\n      name = \"fastfetch\",\n      hooks = {\n        before_install = [\n          \"echo\",\n          \"before_install\",\n        ],\n        after_install = [\n          \"echo\",\n          \"after_install\",\n        ],\n        before_sync = [\n          \"echo\",\n          \"before_sync\",\n        ],\n        after_sync = [\n          \"echo\",\n          \"after_sync\",\n        ]\n      }\n    },\n  ]\n}\nbrew = {\n  packages = [\n    \"package1\",\n    { name = \"package2\", options = { quarantine = false } }\n  ]\n}\nbun = { packages = [\"package1\", { name = \"package2\" }] }\ncargo = {\n  packages = [\n    \"package1\",\n    {\n      name = \"package2\",\n      options = {\n        git = \"https://github.com/ripytide/metapac\",\n        all_features = true,\n        no_default_features = false,\n        features = [\n          \"feature1\",\n        ],\n        locked = true\n      }\n    },\n  ]\n}\ndnf = {\n  repos = [\n    \"copr.fedorainfracloud.org/ripytide/package1\",\n    {\n      name = \"copr.fedorainfracloud.org/ripytide/package2\",\n      hooks = {\n        before_install = [\n          \"echo\",\n          \"hooks still work with repos too!\",\n        ],\n      }\n    },\n  ],\n  packages = [\"package1\", { name = \"package2\" }]\n}\nflatpak = {\n  repos = [\n    {\n      name = \"system:flathub\",\n      options = { url = \"https://dl.flathub.org/repo/\" }\n    },\n    {\n      name = \"user:ykc\",\n      options = { url = \"https://flatpak.yellowkeycard.net/ykc.flatpakrepo\" }\n    },\n    {\n      name = \"custom_installation:flathub_beta\",\n      options = {\n        url = \"https://flathub.org/beta-repo/flathub-beta.flatpakrepo\"\n      }\n    },\n  ],\n  packages = [\n    { name = \"system:org.gimp.GIMP\", options = { remote = \"flathub\" } },\n    { name = \"user:io.github.sonicgalactic\", options = { remote = \"ykc\" } },\n    {\n      name = \"custom_installation:org.mozilla.firefox\",\n      options = { remote = \"flathub_beta\" }\n    },\n  ]\n}\nmas = { packages = [\"package1\", { name = \"package2\" }] }\nmise = {\n  packages = [\n    \"package1\",\n    { name = \"package2\", options = { version = \"1.0.0\" } },\n    { name = \"package3\", options = { version = \"lts\" } },\n  ]\n}\nnpm = { packages = [\"package1\", { name = \"package2\" }] }\npipx = { packages = [\"package1\", { name = \"package2\" }] }\npnpm = { packages = [\"package1\", { name = \"package2\" }] }\nscoop = { packages = [\"main/metapac1\", { name = \"main/package2\" }] }\nsnap = {\n  packages = [\n    \"package1\",\n    { name = \"package2\" },\n    { name = \"package3\", options = { confinement = \"strict\" } },\n    { name = \"package4\", options = { confinement = \"classic\" } },\n    { name = \"package5\", options = { confinement = \"dangerous\" } },\n    { name = \"package6\", options = { confinement = \"devmode\" } },\n    { name = \"package7\", options = { confinement = \"jailmode\" } },\n  ]\n}\nuv = {\n  packages = [\"package1\", { name = \"package2\", options = { python = \"3.11\" } }]\n}\nvscode = { packages = [\"package1\", { name = \"package2\" }] }\nwinget = { packages = [\"ripytide.package1\", { name = \"ripytide.package2\" }] }\nxbps = { packages = [\"package1\", { name = \"package2\" }] }\nyarn = { packages = [\"package1\", { name = \"package2\" }] }\nzypper = { packages = [\"package1\", { name = \"package2\" }] }\n```\n\n## Wishlist\n\nHere is a list of package managers we would like to support along with any\nreasons why we can't yet if any. Feel free to add to this list if you know\nof any other package managers we should be aware of.\n\n- [`apk`](https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper): no\n  attempt made yet\n- [`cygwin`](https://cygwin.com/): no attempt made yet\n- [`choco`](https://github.com/chocolatey/choco): no attempt made yet\n- [`deno`](https://github.com/denoland/deno): can't list installed global\n  packages \u003chttps://github.com/denoland/deno/discussions/28230\u003e\n- [`emerge`](https://wiki.gentoo.org/wiki/Emerge): no attempt made yet\n- [`guix`](https://codeberg.org/guix/guix): no attempt made yet\n- [`nala`](https://github.com/volitank/nala): no attempt made yet\n- [`nix`](https://github.com/NixOS/nix): no attempt made yet\n- [`opkg`](https://github.com/oe-mirrors/opkg): no attempt made yet\n- [`pip`](https://pypi.org/project/pip/): we support `pipx` instead which\n  only allows you to install cli programs which makes sense for a global\n  package manager\n- [`pkg`](https://github.com/freebsd/pkg): no attempt made yet\n- [`ports`](https://github.com/openbsd/ports): no attempt made yet\n- [`pkgsrc`](https://github.com/NetBSD/pkgsrc): no attempt made yet\n- [`sdk`](https://github.com/sdkman/sdkman-cli): can't list installed\n  packages \u003chttps://github.com/sdkman/sdkman-cli/issues/466\u003e. The project\n  is being rewritten in rust with the intention to implement the command in\n  the new version \u003chttps://github.com/sdkman/sdkman-cli-native\u003e, also see\n  \u003chttps://github.com/ripytide/metapac/issues/86\u003e\n- [`yum`](https://github.com/rpm-software-management/yum): project\n  deprecated in favor of `dnf`\n\n## Similar Projects\n\n- [decman](https://github.com/kiviktnm/decman): written in python,\n  archlinux specific, supports installing dotfiles\n- [declaro](https://github.com/mantinhas/declaro): written in shell script,\n  currently provides support for `apt`, `dnf`, `pacman`, `paru` and `yay`\n  but is extensible\n- [pacdef](https://github.com/steven-omaha/pacdef): written in rust, custom\n  file format, unmaintained, supported `pacman`, `apt`, `dnf`, `flatpak`,\n  `pip`, `cargo`, `rustup` and `xbps`\n- [upt](https://github.com/sigoden/upt/tree/main): written in rust,\n  supports 28 package managers! Designed for manual package management\n  rather than declarative.\n\n## Credits\n\nThis project was forked from \u003chttps://github.com/steven-omaha/pacdef\u003e so\ncredits to the author(s) of that project for all their prior work.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fripytide%2Fmetapac","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fripytide%2Fmetapac","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fripytide%2Fmetapac/lists"}