{"id":13542272,"url":"https://github.com/buildinspace/peru","last_synced_at":"2025-05-14T05:03:41.857Z","repository":{"id":11427847,"uuid":"13880738","full_name":"buildinspace/peru","owner":"buildinspace","description":"a generic package manager, for including other people's code in your projects","archived":false,"fork":false,"pushed_at":"2024-12-26T20:04:36.000Z","size":4146,"stargazers_count":1125,"open_issues_count":85,"forks_count":71,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-05-12T10:18:59.939Z","etag":null,"topics":["dependency-manager","package-manager","packaging","plugin-manager","toolchain"],"latest_commit_sha":null,"homepage":"","language":"Python","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/buildinspace.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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,"zenodo":null}},"created_at":"2013-10-26T09:14:57.000Z","updated_at":"2025-05-02T08:52:08.000Z","dependencies_parsed_at":"2023-01-13T16:30:19.681Z","dependency_job_id":"30946d69-bcbc-42b0-b0c3-663cbae5838d","html_url":"https://github.com/buildinspace/peru","commit_stats":{"total_commits":668,"total_committers":11,"mean_commits":60.72727272727273,"dds":0.09730538922155685,"last_synced_commit":"9dbf098ec12f44a84c50ac9c616f4c107985a468"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buildinspace%2Fperu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buildinspace%2Fperu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buildinspace%2Fperu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buildinspace%2Fperu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/buildinspace","download_url":"https://codeload.github.com/buildinspace/peru/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254076393,"owners_count":22010611,"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":["dependency-manager","package-manager","packaging","plugin-manager","toolchain"],"created_at":"2024-08-01T10:01:03.754Z","updated_at":"2025-05-14T05:03:41.785Z","avatar_url":"https://github.com/buildinspace.png","language":"Python","readme":"# peru [![Actions Status](https://github.com/buildinspace/peru/workflows/tests/badge.svg)](https://github.com/buildinspace/peru/actions) [![PyPI version](https://badge.fury.io/py/peru.svg)](https://badge.fury.io/py/peru)\n\n##### Maybe sometimes better than copy-paste.\n\nPeru is a tool for including other people's code in your projects. It\nfetches from anywhere -- git, hg, svn, tarballs -- and puts files\nwherever you like. Peru helps you track exact versions of your\ndependencies, so that your history is always reproducible. And it fits\ninside your scripts and [Makefiles](docs/make_examples), so your build\nstays simple and foolproof.\n\n![snazzy gif](docs/peru.gif)\n\n## Why?\n\nIf you build with `make`, you don't have to do anything special when you\nswitch branches or pull new commits. Build tools notice those changes\nwithout any help. But if you depend on other people's code, the tools\naren't so automatic anymore. You need to remember when to `git submodule\nupdate` or `go get -u` or `pip install -r`. If you forget a step you can\nbreak your build, or worse, you might build something wrong without\nnoticing.\n\nPeru wants you to automate dependency management just like you automate\nthe rest of your build. It doesn't interfere with your source control or\ninstall anything global, so you can just throw it in at the start of a\nscript and forget about it. It'll run every time, and your dependencies\nwill never be out of sync. Simple, and fast as heck.\n\nThe name \"peru\", along with our love for reproducible builds, was inspired by\n[Amazon's Brazil build system](https://web.archive.org/web/20130731100223/http://stackoverflow.com/questions/3380795/what-does-amazon-use-for-its-build-and-release-system).\nIt also happens to be an anagram for \"[reup](#magical-updates)\".\n\n## Installation\n\nPeru supports Linux, macOS, and Windows. It requires:\n\n* `python` 3.8 or later\n* `git`, any version\n* optionally, if you want fetch from these types of repos:\n  * `hg`, any version\n  * `svn`, any version\n\n`git` is required even if you are not retrieving a git-based module because\nPeru uses it internally.\n\n### Using `pip`\n\nUse [pip](https://pip.pypa.io/en/latest/) to install it:\n\n```bash\npip install peru\n```\n\nNote that depending on how Python is set up on your machine, you might\nneed to use `sudo` with that, and Python 3's pip might be called `pip3`.\n\n### Using OS package managers\n\nOn Arch Linux, you can install `peru` [from the\nAUR](https://aur.archlinux.org/packages/peru).\n\nHomebrew has a [Peru formula](https://formulae.brew.sh/formula/peru) for macOS\nand Linux. `brew install peru` will install it running on the latest Python\nversion that Homebrew supports.\n\n## Getting Started\n\nHere's the peru version of the [first git submodules\nexample](http://git-scm.com/book/en/Git-Tools-Submodules#Starting-with-Submodules)\nfrom the [Git Book](http://git-scm.com/book). We're going to add the Rack\nlibrary to our project. First, create a `peru.yaml` file like this:\n\n```yaml\nimports:\n    rack_example: rack/  # This is where we want peru to put the module.\n\ngit module rack_example:\n    url: git://github.com/chneukirchen/rack.git\n```\n\nNow run `peru sync`.\n\n#### What the heck just happened?\n\nPeru cloned Rack for you, and imported a copy of it under the `rack` directory.\nIt also created a magical directory called `.peru` to hold that clone and some\nother business. If you're using source control, now would be a good time to put\nthese directories in your ignore list (like `.gitignore`). You usually don't\nwant to check them in.\n\nRunning `peru clean` will make the imported directory disappear.  Running `peru\nsync` again will make it come back, and it'll be a lot faster this time,\nbecause peru caches everything.\n\n## Getting Fancy\n\nFor a more involved example, let's use peru to manage some dotfiles. We're big\nfans of the [Solarized colorscheme](http://ethanschoonover.com/solarized), and\nwe want to get it working in both `ls` and `vim`. For `ls` all we need peru to\ndo is fetch a Solarized dircolors file. (That'll get loaded somewhere like\n`.bashrc`, not included in this example.) For `vim` we're going to need the\n[Solarized vim plugin](https://github.com/altercation/vim-colors-solarized),\nand we also want [Pathogen](https://github.com/tpope/vim-pathogen), which makes\nplugin installation much cleaner. Here's the `peru.yaml`:\n\n```yaml\nimports:\n    # The dircolors file just goes at the root of our project.\n    dircolors: ./\n    # We're going to merge Pathogen's autoload directory into our own.\n    pathogen: .vim/autoload/\n    # The Solarized plugin gets its own directory, where Pathogen expects it.\n    vim-solarized: .vim/bundle/solarized/\n\ngit module dircolors:\n    url: https://github.com/seebi/dircolors-solarized\n    # Only copy this file. Can be a list of files. Accepts * and ** globs.\n    pick: dircolors.ansi-dark\n\ncurl module pathogen:\n    url: https://codeload.github.com/tpope/vim-pathogen/tar.gz/v2.3\n    # Untar the archive after fetching.\n    unpack: tar\n    # After the unpack, use this subdirectory as the root of the module.\n    export: vim-pathogen-2.3/autoload/\n\ngit module vim-solarized:\n    url: https://github.com/altercation/vim-colors-solarized\n    # Fetch this exact commit, instead of master or main.\n    rev: 7a7e5c8818d717084730133ed6b84a3ffc9d0447\n```\n\nThe contents of the `dircolors` module are copied to the root of our repo. The\n`pick` field restricts this to just one file, `dircolors.ansi-dark`.\n\nThe `pathogen` module uses the `curl` type instead of `git`, and its URL points\nto a tarball. (This is for the sake of an example. In real life you'd probably\nuse `git` here too.) The `unpack` field means that we get the contents of the\ntarball rather than the tarball file itself. Because the module specifies an\n`export` directory, it's that directory rather than the whole module that gets\ncopied to the import path, `.vim/autoload`. The result is that Pathogen's\n`autoload` directory gets merged with our own, which is the standard way to\ninstall Pathogen.\n\nThe `vim-solarized` module gets copied into its own directory under `bundle`,\nwhich is where Pathogen will look for it. Note that it has an explicit `rev`\nfield, which tells peru to fetch that exact revision, rather than the default\nbranch (`master` or `main` in git). That's a **Super Serious Best Practice™**,\nbecause it means your dependencies will always be consistent, even when you\nlook at commits from a long time ago.\n\nYou really want all of your dependencies to have hashes, but editing\nthose by hand is painful. The next section is about making that easier.\n\n## Magical Updates\n\nIf you run `peru reup`, peru will talk to each of your upstream repos, get\ntheir latest versions, and then edit your `peru.yaml` file with any updates. If\nyou don't have `peru.yaml` checked into some kind of source control, you should\nprobably do that first, because the reup will modify it in place. When we reup\nthe example above, the changes look something like this:\n\n```diff\ndiff --git a/peru.yaml b/peru.yaml\nindex 15c758d..7f0e26b 100644\n--- a/peru.yaml\n+++ b/peru.yaml\n@@ -6,12 +6,14 @@ imports:\n git module dircolors:\n     url: https://github.com/seebi/dircolors-solarized\n     pick: dircolors.ansi-dark\n+    rev: a5e130c642e45323a22226f331cb60fd37ce564f\n\n curl module pathogen:\n     url: https://codeload.github.com/tpope/vim-pathogen/tar.gz/v2.3\n     unpack: tar\n     export: vim-pathogen-2.3/autoload/\n+    sha1: 9c3fd6d9891bfe2cd3ed3ddc9ffe5f3fccb72b6a\n\n git module vim-solarized:\n     url: https://github.com/altercation/vim-colors-solarized\n-    rev: 7a7e5c8818d717084730133ed6b84a3ffc9d0447\n+    rev: 528a59f26d12278698bb946f8fb82a63711eec21\n```\n\nPeru made three changes:\n- The `dircolors` module, which didn't have a `rev` before, just got one. By\n  default for `git`, this is the current `master` or `main`. To change that,\n  you can set the `reup` field to the name of a different branch.\n- The `pathogen` module got a `sha1` field. Unlike `git`, a `curl` module is\n  plain old HTTP, so it's stuck downloading whatever file is at the `url`. But\n  it will check this hash after the download is finished, and it will raise an\n  error if there's a mismatch.\n- The `vim-solarized` module had a hash before, but it's been updated. Again,\n  the new value comes from `master` or `main` by default.\n\nAt this point, you'll probably want to make a new commit of `peru.yaml` to\nrecord the version bumps. You can do this every so often to keep your plugins\nup to date, and you'll always be able to reach old versions in your history.\n\n## Commands\n- `sync`\n  - Pull in your imports. `sync` yells at you instead of overwriting existing\n    or modified files. Use `--force`/`-f` to tell it you're serious.\n- `clean`\n  - Remove imported files. Same `--force`/`-f` flag as `sync`.\n- `reup`\n  - Update module fields with new revision information. For `git`, `hg`, and\n    `svn`, this updates the `rev` field. For `curl`, this sets the `sha1`\n    field. You can optionally give specific module names as arguments.\n- `copy`\n  - Make a copy of all the files in a module. Either specify a directory to put\n    them in, or peru will create a temp dir for you. You can use this to see\n    modules you don't normally import, or to play with different module/rule\n    combinations (see \"Rules\" below).\n- `override`\n  - Replace the contents of a module with a local directory path, usually a\n    clone you've made of the same repo. This lets you test changes to imported\n    modules without needing to push your changes upstream or edit `peru.yaml`.\n\n## Module Types\n\n##### git, hg, svn\nFor cloning repos. These types all provide the same fields:\n- `url`: required, any protocol supported by the underlying VCS\n- `rev`: optional, the specific revision/branch/tag to fetch\n- `reup`: optional, the branch/tag to get the latest rev from when running\n  `peru reup`\n\nThe `git` type also supports setting `submodules: false` to skip\nfetching git submodules. Otherwise they're included by default.\n\n##### curl\nFor downloading a file from a URL. This type is powered by Pythons's standard\nlibrary, rather than an external program.\n- `url`: required, any kind supported by `urllib` (HTTP, FTP, `file://`)\n- `filename`: optional, overrides the default filename\n- `sha1`: optional, checks that the downloaded file matches the checksum\n- `unpack`: optional, `tar` or `zip`\n\nPeru includes a few other types mostly for testing purposes. See `rsync` for an\nexample implemented in Bash.\n\n## Creating New Module Types\nModule type plugins are as-dumb-as-possible scripts that only know how to\nsync, and optionally reup. Peru shells out to them and then handles most of\nthe caching magic itself, though plugins can also do their own caching as\nappropriate.  For example, the git and hg plugins keep track of repos they\nclone. Peru itself doesn't need to know how to do that. For all the details,\nsee [Architecture: Plugins](docs/architecture.md#plugins).\n\n## Rules\nSome fields (like `rev` and `unpack`) are specific to certain module\ntypes. There are also fields you can use in any module, which modify the\ntree of files after it's fetched. Some of these made an appearance in\nthe fancy example above:\n\n- `copy`: A map or multimap of source and destination paths to copy.\n  Works like `cp` on the command line, so if the destination is a\n  directory, it'll preserve the source filename and copy into the\n  destination directory.\n- `move`: A map or multimap of source and destination paths to move.\n  Similar to `copy` above, but removes the source.\n- `drop`: A file or directory, or a list of files and directories, to\n  remove from the module. Paths can contain `*` or `**` globs.\n- `pick`: A file or directory, or a list of files and directories, to\n  include in the module. Everything else is dropped. Paths can contain\n  `*` or `**` globs.\n- `executable`: A file or list of files to make executable, as if\n  calling `chmod +x`. Also accepts globs.\n- `export`: A subdirectory that peru should treat as the root of the\n  module tree. Everything else is dropped, including parent directories.\n\nNote that these fields always take effect in the order listed above, regardless\nof the order they're given in `peru.yaml`. For example, a `move` is always\nperformed before a `pick`. Also note that these fields can't be given twice.\nFor example, instead of using two separate `move` fields (one of which would be\nignored), use a single `move` field containing multiple moves. In practice,\nthings work this way because these fields are parsed as keys in a dictionary,\nwhich don't preserve ordering and can't repeat.\n\nBesides using those fields in your modules, you can also use them in \"named\nrules\", which let you transform one module in multiple ways. For example, say\nyou want the `asyncio` subdir from the Tulip project, but you also want the\nlicense file somewhere else. Rather than defining the same module twice, you\ncan use one module and two named rules, like this:\n\n```yaml\nimports:\n    tulip|asyncio: python/asyncio/\n    tulip|license: licenses/\n\ngit module tulip:\n    url: https://github.com/python/asyncio\n\nrule asyncio:\n    export: asyncio/\n\nrule license:\n    pick: COPYING\n```\n\nAs in this example, named rules are declared a lot like modules and then\nused in the `imports` list, with the syntax `module|rule`.  The `|`\noperator there works kind of like a shell pipeline, so you can even do\ntwisted things like `module|rule1|rule2`, with each rule applying to the\noutput tree of the previous.\n\n## Recursion\n\nIf you import a module that has a peru file of its own, peru can include\nthat module's imports along with it, similar to how git submodules\nbehave with `git clone --recursive`. To enable this, add `recursive:\ntrue` in a module's definition.\n\nIt's also possible to directly import modules that are defined in the\n`peru.yaml` file of another module. If your project defines a module\n`foo`, and `foo` has a peru file in it that defines a module `bar`, you\ncan use `foo.bar` in your own imports. This works even if you never\nactually import `foo`, and it does not require setting `recursive:\ntrue`.\n\n## Configuration\n\nThere are several flags and environment variables you can set, to\ncontrol where peru puts things. Flags always take precedence.\n\n- `--file=\u003cfile\u003e`: The path to your peru YAML file. By default peru\n  looks for `peru.yaml` in the current directory or one of its parents.\n  This setting tells peru to use a specific file. If set, `--sync-dir`\n  must also be set.\n- `--sync-dir=\u003cdir\u003e`: The path that all `imports` are interpreted\n  relative to. That is, if you import a module to `./`, the contents of\n  that module go directly in the sync dir. By default this is the\n  directory containing your `peru.yaml` file. If set, `--file` must also\n  be set.\n- `--state-dir=\u003cdir\u003e`: The directory where peru stashes all of its state\n  metadata, and also the parent of the cache dir. By default this is\n  `.peru` inside the sync dir. You should not share this directory\n  between two projects, or `peru sync` will get confused.\n- `--cache-dir=\u003cdir\u003e` or `PERU_CACHE_DIR`: The directory where peru\n  keeps everything it's fetched. If you have many projects fetching the\n  same dependencies, you can use a shared cache dir to speed things up.\n- `--file-basename=\u003cname\u003e`: Change the default peru file name (normally\n  `peru.yaml`). As usual, peru will search the current directory and its\n  parents for a file of that name, and it will use that file's parent\n  dir as the sync dir. Incompatible with `--file`.\n\n## Links\n- [Discussion and announcements (Google\n  Group)](https://groups.google.com/forum/#!forum/peru-tool)\n- [Architecture doc](docs/architecture.md)\n- [Using peru with make](docs/make_examples)\n","funding_links":[],"categories":["Python","package-manager"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuildinspace%2Fperu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbuildinspace%2Fperu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuildinspace%2Fperu/lists"}